Implement desktop capture API on OS X.
This commit is contained in:
parent
a0638fe801
commit
c9fbde321c
12 changed files with 768 additions and 6 deletions
4
atom.gyp
4
atom.gyp
|
@ -245,6 +245,10 @@
|
|||
'vendor/node/deps/cares/include',
|
||||
# The `third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h` is using `platform/PlatformExport.h`.
|
||||
'<(libchromiumcontent_src_dir)/third_party/WebKit/Source',
|
||||
# The 'third_party/libyuv/include/libyuv/scale_argb.h' is using 'libyuv/basic_types.h'.
|
||||
'<(libchromiumcontent_src_dir)/third_party/libyuv/include',
|
||||
# The 'third_party/webrtc/modules/desktop_capture/desktop_frame.h' is using 'webrtc/base/scoped_ptr.h'.
|
||||
'<(libchromiumcontent_src_dir)/third_party/',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
|
|
127
atom/browser/api/atom_api_desktop_capturer.cc
Normal file
127
atom/browser/api/atom_api_desktop_capturer.cc
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/api/atom_api_desktop_capturer.h"
|
||||
|
||||
#include "atom/common/api/atom_api_native_image.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/media/desktop_media_list.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
|
||||
#include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<DesktopMediaList::Source> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const DesktopMediaList::Source& source) {
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
content::DesktopMediaID id = source.id;
|
||||
dict.Set("name", base::UTF16ToUTF8(source.name));
|
||||
dict.Set("id", id.ToString());
|
||||
dict.Set("thumbnail",
|
||||
atom::api::NativeImage::Create(isolate, gfx::Image(source.thumbnail)));
|
||||
return ConvertToV8(isolate, dict);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
// The wrapDesktopCapturer funtion which is implemented in JavaScript
|
||||
using WrapDesktopCapturerCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
WrapDesktopCapturerCallback g_wrap_desktop_capturer;
|
||||
|
||||
const int kThumbnailWidth = 150;
|
||||
const int kThumbnailHeight = 150;
|
||||
} // namespace
|
||||
|
||||
DesktopCapturer::DesktopCapturer(bool show_screens, bool show_windows) {
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
|
||||
show_screens ? webrtc::ScreenCapturer::Create() : nullptr);
|
||||
scoped_ptr<webrtc::WindowCapturer> window_capturer(
|
||||
show_windows ? webrtc::WindowCapturer::Create() : nullptr);
|
||||
media_list_.reset(new NativeDesktopMediaList(screen_capturer.Pass(),
|
||||
window_capturer.Pass()));
|
||||
media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
|
||||
media_list_->StartUpdating(this);
|
||||
}
|
||||
|
||||
DesktopCapturer::~DesktopCapturer() {
|
||||
}
|
||||
|
||||
const DesktopMediaList::Source& DesktopCapturer::GetSource(int index) {
|
||||
return media_list_->GetSource(index);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnSourceAdded(int index) {
|
||||
Emit("source-added", index);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnSourceRemoved(int index) {
|
||||
Emit("source-removed", index);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnSourceMoved(int old_index, int new_index) {
|
||||
Emit("source-moved", old_index, new_index);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnSourceNameChanged(int index) {
|
||||
Emit("source-name-changed", index);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnSourceThumbnailChanged(int index) {
|
||||
Emit("source-thumbnail-changed", index);
|
||||
}
|
||||
|
||||
mate::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("getSource", &DesktopCapturer::GetSource);
|
||||
}
|
||||
|
||||
void SetWrapDesktopCapturer(const WrapDesktopCapturerCallback& callback) {
|
||||
g_wrap_desktop_capturer = callback;
|
||||
}
|
||||
|
||||
void ClearWrapDesktopCapturer() {
|
||||
g_wrap_desktop_capturer.Reset();
|
||||
}
|
||||
|
||||
// static
|
||||
mate::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate,
|
||||
bool show_screens, bool show_windows) {
|
||||
auto handle = mate::CreateHandle(isolate,
|
||||
new DesktopCapturer(show_screens, show_windows));
|
||||
g_wrap_desktop_capturer.Run(handle.ToV8());
|
||||
return handle;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("_setWrapDesktopCapturer", &atom::api::SetWrapDesktopCapturer);
|
||||
dict.SetMethod("_clearWrapDesktopCapturer",
|
||||
&atom::api::ClearWrapDesktopCapturer);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_desktop_capturer, Initialize);
|
51
atom/browser/api/atom_api_desktop_capturer.h
Normal file
51
atom/browser/api/atom_api_desktop_capturer.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_
|
||||
#define ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "chrome/browser/media/desktop_media_list_observer.h"
|
||||
#include "chrome/browser/media/native_desktop_media_list.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class DesktopCapturer: public mate::EventEmitter,
|
||||
public DesktopMediaListObserver {
|
||||
public:
|
||||
static mate::Handle<DesktopCapturer> Create(v8::Isolate* isolate,
|
||||
bool show_screens, bool show_windows);
|
||||
|
||||
const DesktopMediaList::Source& GetSource(int index);
|
||||
|
||||
protected:
|
||||
DesktopCapturer(bool show_screens, bool show_windows);
|
||||
~DesktopCapturer();
|
||||
|
||||
// DesktopMediaListObserver overrides.
|
||||
void OnSourceAdded(int index) override;
|
||||
void OnSourceRemoved(int index) override;
|
||||
void OnSourceMoved(int old_index, int new_index) override;
|
||||
void OnSourceNameChanged(int index) override;
|
||||
void OnSourceThumbnailChanged(int index) override;
|
||||
|
||||
private:
|
||||
// mate::Wrappable:
|
||||
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override;
|
||||
|
||||
scoped_ptr<DesktopMediaList> media_list_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DesktopCapturer);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_
|
|
@ -56,6 +56,21 @@ Screen::~Screen() {
|
|||
screen_->RemoveObserver(this);
|
||||
}
|
||||
|
||||
mate::Handle<atom::api::DesktopCapturer> Screen::GetDesktopCapturer(
|
||||
const std::vector<std::string>& sources) {
|
||||
bool show_screens = false;
|
||||
bool show_windows = false;
|
||||
for (const auto& source_type : sources) {
|
||||
if (source_type == "screen") {
|
||||
show_screens = true;
|
||||
} else if (source_type == "window") {
|
||||
show_windows = true;
|
||||
}
|
||||
}
|
||||
|
||||
return DesktopCapturer::Create(isolate(), show_screens, show_windows);
|
||||
}
|
||||
|
||||
gfx::Point Screen::GetCursorScreenPoint() {
|
||||
return screen_->GetCursorScreenPoint();
|
||||
}
|
||||
|
@ -107,7 +122,8 @@ mate::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder(
|
|||
.SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay)
|
||||
.SetMethod("getAllDisplays", &Screen::GetAllDisplays)
|
||||
.SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint)
|
||||
.SetMethod("getDisplayMatching", &Screen::GetDisplayMatching);
|
||||
.SetMethod("getDisplayMatching", &Screen::GetDisplayMatching)
|
||||
.SetMethod("getDesktopCapturer", &Screen::GetDesktopCapturer);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
#define ATOM_BROWSER_API_ATOM_API_SCREEN_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/api/atom_api_desktop_capturer.h"
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "ui/gfx/display_observer.h"
|
||||
|
@ -36,6 +38,9 @@ class Screen : public mate::EventEmitter,
|
|||
gfx::Display GetDisplayNearestPoint(const gfx::Point& point);
|
||||
gfx::Display GetDisplayMatching(const gfx::Rect& match_rect);
|
||||
|
||||
mate::Handle<DesktopCapturer> GetDesktopCapturer(
|
||||
const std::vector<std::string>& sources);
|
||||
|
||||
// gfx::DisplayObserver:
|
||||
void OnDisplayAdded(const gfx::Display& new_display) override;
|
||||
void OnDisplayRemoved(const gfx::Display& old_display) override;
|
||||
|
|
|
@ -3,17 +3,18 @@ EventEmitter = require('events').EventEmitter
|
|||
bindings = process.atomBinding 'app'
|
||||
sessionBindings = process.atomBinding 'session'
|
||||
downloadItemBindings = process.atomBinding 'download_item'
|
||||
desktopCapturerBindings = process.atomBinding 'desktop_capturer'
|
||||
|
||||
app = bindings.app
|
||||
app.__proto__ = EventEmitter.prototype
|
||||
|
||||
wrapSession = (session) ->
|
||||
# session is an Event Emitter.
|
||||
session.__proto__ = EventEmitter.prototype
|
||||
wrapToEventListener = (item) ->
|
||||
# item is an Event Emitter.
|
||||
item.__proto__ = EventEmitter.prototype
|
||||
|
||||
wrapDownloadItem = (download_item) ->
|
||||
# download_item is an Event Emitter.
|
||||
download_item.__proto__ = EventEmitter.prototype
|
||||
wrapToEventListener download_item
|
||||
# Be compatible with old APIs.
|
||||
download_item.url = download_item.getUrl()
|
||||
download_item.filename = download_item.getFilename()
|
||||
|
@ -58,11 +59,14 @@ app.resolveProxy = -> @defaultSession.resolveProxy.apply @defaultSession, argume
|
|||
app.on 'activate', (event, hasVisibleWindows) -> @emit 'activate-with-no-open-windows' if not hasVisibleWindows
|
||||
|
||||
# Session wrapper.
|
||||
sessionBindings._setWrapSession wrapSession
|
||||
sessionBindings._setWrapSession wrapToEventListener
|
||||
process.once 'exit', sessionBindings._clearWrapSession
|
||||
|
||||
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
|
||||
process.once 'exit', downloadItemBindings._clearWrapDownloadItem
|
||||
|
||||
desktopCapturerBindings._setWrapDesktopCapturer wrapToEventListener
|
||||
process.once 'exit', desktopCapturerBindings._clearWrapDesktopCapturer
|
||||
|
||||
# Only one App object pemitted.
|
||||
module.exports = app
|
||||
|
|
|
@ -34,6 +34,7 @@ REFERENCE_MODULE(atom_browser_app);
|
|||
REFERENCE_MODULE(atom_browser_auto_updater);
|
||||
REFERENCE_MODULE(atom_browser_content_tracing);
|
||||
REFERENCE_MODULE(atom_browser_dialog);
|
||||
REFERENCE_MODULE(atom_browser_desktop_capturer);
|
||||
REFERENCE_MODULE(atom_browser_download_item);
|
||||
REFERENCE_MODULE(atom_browser_menu);
|
||||
REFERENCE_MODULE(atom_browser_power_monitor);
|
||||
|
|
60
chromium_src/chrome/browser/media/desktop_media_list.h
Normal file
60
chromium_src/chrome/browser/media/desktop_media_list.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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/basictypes.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;
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_
|
|
@ -0,0 +1,22 @@
|
|||
// 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;
|
||||
|
||||
protected:
|
||||
virtual ~DesktopMediaListObserver() {}
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_
|
366
chromium_src/chrome/browser/media/native_desktop_media_list.cc
Normal file
366
chromium_src/chrome/browser/media/native_desktop_media_list.cc
Normal file
|
@ -0,0 +1,366 @@
|
|||
// 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>
|
||||
|
||||
#include "base/hash.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/sequenced_worker_pool.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_frame.h"
|
||||
#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
|
||||
#include "third_party/webrtc/modules/desktop_capture/window_capturer.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 GetFrameHash(webrtc::DesktopFrame* frame) {
|
||||
int data_size = frame->stride() * frame->size().height();
|
||||
return base::SuperFastHash(reinterpret_cast<char*>(frame->data()), data_size);
|
||||
}
|
||||
|
||||
gfx::ImageSkia ScaleDesktopFrame(scoped_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);
|
||||
result.lockPixels();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
result.unlockPixels();
|
||||
|
||||
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,
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
|
||||
scoped_ptr<webrtc::WindowCapturer> 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.
|
||||
webrtc::SharedMemory* CreateSharedMemory(size_t size) override;
|
||||
void OnCaptureCompleted(webrtc::DesktopFrame* frame) override;
|
||||
|
||||
base::WeakPtr<NativeDesktopMediaList> media_list_;
|
||||
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
|
||||
scoped_ptr<webrtc::WindowCapturer> window_capturer_;
|
||||
|
||||
scoped_ptr<webrtc::DesktopFrame> current_frame_;
|
||||
|
||||
ImageHashesMap image_hashes_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Worker);
|
||||
};
|
||||
|
||||
NativeDesktopMediaList::Worker::Worker(
|
||||
base::WeakPtr<NativeDesktopMediaList> media_list,
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
|
||||
scoped_ptr<webrtc::WindowCapturer> window_capturer)
|
||||
: media_list_(media_list),
|
||||
screen_capturer_(screen_capturer.Pass()),
|
||||
window_capturer_(window_capturer.Pass()) {
|
||||
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::ScreenCapturer::ScreenList screens;
|
||||
if (screen_capturer_->GetScreenList(&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::WindowCapturer::WindowList windows;
|
||||
if (window_capturer_->GetWindowList(&windows)) {
|
||||
for (webrtc::WindowCapturer::WindowList::iterator 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_->SelectScreen(source.id.id))
|
||||
continue;
|
||||
screen_capturer_->Capture(webrtc::DesktopRegion());
|
||||
break;
|
||||
|
||||
case DesktopMediaID::TYPE_WINDOW:
|
||||
if (!window_capturer_->SelectWindow(source.id.id))
|
||||
continue;
|
||||
window_capturer_->Capture(webrtc::DesktopRegion());
|
||||
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 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(current_frame_.Pass(), 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_));
|
||||
}
|
||||
|
||||
webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory(
|
||||
size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NativeDesktopMediaList::Worker::OnCaptureCompleted(
|
||||
webrtc::DesktopFrame* frame) {
|
||||
current_frame_.reset(frame);
|
||||
}
|
||||
|
||||
NativeDesktopMediaList::NativeDesktopMediaList(
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
|
||||
scoped_ptr<webrtc::WindowCapturer> window_capturer)
|
||||
: screen_capturer_(screen_capturer.Pass()),
|
||||
window_capturer_(window_capturer.Pass()),
|
||||
update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
|
||||
thumbnail_size_(100, 100),
|
||||
view_dialog_id_(-1),
|
||||
observer_(NULL),
|
||||
weak_factory_(this) {
|
||||
base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
|
||||
capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
|
||||
worker_pool->GetSequenceToken());
|
||||
}
|
||||
|
||||
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(),
|
||||
screen_capturer_.Pass(), window_capturer_.Pass()));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
int NativeDesktopMediaList::GetSourceCount() const {
|
||||
return sources_.size();
|
||||
}
|
||||
|
||||
const DesktopMediaList::Source& NativeDesktopMediaList::GetSource(
|
||||
int index) const {
|
||||
return sources_[index];
|
||||
}
|
||||
|
||||
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()) {
|
||||
sources_.erase(sources_.begin() + i);
|
||||
observer_->OnSourceRemoved(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() {
|
||||
BrowserThread::PostDelayedTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&NativeDesktopMediaList::Refresh,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
update_period_);
|
||||
}
|
100
chromium_src/chrome/browser/media/native_desktop_media_list.h
Normal file
100
chromium_src/chrome/browser/media/native_desktop_media_list.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
// 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/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.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 ScreenCapturer;
|
||||
class WindowCapturer;
|
||||
}
|
||||
|
||||
// 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(
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
|
||||
scoped_ptr<webrtc::WindowCapturer> 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;
|
||||
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.
|
||||
scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
|
||||
scoped_ptr<webrtc::WindowCapturer> 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.
|
||||
scoped_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_
|
|
@ -71,6 +71,8 @@
|
|||
'atom/browser/api/atom_api_content_tracing.cc',
|
||||
'atom/browser/api/atom_api_cookies.cc',
|
||||
'atom/browser/api/atom_api_cookies.h',
|
||||
'atom/browser/api/atom_api_desktop_capturer.cc',
|
||||
'atom/browser/api/atom_api_desktop_capturer.h',
|
||||
'atom/browser/api/atom_api_download_item.cc',
|
||||
'atom/browser/api/atom_api_download_item.h',
|
||||
'atom/browser/api/atom_api_dialog.cc',
|
||||
|
@ -349,6 +351,10 @@
|
|||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h',
|
||||
'chromium_src/chrome/browser/media/desktop_media_list.h',
|
||||
'chromium_src/chrome/browser/media/desktop_media_list_observer.h',
|
||||
'chromium_src/chrome/browser/media/native_desktop_media_list.cc',
|
||||
'chromium_src/chrome/browser/media/native_desktop_media_list.h',
|
||||
'chromium_src/chrome/browser/printing/print_job.cc',
|
||||
'chromium_src/chrome/browser/printing/print_job.h',
|
||||
'chromium_src/chrome/browser/printing/print_job_manager.cc',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue