Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: chrome/browser/media/native_desktop_media_list.cc

Issue 1763753003: Capture chrome browser windows from internal rendering procedure for windows and linux (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/native_desktop_media_list.h" 5 #include "chrome/browser/media/native_desktop_media_list.h"
6 6
7 #include "base/hash.h" 7 #include "base/hash.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "base/threading/sequenced_worker_pool.h" 9 #include "base/threading/sequenced_worker_pool.h"
10 #include "chrome/browser/media/desktop_media_list_observer.h" 10 #include "chrome/browser/media/desktop_media_list_observer.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_list.h"
13 #include "chrome/browser/ui/browser_window.h"
11 #include "chrome/grit/generated_resources.h" 14 #include "chrome/grit/generated_resources.h"
12 #include "content/public/browser/browser_thread.h" 15 #include "content/public/browser/browser_thread.h"
13 #include "media/base/video_util.h" 16 #include "media/base/video_util.h"
14 #include "third_party/libyuv/include/libyuv/scale_argb.h" 17 #include "third_party/libyuv/include/libyuv/scale_argb.h"
15 #include "third_party/skia/include/core/SkBitmap.h" 18 #include "third_party/skia/include/core/SkBitmap.h"
16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
17 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 20 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
18 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h" 21 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
22 #include "ui/aura/window.h"
23 #include "ui/aura/window_tree_host.h"
19 #include "ui/base/l10n/l10n_util.h" 24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/gfx/native_widget_types.h"
26 #include "ui/snapshot/snapshot.h"
20 27
21 using content::BrowserThread; 28 using content::BrowserThread;
22 using content::DesktopMediaID; 29 using content::DesktopMediaID;
23 30
24 namespace { 31 namespace {
25 32
26 // Update the list every second. 33 // Update the list every second.
27 const int kDefaultUpdatePeriod = 1000; 34 const int kDefaultUpdatePeriod = 1000;
28 35
29 // Returns a hash of a DesktopFrame content to detect when image for a desktop 36 // Returns a hash of a DesktopFrame content to detect when image for a desktop
30 // media source has changed. 37 // media source has changed.
31 uint32_t GetFrameHash(webrtc::DesktopFrame* frame) { 38 uint32_t GetFrameHash(webrtc::DesktopFrame* frame) {
32 int data_size = frame->stride() * frame->size().height(); 39 int data_size = frame->stride() * frame->size().height();
33 return base::SuperFastHash(reinterpret_cast<char*>(frame->data()), data_size); 40 return base::Hash(reinterpret_cast<char*>(frame->data()), data_size);
34 } 41 }
35 42
36 gfx::ImageSkia ScaleDesktopFrame(scoped_ptr<webrtc::DesktopFrame> frame, 43 gfx::ImageSkia ScaleDesktopFrame(scoped_ptr<webrtc::DesktopFrame> frame,
37 gfx::Size size) { 44 gfx::Size size) {
38 gfx::Rect scaled_rect = media::ComputeLetterboxRegion( 45 gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
39 gfx::Rect(0, 0, size.width(), size.height()), 46 gfx::Rect(0, 0, size.width(), size.height()),
40 gfx::Size(frame->size().width(), frame->size().height())); 47 gfx::Size(frame->size().width(), frame->size().height()));
41 48
42 SkBitmap result; 49 SkBitmap result;
43 result.allocN32Pixels(scaled_rect.width(), scaled_rect.height(), true); 50 result.allocN32Pixels(scaled_rect.width(), scaled_rect.height(), true);
(...skipping 16 matching lines...) Expand all
60 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] = 67 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] =
61 0xff; 68 0xff;
62 } 69 }
63 } 70 }
64 71
65 result.unlockPixels(); 72 result.unlockPixels();
66 73
67 return gfx::ImageSkia::CreateFrom1xBitmap(result); 74 return gfx::ImageSkia::CreateFrom1xBitmap(result);
68 } 75 }
69 76
77 #if defined(USE_AURA)
78
79 NativeDesktopMediaList::NativeAuraIdMap GetBrowserNativeAuraIdMap() {
80 NativeDesktopMediaList::NativeAuraIdMap id_map;
81 for (auto* browser : *BrowserList::GetInstance()) {
82 aura::Window* aura_window = browser->window()->GetNativeWindow();
83 if (!aura_window)
84 continue;
85 aura::WindowTreeHost* host = aura_window->GetHost();
86 if (!host)
87 continue;
88 gfx::AcceleratedWidget widget = host->GetAcceleratedWidget();
89 #if defined(OS_WIN)
90 DesktopMediaID::Id native_id = reinterpret_cast<DesktopMediaID::Id>(widget);
91 #else
92 DesktopMediaID::Id native_id = widget;
93 #endif
94 DesktopMediaID media_id = DesktopMediaID::RegisterAuraWindow(
95 DesktopMediaID::TYPE_WINDOW, aura_window);
96 id_map[native_id] = media_id.aura_id;
97 }
98
99 return id_map;
100 }
101
102 #endif // defined(USE_AURA)
103
70 } // namespace 104 } // namespace
71 105
72 class NativeDesktopMediaList::Worker 106 class NativeDesktopMediaList::Worker
73 : public webrtc::DesktopCapturer::Callback { 107 : public webrtc::DesktopCapturer::Callback {
74 public: 108 public:
75 Worker(base::WeakPtr<NativeDesktopMediaList> media_list, 109 Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
76 scoped_ptr<webrtc::ScreenCapturer> screen_capturer, 110 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
77 scoped_ptr<webrtc::WindowCapturer> window_capturer); 111 scoped_ptr<webrtc::WindowCapturer> window_capturer);
78 ~Worker() override; 112 ~Worker() override;
79 113
80 void Refresh(const gfx::Size& thumbnail_size, 114 void Refresh(const gfx::Size& thumbnail_size,
81 content::DesktopMediaID::Id view_dialog_id); 115 DesktopMediaID::Id view_dialog_id,
116 NativeAuraIdMap native_aura_id_map);
DaleCurtis 2016/03/16 21:10:00 This appears to be passing by copy, is that intent
GeorgeZ 2016/03/16 21:34:13 Can be constant. Good catch.
82 117
83 private: 118 private:
84 typedef std::map<DesktopMediaID, uint32_t> ImageHashesMap; 119 typedef std::map<DesktopMediaID, uint32_t> ImageHashesMap;
85 120
86 // webrtc::DesktopCapturer::Callback interface. 121 // webrtc::DesktopCapturer::Callback interface.
87 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override; 122 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override;
88 123
89 base::WeakPtr<NativeDesktopMediaList> media_list_; 124 base::WeakPtr<NativeDesktopMediaList> media_list_;
90 125
91 scoped_ptr<webrtc::ScreenCapturer> screen_capturer_; 126 scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
(...skipping 16 matching lines...) Expand all
108 if (screen_capturer_) 143 if (screen_capturer_)
109 screen_capturer_->Start(this); 144 screen_capturer_->Start(this);
110 if (window_capturer_) 145 if (window_capturer_)
111 window_capturer_->Start(this); 146 window_capturer_->Start(this);
112 } 147 }
113 148
114 NativeDesktopMediaList::Worker::~Worker() {} 149 NativeDesktopMediaList::Worker::~Worker() {}
115 150
116 void NativeDesktopMediaList::Worker::Refresh( 151 void NativeDesktopMediaList::Worker::Refresh(
117 const gfx::Size& thumbnail_size, 152 const gfx::Size& thumbnail_size,
118 content::DesktopMediaID::Id view_dialog_id) { 153 DesktopMediaID::Id view_dialog_id,
154 NativeAuraIdMap native_aura_id_map) {
DaleCurtis 2016/03/16 21:10:00 Ditto.
GeorgeZ 2016/03/16 21:34:14 Done.
119 std::vector<SourceDescription> sources; 155 std::vector<SourceDescription> sources;
156 std::vector<DesktopMediaID> aura_media_ids;
120 157
121 if (screen_capturer_) { 158 if (screen_capturer_) {
122 webrtc::ScreenCapturer::ScreenList screens; 159 webrtc::ScreenCapturer::ScreenList screens;
123 if (screen_capturer_->GetScreenList(&screens)) { 160 if (screen_capturer_->GetScreenList(&screens)) {
124 bool mutiple_screens = screens.size() > 1; 161 bool mutiple_screens = screens.size() > 1;
125 base::string16 title; 162 base::string16 title;
126 for (size_t i = 0; i < screens.size(); ++i) { 163 for (size_t i = 0; i < screens.size(); ++i) {
127 if (mutiple_screens) { 164 if (mutiple_screens) {
128 title = l10n_util::GetStringFUTF16Int( 165 title = l10n_util::GetStringFUTF16Int(
129 IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME, 166 IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
130 static_cast<int>(i + 1)); 167 static_cast<int>(i + 1));
131 } else { 168 } else {
132 title = l10n_util::GetStringUTF16( 169 title = l10n_util::GetStringUTF16(
133 IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME); 170 IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
134 } 171 }
135 sources.push_back(SourceDescription(DesktopMediaID( 172 sources.push_back(SourceDescription(DesktopMediaID(
136 DesktopMediaID::TYPE_SCREEN, screens[i].id), title)); 173 DesktopMediaID::TYPE_SCREEN, screens[i].id), title));
137 } 174 }
138 } 175 }
139 } 176 }
140 177
141 if (window_capturer_) { 178 if (window_capturer_) {
142 webrtc::WindowCapturer::WindowList windows; 179 webrtc::WindowCapturer::WindowList windows;
143 if (window_capturer_->GetWindowList(&windows)) { 180 if (window_capturer_->GetWindowList(&windows)) {
144 for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin(); 181 for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin();
145 it != windows.end(); ++it) { 182 it != windows.end(); ++it) {
146 // Skip the picker dialog window. 183 // Skip the picker dialog window.
147 if (it->id != view_dialog_id) { 184 if (it->id != view_dialog_id) {
148 sources.push_back(SourceDescription( 185 DesktopMediaID media_id(DesktopMediaID::TYPE_WINDOW, it->id);
149 DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id), 186 #if defined(USE_AURA)
150 base::UTF8ToUTF16(it->title))); 187 // Associate aura id with native id.
188 auto aura_id = native_aura_id_map.find(media_id.id);
189 if (aura_id != native_aura_id_map.end()) {
190 media_id.aura_id = aura_id->second;
191 aura_media_ids.push_back(media_id);
192 }
193 #endif
194 sources.push_back(
195 SourceDescription(media_id, base::UTF8ToUTF16(it->title)));
151 } 196 }
152 } 197 }
153 } 198 }
154 } 199 }
200
155 // Update list of windows before updating thumbnails. 201 // Update list of windows before updating thumbnails.
156 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 202 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
157 base::Bind(&NativeDesktopMediaList::UpdateSourcesList, 203 base::Bind(&NativeDesktopMediaList::UpdateSourcesList,
158 media_list_, sources)); 204 media_list_, sources));
159 205
160 ImageHashesMap new_image_hashes; 206 ImageHashesMap new_image_hashes;
161 207
162 // Get a thumbnail for each source. 208 // Get a thumbnail for each source.
163 for (size_t i = 0; i < sources.size(); ++i) { 209 for (size_t i = 0; i < sources.size(); ++i) {
164 SourceDescription& source = sources[i]; 210 SourceDescription& source = sources[i];
211
165 switch (source.id.type) { 212 switch (source.id.type) {
166 case DesktopMediaID::TYPE_SCREEN: 213 case DesktopMediaID::TYPE_SCREEN:
167 if (!screen_capturer_->SelectScreen(source.id.id)) 214 if (!screen_capturer_->SelectScreen(source.id.id))
168 continue; 215 continue;
169 screen_capturer_->Capture(webrtc::DesktopRegion()); 216 screen_capturer_->Capture(webrtc::DesktopRegion());
170 break; 217 break;
171 218
172 case DesktopMediaID::TYPE_WINDOW: 219 case DesktopMediaID::TYPE_WINDOW:
220 #if defined(USE_AURA)
221 // Aura window thumbmail capture is skipped here. It will be done
222 // asynchronously in the UI thread.
223 if (source.id.aura_id > DesktopMediaID::kNullId)
224 continue;
225 #endif
173 if (!window_capturer_->SelectWindow(source.id.id)) 226 if (!window_capturer_->SelectWindow(source.id.id))
174 continue; 227 continue;
175 window_capturer_->Capture(webrtc::DesktopRegion()); 228 window_capturer_->Capture(webrtc::DesktopRegion());
176 break; 229 break;
177 230
178 default: 231 default:
179 NOTREACHED(); 232 NOTREACHED();
180 } 233 }
181 234
182 // Expect that DesktopCapturer to always captures frames synchronously. 235 // Expect that DesktopCapturer to always captures frames synchronously.
183 // |current_frame_| may be NULL if capture failed (e.g. because window has 236 // |current_frame_| may be NULL if capture failed (e.g. because window has
184 // been closed). 237 // been closed).
185 if (current_frame_) { 238 if (current_frame_) {
186 uint32_t frame_hash = GetFrameHash(current_frame_.get()); 239 uint32_t frame_hash = GetFrameHash(current_frame_.get());
187 new_image_hashes[source.id] = frame_hash; 240 new_image_hashes[source.id] = frame_hash;
188 241
189 // Scale the image only if it has changed. 242 // Scale the image only if it has changed.
190 ImageHashesMap::iterator it = image_hashes_.find(source.id); 243 ImageHashesMap::iterator it = image_hashes_.find(source.id);
191 if (it == image_hashes_.end() || it->second != frame_hash) { 244 if (it == image_hashes_.end() || it->second != frame_hash) {
192 gfx::ImageSkia thumbnail = 245 gfx::ImageSkia thumbnail =
193 ScaleDesktopFrame(std::move(current_frame_), thumbnail_size); 246 ScaleDesktopFrame(std::move(current_frame_), thumbnail_size);
194 BrowserThread::PostTask( 247 BrowserThread::PostTask(
195 BrowserThread::UI, FROM_HERE, 248 BrowserThread::UI, FROM_HERE,
196 base::Bind(&NativeDesktopMediaList::OnSourceThumbnail, media_list_, 249 base::Bind(&NativeDesktopMediaList::OnSourceThumbnailCaptured,
197 i, thumbnail)); 250 media_list_, i, thumbnail));
198 } 251 }
199 } 252 }
200 } 253 }
201 254
202 image_hashes_.swap(new_image_hashes); 255 image_hashes_.swap(new_image_hashes);
203 256
257 // Aura thumbnail captures have to be done in UI thread. After they are done,
258 // a refresh will be scheduled.
204 BrowserThread::PostTask( 259 BrowserThread::PostTask(
205 BrowserThread::UI, FROM_HERE, 260 BrowserThread::UI, FROM_HERE,
206 base::Bind(&NativeDesktopMediaList::ScheduleNextRefresh, media_list_)); 261 base::Bind(
262 &NativeDesktopMediaList::FinishRefreshOnUiThreadAndScheduleNext,
263 media_list_, aura_media_ids));
207 } 264 }
208 265
209 void NativeDesktopMediaList::Worker::OnCaptureCompleted( 266 void NativeDesktopMediaList::Worker::OnCaptureCompleted(
210 webrtc::DesktopFrame* frame) { 267 webrtc::DesktopFrame* frame) {
211 current_frame_.reset(frame); 268 current_frame_.reset(frame);
212 } 269 }
213 270
214 NativeDesktopMediaList::NativeDesktopMediaList( 271 NativeDesktopMediaList::NativeDesktopMediaList(
215 scoped_ptr<webrtc::ScreenCapturer> screen_capturer, 272 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
216 scoped_ptr<webrtc::WindowCapturer> window_capturer) 273 scoped_ptr<webrtc::WindowCapturer> window_capturer)
217 : DesktopMediaListBase( 274 : DesktopMediaListBase(
218 base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)), 275 base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
219 weak_factory_(this) { 276 weak_factory_(this) {
220 base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool(); 277 base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
221 capture_task_runner_ = worker_pool->GetSequencedTaskRunner( 278 capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
222 worker_pool->GetSequenceToken()); 279 worker_pool->GetSequenceToken());
223 280
224 worker_.reset(new Worker(weak_factory_.GetWeakPtr(), 281 worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
225 std::move(screen_capturer), 282 std::move(screen_capturer),
226 std::move(window_capturer))); 283 std::move(window_capturer)));
227 } 284 }
228 285
229 NativeDesktopMediaList::~NativeDesktopMediaList() { 286 NativeDesktopMediaList::~NativeDesktopMediaList() {
230 capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release()); 287 capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release());
231 } 288 }
232 289
233 void NativeDesktopMediaList::Refresh() { 290 void NativeDesktopMediaList::Refresh() {
291 NativeAuraIdMap native_aura_id_map;
292 #if defined(USE_AURA)
293 native_aura_id_map = GetBrowserNativeAuraIdMap();
294 pending_aura_capture_requests_ = 0;
295 new_aura_thumbnail_hashes_.clear();
296 #endif
297
234 capture_task_runner_->PostTask( 298 capture_task_runner_->PostTask(
235 FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()), 299 FROM_HERE,
236 thumbnail_size_, view_dialog_id_.id)); 300 base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
301 thumbnail_size_, view_dialog_id_.id, native_aura_id_map));
237 } 302 }
238 303
239 void NativeDesktopMediaList::OnSourceThumbnail(int index, 304 void NativeDesktopMediaList::OnSourceThumbnailCaptured(
240 const gfx::ImageSkia& image) { 305 int index,
306 const gfx::ImageSkia& image) {
241 UpdateSourceThumbnail(GetSource(index).id, image); 307 UpdateSourceThumbnail(GetSource(index).id, image);
242 } 308 }
309
310 void NativeDesktopMediaList::FinishRefreshOnUiThreadAndScheduleNext(
311 const std::vector<DesktopMediaID>& aura_ids) {
312 // Schedule a refresh here when there is no aura thumbanil capture or schedule
313 // a refresh in OnAuraThumbnailCaptured() after all aura thumbnails are
314 // captured.
315 if (aura_ids.size() == 0) {
316 ScheduleNextRefresh();
317 return;
318 }
319
320 #if defined(USE_AURA)
321 DCHECK_EQ(pending_aura_capture_requests_, 0);
322 for (const auto& aura_id : aura_ids) {
323 CaptureAuraWindowThumbnail(aura_id);
324 }
325 #endif
326 }
327
328 #if defined(USE_AURA)
329
330 void NativeDesktopMediaList::CaptureAuraWindowThumbnail(DesktopMediaID id) {
331 gfx::NativeWindow window = DesktopMediaID::GetAuraWindowById(id);
332 if (!window)
333 return;
334
335 gfx::Rect window_rect(window->bounds().width(), window->bounds().height());
336 gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
337 gfx::Rect(thumbnail_size_), window_rect.size());
338
339 pending_aura_capture_requests_++;
340 ui::GrabWindowSnapshotAndScaleAsync(
341 window, window_rect, scaled_rect.size(), BrowserThread::GetBlockingPool(),
342 base::Bind(&NativeDesktopMediaList::OnAuraThumbnailCaptured,
343 weak_factory_.GetWeakPtr(), id));
344 }
345
346 void NativeDesktopMediaList::OnAuraThumbnailCaptured(DesktopMediaID id,
347 const gfx::Image& image) {
348 if (!image.IsEmpty()) {
349 // Only new or changed thumbnail need update.
350 new_aura_thumbnail_hashes_[id] = GetImageHash(image);
351 if (!previous_aura_thumbnail_hashes_.count(id) ||
352 previous_aura_thumbnail_hashes_[id] != new_aura_thumbnail_hashes_[id]) {
353 UpdateSourceThumbnail(id, image.AsImageSkia());
354 }
355 }
356
357 // After all aura windows are processed, schedule next refresh;
358 pending_aura_capture_requests_--;
359 DCHECK_GE(pending_aura_capture_requests_, 0);
360 if (pending_aura_capture_requests_ == 0) {
361 previous_aura_thumbnail_hashes_ = std::move(new_aura_thumbnail_hashes_);
362 ScheduleNextRefresh();
363 }
364 }
365
366 #endif // defined(USE_AURA)
OLDNEW
« no previous file with comments | « chrome/browser/media/native_desktop_media_list.h ('k') | chrome/browser/media/tab_desktop_media_list.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698