refactor: desktop capturer module (#14835)

* Revert "post all desktop capturer apis to worker threads"

This reverts commit 5a28759fea.

* refactor: desktop capturer module

* Creates the screen and window capturer for the liftime of the app
* Fixes incorrect usage of weak ptr

* build: add //ui/snapshot to chromium_src deps

* fix: handle scenarios when there are no captured sources
This commit is contained in:
Robo 2018-10-03 17:56:42 +05:30 committed by Cheng Zhao
parent e06bc311a9
commit 596ae2c2df
13 changed files with 465 additions and 829 deletions

View file

@ -1,61 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_
#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "content/public/browser/desktop_media_id.h"
#include "ui/gfx/image/image_skia.h"
class DesktopMediaListObserver;
// DesktopMediaList provides the list of desktop media source (screens, windows,
// tabs), and their thumbnails, to the desktop media picker dialog. It
// transparently updates the list in the background, and notifies the desktop
// media picker when something changes.
class DesktopMediaList {
public:
// Struct used to represent each entry in the list.
struct Source {
// Id of the source.
content::DesktopMediaID id;
// Name of the source that should be shown to the user.
base::string16 name;
// The thumbnail for the source.
gfx::ImageSkia thumbnail;
};
virtual ~DesktopMediaList() {}
// Sets time interval between updates. By default list of sources and their
// thumbnail are updated once per second. If called after StartUpdating() then
// it will take effect only after the next update.
virtual void SetUpdatePeriod(base::TimeDelta period) = 0;
// Sets size to which the thumbnails should be scaled. If called after
// StartUpdating() then some thumbnails may be still scaled to the old size
// until they are updated.
virtual void SetThumbnailSize(const gfx::Size& thumbnail_size) = 0;
// Sets ID of the hosting desktop picker dialog. The window with this ID will
// be filtered out from the list of sources.
virtual void SetViewDialogWindowId(content::DesktopMediaID::Id dialog_id) = 0;
// Starts updating the model. The model is initially empty, so OnSourceAdded()
// notifications will be generated for each existing source as it is
// enumerated. After the initial enumeration the model will be refreshed based
// on the update period, and notifications generated only for changes in the
// model.
virtual void StartUpdating(DesktopMediaListObserver* observer) = 0;
virtual int GetSourceCount() const = 0;
virtual const Source& GetSource(int index) const = 0;
virtual std::vector<Source> GetSources() const = 0;
};
#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_

View file

@ -1,26 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_
#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_
// Interface implemented by the desktop media picker dialog to receive
// notifications about changes in DesktopMediaList.
class DesktopMediaListObserver {
public:
virtual void OnSourceAdded(int index) = 0;
virtual void OnSourceRemoved(int index) = 0;
virtual void OnSourceMoved(int old_index, int new_index) = 0;
virtual void OnSourceNameChanged(int index) = 0;
virtual void OnSourceThumbnailChanged(int index) = 0;
// Return false to stop refreshing. The associated |DesktopMediaList| should
// no longer be used.
virtual bool OnRefreshFinished() = 0;
protected:
virtual ~DesktopMediaListObserver() {}
};
#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_

View file

@ -1,367 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/native_desktop_media_list.h"
#include <map>
#include <set>
#include <sstream>
using base::PlatformThreadRef;
#include "base/hash.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/media/desktop_media_list_observer.h"
#include "content/public/browser/browser_thread.h"
#include "media/base/video_util.h"
#include "third_party/libyuv/include/libyuv/scale_argb.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/skia_util.h"
using content::BrowserThread;
using content::DesktopMediaID;
namespace {
// Update the list every second.
const int kDefaultUpdatePeriod = 1000;
// Returns a hash of a DesktopFrame content to detect when image for a desktop
// media source has changed.
uint32_t GetFrameHash(webrtc::DesktopFrame* frame) {
int data_size = frame->stride() * frame->size().height();
return base::Hash(frame->data(), data_size);
}
gfx::ImageSkia ScaleDesktopFrame(std::unique_ptr<webrtc::DesktopFrame> frame,
gfx::Size size) {
gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
gfx::Rect(0, 0, size.width(), size.height()),
gfx::Size(frame->size().width(), frame->size().height()));
SkBitmap result;
result.allocN32Pixels(scaled_rect.width(), scaled_rect.height(), true);
uint8* pixels_data = reinterpret_cast<uint8*>(result.getPixels());
libyuv::ARGBScale(frame->data(), frame->stride(), frame->size().width(),
frame->size().height(), pixels_data, result.rowBytes(),
scaled_rect.width(), scaled_rect.height(),
libyuv::kFilterBilinear);
// Set alpha channel values to 255 for all pixels.
// TODO(sergeyu): Fix screen/window capturers to capture alpha channel and
// remove this code. Currently screen/window capturers (at least some
// implementations) only capture R, G and B channels and set Alpha to 0.
// crbug.com/264424
for (int y = 0; y < result.height(); ++y) {
for (int x = 0; x < result.width(); ++x) {
pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] =
0xff;
}
}
return gfx::ImageSkia::CreateFrom1xBitmap(result);
}
} // namespace
NativeDesktopMediaList::SourceDescription::SourceDescription(
DesktopMediaID id,
const base::string16& name)
: id(id), name(name) {}
class NativeDesktopMediaList::Worker
: public webrtc::DesktopCapturer::Callback {
public:
Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
std::unique_ptr<webrtc::DesktopCapturer> window_capturer);
~Worker() override;
void Refresh(const gfx::Size& thumbnail_size,
content::DesktopMediaID::Id view_dialog_id);
private:
typedef std::map<DesktopMediaID, uint32> ImageHashesMap;
// webrtc::DesktopCapturer::Callback interface.
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) override;
base::WeakPtr<NativeDesktopMediaList> media_list_;
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer_;
std::unique_ptr<webrtc::DesktopCapturer> window_capturer_;
std::unique_ptr<webrtc::DesktopFrame> current_frame_;
ImageHashesMap image_hashes_;
DISALLOW_COPY_AND_ASSIGN(Worker);
};
NativeDesktopMediaList::Worker::Worker(
base::WeakPtr<NativeDesktopMediaList> media_list,
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
std::unique_ptr<webrtc::DesktopCapturer> window_capturer)
: media_list_(media_list),
screen_capturer_(std::move(screen_capturer)),
window_capturer_(std::move(window_capturer)) {
if (screen_capturer_)
screen_capturer_->Start(this);
if (window_capturer_)
window_capturer_->Start(this);
}
NativeDesktopMediaList::Worker::~Worker() {}
void NativeDesktopMediaList::Worker::Refresh(
const gfx::Size& thumbnail_size,
content::DesktopMediaID::Id view_dialog_id) {
std::vector<SourceDescription> sources;
if (screen_capturer_) {
webrtc::DesktopCapturer::SourceList screens;
if (screen_capturer_->GetSourceList(&screens)) {
bool mutiple_screens = screens.size() > 1;
base::string16 title;
for (size_t i = 0; i < screens.size(); ++i) {
if (mutiple_screens) {
title = base::UTF8ToUTF16("Screen " + base::IntToString(i + 1));
} else {
title = base::UTF8ToUTF16("Entire screen");
}
sources.push_back(SourceDescription(
DesktopMediaID(DesktopMediaID::TYPE_SCREEN, screens[i].id), title));
}
}
}
if (window_capturer_) {
webrtc::DesktopCapturer::SourceList windows;
if (window_capturer_->GetSourceList(&windows)) {
for (auto it = windows.begin(); it != windows.end(); ++it) {
// Skip the picker dialog window.
if (it->id != view_dialog_id) {
sources.push_back(SourceDescription(
DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id),
base::UTF8ToUTF16(it->title)));
}
}
}
}
// Update list of windows before updating thumbnails.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&NativeDesktopMediaList::OnSourcesList, media_list_, sources));
ImageHashesMap new_image_hashes;
// Get a thumbnail for each source.
for (size_t i = 0; i < sources.size(); ++i) {
SourceDescription& source = sources[i];
switch (source.id.type) {
case DesktopMediaID::TYPE_SCREEN:
if (!screen_capturer_->SelectSource(source.id.id))
continue;
screen_capturer_->CaptureFrame();
break;
case DesktopMediaID::TYPE_WINDOW:
if (!window_capturer_->SelectSource(source.id.id))
continue;
window_capturer_->CaptureFrame();
break;
default:
NOTREACHED();
}
// Expect that DesktopCapturer to always captures frames synchronously.
// |current_frame_| may be NULL if capture failed (e.g. because window has
// been closed).
if (current_frame_) {
uint32_t frame_hash = GetFrameHash(current_frame_.get());
new_image_hashes[source.id] = frame_hash;
// Scale the image only if it has changed.
ImageHashesMap::iterator it = image_hashes_.find(source.id);
if (it == image_hashes_.end() || it->second != frame_hash) {
gfx::ImageSkia thumbnail =
ScaleDesktopFrame(std::move(current_frame_), thumbnail_size);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&NativeDesktopMediaList::OnSourceThumbnail, media_list_,
i, thumbnail));
}
}
}
image_hashes_.swap(new_image_hashes);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_));
// Destroy capturers when done.
screen_capturer_.reset();
window_capturer_.reset();
}
void NativeDesktopMediaList::Worker::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) {
current_frame_ = std::move(frame);
}
NativeDesktopMediaList::NativeDesktopMediaList(
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
std::unique_ptr<webrtc::DesktopCapturer> window_capturer)
: screen_capturer_(std::move(screen_capturer)),
window_capturer_(std::move(window_capturer)),
update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
thumbnail_size_(100, 100),
view_dialog_id_(-1),
observer_(NULL),
weak_factory_(this) {
capture_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
{base::WithBaseSyncPrimitives(), base::MayBlock(),
base::TaskPriority::USER_VISIBLE});
}
NativeDesktopMediaList::~NativeDesktopMediaList() {
capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release());
}
void NativeDesktopMediaList::SetUpdatePeriod(base::TimeDelta period) {
DCHECK(!observer_);
update_period_ = period;
}
void NativeDesktopMediaList::SetThumbnailSize(const gfx::Size& thumbnail_size) {
thumbnail_size_ = thumbnail_size;
}
void NativeDesktopMediaList::SetViewDialogWindowId(
content::DesktopMediaID::Id dialog_id) {
view_dialog_id_ = dialog_id;
}
void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) {
DCHECK(!observer_);
DCHECK(screen_capturer_ || window_capturer_);
observer_ = observer;
worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
std::move(screen_capturer_),
std::move(window_capturer_)));
Refresh();
}
int NativeDesktopMediaList::GetSourceCount() const {
return sources_.size();
}
const DesktopMediaList::Source& NativeDesktopMediaList::GetSource(
int index) const {
return sources_[index];
}
std::vector<DesktopMediaList::Source> NativeDesktopMediaList::GetSources()
const {
return sources_;
}
void NativeDesktopMediaList::Refresh() {
capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
thumbnail_size_, view_dialog_id_));
}
void NativeDesktopMediaList::OnSourcesList(
const std::vector<SourceDescription>& new_sources) {
typedef std::set<content::DesktopMediaID> SourceSet;
SourceSet new_source_set;
for (size_t i = 0; i < new_sources.size(); ++i) {
new_source_set.insert(new_sources[i].id);
}
// Iterate through the old sources to find the removed sources.
for (size_t i = 0; i < sources_.size(); ++i) {
if (new_source_set.find(sources_[i].id) == new_source_set.end()) {
observer_->OnSourceRemoved(i);
sources_.erase(sources_.begin() + i);
--i;
}
}
// Iterate through the new sources to find the added sources.
if (new_sources.size() > sources_.size()) {
SourceSet old_source_set;
for (size_t i = 0; i < sources_.size(); ++i) {
old_source_set.insert(sources_[i].id);
}
for (size_t i = 0; i < new_sources.size(); ++i) {
if (old_source_set.find(new_sources[i].id) == old_source_set.end()) {
sources_.insert(sources_.begin() + i, Source());
sources_[i].id = new_sources[i].id;
sources_[i].name = new_sources[i].name;
observer_->OnSourceAdded(i);
}
}
}
DCHECK_EQ(new_sources.size(), sources_.size());
// Find the moved/changed sources.
size_t pos = 0;
while (pos < sources_.size()) {
if (!(sources_[pos].id == new_sources[pos].id)) {
// Find the source that should be moved to |pos|, starting from |pos + 1|
// of |sources_|, because entries before |pos| should have been sorted.
size_t old_pos = pos + 1;
for (; old_pos < sources_.size(); ++old_pos) {
if (sources_[old_pos].id == new_sources[pos].id)
break;
}
DCHECK(sources_[old_pos].id == new_sources[pos].id);
// Move the source from |old_pos| to |pos|.
Source temp = sources_[old_pos];
sources_.erase(sources_.begin() + old_pos);
sources_.insert(sources_.begin() + pos, temp);
observer_->OnSourceMoved(old_pos, pos);
}
if (sources_[pos].name != new_sources[pos].name) {
sources_[pos].name = new_sources[pos].name;
observer_->OnSourceNameChanged(pos);
}
++pos;
}
}
void NativeDesktopMediaList::OnSourceThumbnail(int index,
const gfx::ImageSkia& image) {
DCHECK_LT(index, static_cast<int>(sources_.size()));
sources_[index].thumbnail = image;
observer_->OnSourceThumbnailChanged(index);
}
void NativeDesktopMediaList::OnRefreshFinished() {
// Give a chance to the observer to stop the refresh work.
bool is_continue = observer_->OnRefreshFinished();
if (is_continue) {
BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
base::Bind(&NativeDesktopMediaList::Refresh,
weak_factory_.GetWeakPtr()),
update_period_);
}
}

View file

@ -1,98 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_
#define CHROME_BROWSER_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "chrome/browser/media/desktop_media_list.h"
#include "content/public/browser/desktop_media_id.h"
#include "ui/gfx/image/image_skia.h"
namespace webrtc {
class DesktopCapturer;
}
// Implementation of DesktopMediaList that shows native screens and
// native windows.
class NativeDesktopMediaList : public DesktopMediaList {
public:
// Caller may pass NULL for either of the arguments in case when only some
// types of sources the model should be populated with (e.g. it will only
// contain windows, if |screen_capturer| is NULL).
NativeDesktopMediaList(
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer,
std::unique_ptr<webrtc::DesktopCapturer> window_capturer);
~NativeDesktopMediaList() override;
// DesktopMediaList interface.
void SetUpdatePeriod(base::TimeDelta period) override;
void SetThumbnailSize(const gfx::Size& thumbnail_size) override;
void StartUpdating(DesktopMediaListObserver* observer) override;
int GetSourceCount() const override;
const Source& GetSource(int index) const override;
std::vector<Source> GetSources() const override;
void SetViewDialogWindowId(content::DesktopMediaID::Id dialog_id) override;
private:
class Worker;
friend class Worker;
// Struct used to represent sources list the model gets from the Worker.
struct SourceDescription {
SourceDescription(content::DesktopMediaID id, const base::string16& name);
content::DesktopMediaID id;
base::string16 name;
};
// Order comparator for sources. Used to sort list of sources.
static bool CompareSources(const SourceDescription& a,
const SourceDescription& b);
// Post a task for the |worker_| to update list of windows and get thumbnails.
void Refresh();
// Called by |worker_| to refresh the model. First it posts tasks for
// OnSourcesList() with the fresh list of sources, then follows with
// OnSourceThumbnail() for each changed thumbnail and then calls
// OnRefreshFinished() at the end.
void OnSourcesList(const std::vector<SourceDescription>& sources);
void OnSourceThumbnail(int index, const gfx::ImageSkia& thumbnail);
void OnRefreshFinished();
// Capturers specified in SetCapturers() and passed to the |worker_| later.
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer_;
std::unique_ptr<webrtc::DesktopCapturer> window_capturer_;
// Time interval between mode updates.
base::TimeDelta update_period_;
// Size of thumbnails generated by the model.
gfx::Size thumbnail_size_;
// ID of the hosting dialog.
content::DesktopMediaID::Id view_dialog_id_;
// The observer passed to StartUpdating().
DesktopMediaListObserver* observer_;
// Task runner used for the |worker_|.
scoped_refptr<base::SequencedTaskRunner> capture_task_runner_;
// An object that does all the work of getting list of sources on a background
// thread (see |capture_task_runner_|). Destroyed on |capture_task_runner_|
// after the model is destroyed.
std::unique_ptr<Worker> worker_;
// Current list of sources.
std::vector<Source> sources_;
base::WeakPtrFactory<NativeDesktopMediaList> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(NativeDesktopMediaList);
};
#endif // CHROME_BROWSER_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_