1a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// found in the LICENSE file.
4a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/media/native_desktop_media_list.h"
6a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
7a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include <map>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <set>
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <sstream>
10a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
11a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/hash.h"
12a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/logging.h"
13a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
14a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/media/desktop_media_list_observer.h"
1603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "chrome/grit/generated_resources.h"
17a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
18a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "media/base/video_util.h"
19a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "third_party/libyuv/include/libyuv/scale_argb.h"
20a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
21a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
22a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
23a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
24a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
25a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ui/gfx/skia_util.h"
26a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
27a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)using content::BrowserThread;
28ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochusing content::DesktopMediaID;
29a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
30a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)namespace {
31a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
32a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Update the list every second.
33a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)const int kDefaultUpdatePeriod = 1000;
34a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
35a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Returns a hash of a DesktopFrame content to detect when image for a desktop
36a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// media source has changed.
37a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)uint32 GetFrameHash(webrtc::DesktopFrame* frame) {
38a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  int data_size = frame->stride() * frame->size().height();
39a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return base::SuperFastHash(reinterpret_cast<char*>(frame->data()), data_size);
40a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
41a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
42a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)gfx::ImageSkia ScaleDesktopFrame(scoped_ptr<webrtc::DesktopFrame> frame,
43a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                 gfx::Size size) {
44a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
45a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      gfx::Rect(0, 0, size.width(), size.height()),
46a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      gfx::Size(frame->size().width(), frame->size().height()));
47a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
48a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  SkBitmap result;
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocN32Pixels(scaled_rect.width(), scaled_rect.height(), true);
50a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  result.lockPixels();
51a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
52a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  uint8* pixels_data = reinterpret_cast<uint8*>(result.getPixels());
53a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  libyuv::ARGBScale(frame->data(), frame->stride(),
54a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    frame->size().width(), frame->size().height(),
55a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    pixels_data, result.rowBytes(),
56a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    scaled_rect.width(), scaled_rect.height(),
57a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    libyuv::kFilterBilinear);
58a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
59a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Set alpha channel values to 255 for all pixels.
60a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // TODO(sergeyu): Fix screen/window capturers to capture alpha channel and
61a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // remove this code. Currently screen/window capturers (at least some
62a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // implementations) only capture R, G and B channels and set Alpha to 0.
63a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // crbug.com/264424
64a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  for (int y = 0; y < result.height(); ++y) {
65a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    for (int x = 0; x < result.width(); ++x) {
66a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] =
67a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          0xff;
68a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
69a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
70a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
71a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  result.unlockPixels();
72a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
73a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return gfx::ImageSkia::CreateFrom1xBitmap(result);
74a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
75a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
76a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}  // namespace
77a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)NativeDesktopMediaList::SourceDescription::SourceDescription(
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DesktopMediaID id,
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& name)
81a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    : id(id),
82a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      name(name) {
83a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
84a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)class NativeDesktopMediaList::Worker
86a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    : public webrtc::DesktopCapturer::Callback {
87a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) public:
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
89a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
90a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         scoped_ptr<webrtc::WindowCapturer> window_capturer);
91a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  virtual ~Worker();
92a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
9368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  void Refresh(const gfx::Size& thumbnail_size,
9468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)               content::DesktopMediaID::Id view_dialog_id);
95a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
96a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) private:
97ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  typedef std::map<DesktopMediaID, uint32> ImageHashesMap;
98a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
99a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // webrtc::DesktopCapturer::Callback interface.
100a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
101a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;
102a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::WeakPtr<NativeDesktopMediaList> media_list_;
104a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
105a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
106a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  scoped_ptr<webrtc::WindowCapturer> window_capturer_;
107a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
108a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  scoped_ptr<webrtc::DesktopFrame> current_frame_;
109a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
110a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  ImageHashesMap image_hashes_;
111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
112a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Worker);
113a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)};
114a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)NativeDesktopMediaList::Worker::Worker(
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::WeakPtr<NativeDesktopMediaList> media_list,
117a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
118a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    scoped_ptr<webrtc::WindowCapturer> window_capturer)
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    : media_list_(media_list),
120a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      screen_capturer_(screen_capturer.Pass()),
121a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      window_capturer_(window_capturer.Pass()) {
122a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (screen_capturer_)
123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    screen_capturer_->Start(this);
124a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (window_capturer_)
125a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    window_capturer_->Start(this);
126a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
127a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)NativeDesktopMediaList::Worker::~Worker() {}
129a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::Worker::Refresh(
13168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const gfx::Size& thumbnail_size,
13268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    content::DesktopMediaID::Id view_dialog_id) {
133a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  std::vector<SourceDescription> sources;
134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
135a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (screen_capturer_) {
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    webrtc::ScreenCapturer::ScreenList screens;
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (screen_capturer_->GetScreenList(&screens)) {
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bool mutiple_screens = screens.size() > 1;
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::string16 title;
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (size_t i = 0; i < screens.size(); ++i) {
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (mutiple_screens) {
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          title = l10n_util::GetStringFUTF16Int(
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              static_cast<int>(i + 1));
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          title = l10n_util::GetStringUTF16(
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sources.push_back(SourceDescription(DesktopMediaID(
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            DesktopMediaID::TYPE_SCREEN, screens[i].id), title));
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
153a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
154a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
155a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (window_capturer_) {
156a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    webrtc::WindowCapturer::WindowList windows;
157a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (window_capturer_->GetWindowList(&windows)) {
158a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin();
159a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)           it != windows.end(); ++it) {
16068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        // Skip the picker dialog window.
16168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (it->id != view_dialog_id) {
16268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          sources.push_back(SourceDescription(
16368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)              DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id),
16468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)              base::UTF8ToUTF16(it->title)));
16568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
166a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
167a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
168a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
169a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Update list of windows before updating thumbnails.
170a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  BrowserThread::PostTask(
171a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      BrowserThread::UI, FROM_HERE,
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::Bind(&NativeDesktopMediaList::OnSourcesList,
173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 media_list_, sources));
174a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
175a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  ImageHashesMap new_image_hashes;
176a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
177a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Get a thumbnail for each source.
178a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  for (size_t i = 0; i < sources.size(); ++i) {
179a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    SourceDescription& source = sources[i];
180a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    switch (source.id.type) {
181ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case DesktopMediaID::TYPE_SCREEN:
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (!screen_capturer_->SelectScreen(source.id.id))
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          continue;
184a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        screen_capturer_->Capture(webrtc::DesktopRegion());
185a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        break;
186a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
187ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case DesktopMediaID::TYPE_WINDOW:
188a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        if (!window_capturer_->SelectWindow(source.id.id))
189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          continue;
190a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        window_capturer_->Capture(webrtc::DesktopRegion());
191a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        break;
192a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
193a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      default:
194a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        NOTREACHED();
195a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
196a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
197a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Expect that DesktopCapturer to always captures frames synchronously.
198a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // |current_frame_| may be NULL if capture failed (e.g. because window has
199a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // been closed).
200a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (current_frame_) {
201a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      uint32 frame_hash = GetFrameHash(current_frame_.get());
202a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      new_image_hashes[source.id] = frame_hash;
203a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
204a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      // Scale the image only if it has changed.
205a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      ImageHashesMap::iterator it = image_hashes_.find(source.id);
206a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (it == image_hashes_.end() || it->second != frame_hash) {
207a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        gfx::ImageSkia thumbnail =
208a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)            ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size);
209a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        BrowserThread::PostTask(
210a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)            BrowserThread::UI, FROM_HERE,
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            base::Bind(&NativeDesktopMediaList::OnSourceThumbnail,
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        media_list_, i, thumbnail));
213a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
214a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
215a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
216a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
217a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  image_hashes_.swap(new_image_hashes);
218a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
219a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  BrowserThread::PostTask(
220a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      BrowserThread::UI, FROM_HERE,
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_));
222a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
223a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory(
225a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    size_t size) {
226a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return NULL;
227a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
228a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
229a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::Worker::OnCaptureCompleted(
230a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    webrtc::DesktopFrame* frame) {
231a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  current_frame_.reset(frame);
232a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
233a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
234a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)NativeDesktopMediaList::NativeDesktopMediaList(
2353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
2363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    scoped_ptr<webrtc::WindowCapturer> window_capturer)
2373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    : screen_capturer_(screen_capturer.Pass()),
2383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      window_capturer_(window_capturer.Pass()),
2393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
240a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      thumbnail_size_(100, 100),
24168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      view_dialog_id_(-1),
242a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      observer_(NULL),
243a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      weak_factory_(this) {
244a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
245a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
246a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      worker_pool->GetSequenceToken());
247a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
248a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
249a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)NativeDesktopMediaList::~NativeDesktopMediaList() {
250a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release());
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
252a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::SetUpdatePeriod(base::TimeDelta period) {
254a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(!observer_);
255a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  update_period_ = period;
256a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
257a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::SetThumbnailSize(
259a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const gfx::Size& thumbnail_size) {
260a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  thumbnail_size_ = thumbnail_size;
261a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
262a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
263a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::SetViewDialogWindowId(
26468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    content::DesktopMediaID::Id dialog_id) {
26568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  view_dialog_id_ = dialog_id;
26668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
26768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
268a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) {
269a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(!observer_);
270a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(screen_capturer_ || window_capturer_);
271a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
272a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  observer_ = observer;
273a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
274a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
275a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           screen_capturer_.Pass(), window_capturer_.Pass()));
276a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  Refresh();
277a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
278a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
279a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)int NativeDesktopMediaList::GetSourceCount() const {
28058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return sources_.size();
28158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
28258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const DesktopMediaList::Source& NativeDesktopMediaList::GetSource(
28458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int index) const {
28558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return sources_[index];
28658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
28758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
288a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::Refresh() {
289a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  capture_task_runner_->PostTask(
290a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
29168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                            thumbnail_size_, view_dialog_id_));
292a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
293a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::OnSourcesList(
295a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const std::vector<SourceDescription>& new_sources) {
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  typedef std::set<content::DesktopMediaID> SourceSet;
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SourceSet new_source_set;
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (size_t i = 0; i < new_sources.size(); ++i) {
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    new_source_set.insert(new_sources[i].id);
3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Iterate through the old sources to find the removed sources.
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (size_t i = 0; i < sources_.size(); ++i) {
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (new_source_set.find(sources_[i].id) == new_source_set.end()) {
3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sources_.erase(sources_.begin() + i);
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      observer_->OnSourceRemoved(i);
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      --i;
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Iterate through the new sources to find the added sources.
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (new_sources.size() > sources_.size()) {
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SourceSet old_source_set;
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (size_t i = 0; i < sources_.size(); ++i) {
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      old_source_set.insert(sources_[i].id);
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (size_t i = 0; i < new_sources.size(); ++i) {
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (old_source_set.find(new_sources[i].id) == old_source_set.end()) {
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sources_.insert(sources_.begin() + i, Source());
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sources_[i].id = new_sources[i].id;
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sources_[i].name = new_sources[i].name;
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        observer_->OnSourceAdded(i);
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(new_sources.size(), sources_.size());
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Find the moved/changed sources.
328a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  size_t pos = 0;
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  while (pos < sources_.size()) {
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!(sources_[pos].id == new_sources[pos].id)) {
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Find the source that should be moved to |pos|, starting from |pos + 1|
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // of |sources_|, because entries before |pos| should have been sorted.
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      size_t old_pos = pos + 1;
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (; old_pos < sources_.size(); ++old_pos) {
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (sources_[old_pos].id == new_sources[pos].id)
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          break;
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DCHECK(sources_[old_pos].id == new_sources[pos].id);
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Move the source from |old_pos| to |pos|.
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      Source temp = sources_[old_pos];
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sources_.erase(sources_.begin() + old_pos);
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sources_.insert(sources_.begin() + pos, temp);
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      observer_->OnSourceMoved(old_pos, pos);
346a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
347a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sources_[pos].name != new_sources[pos].name) {
349a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      sources_[pos].name = new_sources[pos].name;
350a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      observer_->OnSourceNameChanged(pos);
351a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
352a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    ++pos;
353a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
354a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
355a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::OnSourceThumbnail(
357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    int index,
358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const gfx::ImageSkia& image) {
359a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK_LT(index, static_cast<int>(sources_.size()));
360a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  sources_[index].thumbnail = image;
361a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  observer_->OnSourceThumbnailChanged(index);
362a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
363a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
364a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void NativeDesktopMediaList::OnRefreshFinished() {
365a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  BrowserThread::PostDelayedTask(
366a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      BrowserThread::UI, FROM_HERE,
367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::Bind(&NativeDesktopMediaList::Refresh,
36858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 weak_factory_.GetWeakPtr()),
369a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      update_period_);
370a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
371