Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media / native_desktop_media_list.cc
blob7f5128b5dd579cf6aa26f2e19c24e9fd0cebbd17
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media/native_desktop_media_list.h"
7 #include <map>
9 #include "base/hash.h"
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/media/desktop_media_list_observer.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "grit/generated_resources.h"
16 #include "media/base/video_util.h"
17 #include "third_party/libyuv/include/libyuv/scale_argb.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
20 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
21 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/gfx/skia_util.h"
25 using content::BrowserThread;
26 using content::DesktopMediaID;
28 namespace {
30 // Update the list every second.
31 const int kDefaultUpdatePeriod = 1000;
33 // Returns a hash of a DesktopFrame content to detect when image for a desktop
34 // media source has changed.
35 uint32 GetFrameHash(webrtc::DesktopFrame* frame) {
36 int data_size = frame->stride() * frame->size().height();
37 return base::SuperFastHash(reinterpret_cast<char*>(frame->data()), data_size);
40 gfx::ImageSkia ScaleDesktopFrame(scoped_ptr<webrtc::DesktopFrame> frame,
41 gfx::Size size) {
42 gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
43 gfx::Rect(0, 0, size.width(), size.height()),
44 gfx::Size(frame->size().width(), frame->size().height()));
46 SkBitmap result;
47 result.setConfig(SkBitmap::kARGB_8888_Config,
48 scaled_rect.width(), scaled_rect.height(), 0,
49 kOpaque_SkAlphaType);
50 result.allocPixels();
51 result.lockPixels();
53 uint8* pixels_data = reinterpret_cast<uint8*>(result.getPixels());
54 libyuv::ARGBScale(frame->data(), frame->stride(),
55 frame->size().width(), frame->size().height(),
56 pixels_data, result.rowBytes(),
57 scaled_rect.width(), scaled_rect.height(),
58 libyuv::kFilterBilinear);
60 // Set alpha channel values to 255 for all pixels.
61 // TODO(sergeyu): Fix screen/window capturers to capture alpha channel and
62 // remove this code. Currently screen/window capturers (at least some
63 // implementations) only capture R, G and B channels and set Alpha to 0.
64 // crbug.com/264424
65 for (int y = 0; y < result.height(); ++y) {
66 for (int x = 0; x < result.width(); ++x) {
67 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] =
68 0xff;
72 result.unlockPixels();
74 return gfx::ImageSkia::CreateFrom1xBitmap(result);
77 } // namespace
79 NativeDesktopMediaList::SourceDescription::SourceDescription(
80 DesktopMediaID id,
81 const base::string16& name)
82 : id(id),
83 name(name) {
86 class NativeDesktopMediaList::Worker
87 : public webrtc::DesktopCapturer::Callback {
88 public:
89 Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
90 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
91 scoped_ptr<webrtc::WindowCapturer> window_capturer);
92 virtual ~Worker();
94 void Refresh(const gfx::Size& thumbnail_size,
95 content::DesktopMediaID::Id view_dialog_id);
97 private:
98 typedef std::map<DesktopMediaID, uint32> ImageHashesMap;
100 // webrtc::DesktopCapturer::Callback interface.
101 virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
102 virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;
104 base::WeakPtr<NativeDesktopMediaList> media_list_;
106 scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
107 scoped_ptr<webrtc::WindowCapturer> window_capturer_;
109 scoped_ptr<webrtc::DesktopFrame> current_frame_;
111 ImageHashesMap image_hashes_;
113 DISALLOW_COPY_AND_ASSIGN(Worker);
116 NativeDesktopMediaList::Worker::Worker(
117 base::WeakPtr<NativeDesktopMediaList> media_list,
118 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
119 scoped_ptr<webrtc::WindowCapturer> window_capturer)
120 : media_list_(media_list),
121 screen_capturer_(screen_capturer.Pass()),
122 window_capturer_(window_capturer.Pass()) {
123 if (screen_capturer_)
124 screen_capturer_->Start(this);
125 if (window_capturer_)
126 window_capturer_->Start(this);
129 NativeDesktopMediaList::Worker::~Worker() {}
131 void NativeDesktopMediaList::Worker::Refresh(
132 const gfx::Size& thumbnail_size,
133 content::DesktopMediaID::Id view_dialog_id) {
134 std::vector<SourceDescription> sources;
136 if (screen_capturer_) {
137 // TODO(sergeyu): Enumerate each screen when ScreenCapturer supports it.
138 sources.push_back(SourceDescription(DesktopMediaID(
139 DesktopMediaID::TYPE_SCREEN, 0),
140 l10n_util::GetStringUTF16(IDS_DESKTOP_MEDIA_PICKER_SCREEN_NAME)));
143 if (window_capturer_) {
144 webrtc::WindowCapturer::WindowList windows;
145 if (window_capturer_->GetWindowList(&windows)) {
146 for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin();
147 it != windows.end(); ++it) {
148 // Skip the picker dialog window.
149 if (it->id != view_dialog_id) {
150 sources.push_back(SourceDescription(
151 DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id),
152 base::UTF8ToUTF16(it->title)));
158 // Sort the list of sources so that they appear in a predictable order.
159 std::sort(sources.begin(), sources.end(), CompareSources);
161 // Update list of windows before updating thumbnails.
162 BrowserThread::PostTask(
163 BrowserThread::UI, FROM_HERE,
164 base::Bind(&NativeDesktopMediaList::OnSourcesList,
165 media_list_, sources));
167 ImageHashesMap new_image_hashes;
169 // Get a thumbnail for each source.
170 for (size_t i = 0; i < sources.size(); ++i) {
171 SourceDescription& source = sources[i];
172 switch (source.id.type) {
173 case DesktopMediaID::TYPE_SCREEN:
174 screen_capturer_->Capture(webrtc::DesktopRegion());
175 DCHECK(current_frame_);
176 break;
178 case DesktopMediaID::TYPE_WINDOW:
179 if (!window_capturer_->SelectWindow(source.id.id))
180 continue;
181 window_capturer_->Capture(webrtc::DesktopRegion());
182 break;
184 default:
185 NOTREACHED();
188 // Expect that DesktopCapturer to always captures frames synchronously.
189 // |current_frame_| may be NULL if capture failed (e.g. because window has
190 // been closed).
191 if (current_frame_) {
192 uint32 frame_hash = GetFrameHash(current_frame_.get());
193 new_image_hashes[source.id] = frame_hash;
195 // Scale the image only if it has changed.
196 ImageHashesMap::iterator it = image_hashes_.find(source.id);
197 if (it == image_hashes_.end() || it->second != frame_hash) {
198 gfx::ImageSkia thumbnail =
199 ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size);
200 BrowserThread::PostTask(
201 BrowserThread::UI, FROM_HERE,
202 base::Bind(&NativeDesktopMediaList::OnSourceThumbnail,
203 media_list_, i, thumbnail));
208 image_hashes_.swap(new_image_hashes);
210 BrowserThread::PostTask(
211 BrowserThread::UI, FROM_HERE,
212 base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_));
215 webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory(
216 size_t size) {
217 return NULL;
220 void NativeDesktopMediaList::Worker::OnCaptureCompleted(
221 webrtc::DesktopFrame* frame) {
222 current_frame_.reset(frame);
225 NativeDesktopMediaList::NativeDesktopMediaList(
226 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
227 scoped_ptr<webrtc::WindowCapturer> window_capturer)
228 : screen_capturer_(screen_capturer.Pass()),
229 window_capturer_(window_capturer.Pass()),
230 update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
231 thumbnail_size_(100, 100),
232 view_dialog_id_(-1),
233 observer_(NULL),
234 weak_factory_(this) {
235 base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
236 capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
237 worker_pool->GetSequenceToken());
240 NativeDesktopMediaList::~NativeDesktopMediaList() {
241 capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release());
244 void NativeDesktopMediaList::SetUpdatePeriod(base::TimeDelta period) {
245 DCHECK(!observer_);
246 update_period_ = period;
249 void NativeDesktopMediaList::SetThumbnailSize(
250 const gfx::Size& thumbnail_size) {
251 thumbnail_size_ = thumbnail_size;
254 void NativeDesktopMediaList::SetViewDialogWindowId(
255 content::DesktopMediaID::Id dialog_id) {
256 view_dialog_id_ = dialog_id;
259 void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) {
260 DCHECK(!observer_);
261 DCHECK(screen_capturer_ || window_capturer_);
263 observer_ = observer;
265 worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
266 screen_capturer_.Pass(), window_capturer_.Pass()));
267 Refresh();
270 int NativeDesktopMediaList::GetSourceCount() const {
271 return sources_.size();
274 const DesktopMediaList::Source& NativeDesktopMediaList::GetSource(
275 int index) const {
276 return sources_[index];
279 // static
280 bool NativeDesktopMediaList::CompareSources(const SourceDescription& a,
281 const SourceDescription& b) {
282 return a.id < b.id;
285 void NativeDesktopMediaList::Refresh() {
286 capture_task_runner_->PostTask(
287 FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
288 thumbnail_size_, view_dialog_id_));
291 void NativeDesktopMediaList::OnSourcesList(
292 const std::vector<SourceDescription>& new_sources) {
293 // Step through |new_sources| adding and removing entries from |sources_|, and
294 // notifying the |observer_|, until two match. Requires that |sources| and
295 // |sources_| have the same ordering.
296 size_t pos = 0;
297 while (pos < sources_.size() || pos < new_sources.size()) {
298 // If |sources_[pos]| is not in |new_sources| then remove it.
299 if (pos < sources_.size() &&
300 (pos == new_sources.size() || sources_[pos].id < new_sources[pos].id)) {
301 sources_.erase(sources_.begin() + pos);
302 observer_->OnSourceRemoved(pos);
303 continue;
306 if (pos == sources_.size() || !(sources_[pos].id == new_sources[pos].id)) {
307 sources_.insert(sources_.begin() + pos, Source());
308 sources_[pos].id = new_sources[pos].id;
309 sources_[pos].name = new_sources[pos].name;
310 observer_->OnSourceAdded(pos);
311 } else if (sources_[pos].name != new_sources[pos].name) {
312 sources_[pos].name = new_sources[pos].name;
313 observer_->OnSourceNameChanged(pos);
316 ++pos;
319 DCHECK_EQ(new_sources.size(), sources_.size());
322 void NativeDesktopMediaList::OnSourceThumbnail(
323 int index,
324 const gfx::ImageSkia& image) {
325 DCHECK_LT(index, static_cast<int>(sources_.size()));
326 sources_[index].thumbnail = image;
327 observer_->OnSourceThumbnailChanged(index);
330 void NativeDesktopMediaList::OnRefreshFinished() {
331 BrowserThread::PostDelayedTask(
332 BrowserThread::UI, FROM_HERE,
333 base::Bind(&NativeDesktopMediaList::Refresh,
334 weak_factory_.GetWeakPtr()),
335 update_period_);