Merge pull request #3727 from atom/desktop-capture-api-continue
Merge the desktopCapturer API
This commit is contained in:
commit
5e9aca4524
20 changed files with 894 additions and 4 deletions
5
atom.gyp
5
atom.gyp
|
@ -256,6 +256,10 @@
|
||||||
'vendor/node/deps/cares/include',
|
'vendor/node/deps/cares/include',
|
||||||
# The `third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h` is using `platform/PlatformExport.h`.
|
# The `third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h` is using `platform/PlatformExport.h`.
|
||||||
'<(libchromiumcontent_src_dir)/third_party/WebKit/Source',
|
'<(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': {
|
'direct_dependent_settings': {
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
|
@ -282,6 +286,7 @@
|
||||||
'-lcomctl32.lib',
|
'-lcomctl32.lib',
|
||||||
'-lcomdlg32.lib',
|
'-lcomdlg32.lib',
|
||||||
'-lwininet.lib',
|
'-lwininet.lib',
|
||||||
|
'-lwinmm.lib',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
|
|
120
atom/browser/api/atom_api_desktop_capturer.cc
Normal file
120
atom/browser/api/atom_api_desktop_capturer.cc
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// 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/node_includes.h"
|
||||||
|
#include "atom/common/native_mate_converters/gfx_converter.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "chrome/browser/media/desktop_media_list.h"
|
||||||
|
#include "native_mate/dictionary.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 {
|
||||||
|
|
||||||
|
DesktopCapturer::DesktopCapturer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopCapturer::~DesktopCapturer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::StartHandling(bool capture_window,
|
||||||
|
bool capture_screen,
|
||||||
|
const gfx::Size& thumbnail_size) {
|
||||||
|
webrtc::DesktopCaptureOptions options =
|
||||||
|
webrtc::DesktopCaptureOptions::CreateDefault();
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
// On windows, desktop effects (e.g. Aero) will be disabled when the Desktop
|
||||||
|
// capture API is active by default.
|
||||||
|
// We keep the desktop effects in most times. Howerver, the screen still
|
||||||
|
// fickers when the API is capturing the window due to limitation of current
|
||||||
|
// implemetation. This is a known and wontFix issue in webrtc (see:
|
||||||
|
// http://code.google.com/p/webrtc/issues/detail?id=3373)
|
||||||
|
options.set_disable_effects(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
|
||||||
|
capture_screen ? webrtc::ScreenCapturer::Create(options) : nullptr);
|
||||||
|
scoped_ptr<webrtc::WindowCapturer> window_capturer(
|
||||||
|
capture_window ? webrtc::WindowCapturer::Create(options) : nullptr);
|
||||||
|
media_list_.reset(new NativeDesktopMediaList(screen_capturer.Pass(),
|
||||||
|
window_capturer.Pass()));
|
||||||
|
|
||||||
|
media_list_->SetThumbnailSize(thumbnail_size);
|
||||||
|
media_list_->StartUpdating(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::OnSourceAdded(int index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::OnSourceRemoved(int index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::OnSourceMoved(int old_index, int new_index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::OnSourceNameChanged(int index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopCapturer::OnSourceThumbnailChanged(int index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DesktopCapturer::OnRefreshFinished() {
|
||||||
|
Emit("finished", media_list_->GetSources());
|
||||||
|
media_list_.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mate::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
|
||||||
|
v8::Isolate* isolate) {
|
||||||
|
return mate::ObjectTemplateBuilder(isolate)
|
||||||
|
.SetMethod("startHandling", &DesktopCapturer::StartHandling);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
mate::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
|
||||||
|
return mate::CreateHandle(isolate, new DesktopCapturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // 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.Set("desktopCapturer", atom::api::DesktopCapturer::Create(isolate));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_desktop_capturer, Initialize);
|
52
atom/browser/api/atom_api_desktop_capturer.h
Normal file
52
atom/browser/api/atom_api_desktop_capturer.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// 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_H_
|
||||||
|
#define ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_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);
|
||||||
|
|
||||||
|
void StartHandling(bool capture_window,
|
||||||
|
bool capture_screen,
|
||||||
|
const gfx::Size& thumbnail_size);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DesktopCapturer();
|
||||||
|
~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;
|
||||||
|
bool OnRefreshFinished() 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_H_
|
37
atom/browser/lib/desktop-capturer.coffee
Normal file
37
atom/browser/lib/desktop-capturer.coffee
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{ipcMain} = require 'electron'
|
||||||
|
{desktopCapturer} = process.atomBinding 'desktop_capturer'
|
||||||
|
|
||||||
|
deepEqual = (opt1, opt2) ->
|
||||||
|
return JSON.stringify(opt1) is JSON.stringify(opt2)
|
||||||
|
|
||||||
|
# A queue for holding all requests from renderer process.
|
||||||
|
requestsQueue = []
|
||||||
|
|
||||||
|
ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) ->
|
||||||
|
request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender
|
||||||
|
requestsQueue.push request
|
||||||
|
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1
|
||||||
|
# If the WebContents is destroyed before receiving result, just remove the
|
||||||
|
# reference from requestsQueue to make the module not send the result to it.
|
||||||
|
event.sender.once 'destroyed', ->
|
||||||
|
request.webContents = null
|
||||||
|
|
||||||
|
desktopCapturer.emit = (event, name, sources) ->
|
||||||
|
# Receiving sources result from main process, now send them back to renderer.
|
||||||
|
handledRequest = requestsQueue.shift 0
|
||||||
|
result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources)
|
||||||
|
handledRequest.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{handledRequest.id}", result
|
||||||
|
|
||||||
|
# Check the queue to see whether there is other same request. If has, handle
|
||||||
|
# it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
||||||
|
unhandledRequestsQueue = []
|
||||||
|
for request in requestsQueue
|
||||||
|
if deepEqual handledRequest.options, request.options
|
||||||
|
request.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{request.id}", errorMessage, result
|
||||||
|
else
|
||||||
|
unhandledRequestsQueue.push request
|
||||||
|
requestsQueue = unhandledRequestsQueue
|
||||||
|
# If the requestsQueue is not empty, start a new request handling.
|
||||||
|
if requestsQueue.length > 0
|
||||||
|
{captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
|
||||||
|
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize
|
|
@ -108,6 +108,9 @@ app.setAppPath packagePath
|
||||||
# Load the chrome extension support.
|
# Load the chrome extension support.
|
||||||
require './chrome-extension'
|
require './chrome-extension'
|
||||||
|
|
||||||
|
# Load internal desktop-capturer module.
|
||||||
|
require './desktop-capturer'
|
||||||
|
|
||||||
# Set main startup script of the app.
|
# Set main startup script of the app.
|
||||||
mainStartupScript = packageJson.main or 'index.js'
|
mainStartupScript = packageJson.main or 'index.js'
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ REFERENCE_MODULE(atom_browser_app);
|
||||||
REFERENCE_MODULE(atom_browser_auto_updater);
|
REFERENCE_MODULE(atom_browser_auto_updater);
|
||||||
REFERENCE_MODULE(atom_browser_content_tracing);
|
REFERENCE_MODULE(atom_browser_content_tracing);
|
||||||
REFERENCE_MODULE(atom_browser_dialog);
|
REFERENCE_MODULE(atom_browser_dialog);
|
||||||
|
REFERENCE_MODULE(atom_browser_desktop_capturer);
|
||||||
REFERENCE_MODULE(atom_browser_download_item);
|
REFERENCE_MODULE(atom_browser_download_item);
|
||||||
REFERENCE_MODULE(atom_browser_menu);
|
REFERENCE_MODULE(atom_browser_menu);
|
||||||
REFERENCE_MODULE(atom_browser_power_monitor);
|
REFERENCE_MODULE(atom_browser_power_monitor);
|
||||||
|
|
20
atom/renderer/api/lib/desktop-capturer.coffee
Normal file
20
atom/renderer/api/lib/desktop-capturer.coffee
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{ipcRenderer, nativeImage} = require 'electron'
|
||||||
|
|
||||||
|
nextId = 0
|
||||||
|
getNextId = -> ++nextId
|
||||||
|
|
||||||
|
# |options.type| can not be empty and has to include 'window' or 'screen'.
|
||||||
|
isValid = (options) ->
|
||||||
|
return options?.types? and Array.isArray options.types
|
||||||
|
|
||||||
|
exports.getSources = (options, callback) ->
|
||||||
|
return callback new Error('Invalid options') unless isValid options
|
||||||
|
|
||||||
|
captureWindow = 'window' in options.types
|
||||||
|
captureScreen = 'screen' in options.types
|
||||||
|
options.thumbnailSize ?= width: 150, height: 150
|
||||||
|
|
||||||
|
id = getNextId()
|
||||||
|
ipcRenderer.send 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id
|
||||||
|
ipcRenderer.once "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{id}", (event, sources) ->
|
||||||
|
callback null, ({id: source.id, name: source.name, thumbnail: nativeImage.createFromDataURL source.thumbnail} for source in sources)
|
|
@ -3,6 +3,9 @@ module.exports = require '../../../../common/api/lib/exports/electron'
|
||||||
|
|
||||||
Object.defineProperties module.exports,
|
Object.defineProperties module.exports,
|
||||||
# Renderer side modules, please sort with alphabet order.
|
# Renderer side modules, please sort with alphabet order.
|
||||||
|
desktopCapturer:
|
||||||
|
enumerable: true
|
||||||
|
get: -> require '../desktop-capturer'
|
||||||
ipcRenderer:
|
ipcRenderer:
|
||||||
enumerable: true
|
enumerable: true
|
||||||
get: -> require '../ipc-renderer'
|
get: -> require '../ipc-renderer'
|
||||||
|
|
62
chromium_src/chrome/browser/media/desktop_media_list.h
Normal file
62
chromium_src/chrome/browser/media/desktop_media_list.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// 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/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_
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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;
|
||||||
|
virtual bool OnRefreshFinished() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~DesktopMediaListObserver() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_
|
375
chromium_src/chrome/browser/media/native_desktop_media_list.cc
Normal file
375
chromium_src/chrome/browser/media/native_desktop_media_list.cc
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
// 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/string_number_conversions.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];
|
||||||
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
}
|
101
chromium_src/chrome/browser/media/native_desktop_media_list.h
Normal file
101
chromium_src/chrome/browser/media/native_desktop_media_list.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// 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;
|
||||||
|
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.
|
||||||
|
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_
|
|
@ -58,6 +58,7 @@ select the tag that matches your version.
|
||||||
|
|
||||||
### Modules for the Renderer Process (Web Page):
|
### Modules for the Renderer Process (Web Page):
|
||||||
|
|
||||||
|
* [desktopCapturer](api/desktop-capturer.md)
|
||||||
* [ipcRenderer](api/ipc-renderer.md)
|
* [ipcRenderer](api/ipc-renderer.md)
|
||||||
* [remote](api/remote.md)
|
* [remote](api/remote.md)
|
||||||
* [webFrame](api/web-frame.md)
|
* [webFrame](api/web-frame.md)
|
||||||
|
|
69
docs/api/desktop-capturer.md
Normal file
69
docs/api/desktop-capturer.md
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# desktopCapturer
|
||||||
|
|
||||||
|
The `desktopCapturer` module can be used to get available sources that can be
|
||||||
|
used to be captured with `getUserMedia`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// In the renderer process.
|
||||||
|
var desktopCapturer = require('electron').desktopCapturer;
|
||||||
|
|
||||||
|
desktopCapturer.getSources({types: ['window', 'screen']}, function(error, sources) {
|
||||||
|
if (error) throw error;
|
||||||
|
for (var i = 0; i < sources.length; ++i) {
|
||||||
|
if (sources[i].name == "Electron") {
|
||||||
|
navigator.webkitGetUserMedia({
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
mandatory: {
|
||||||
|
chromeMediaSource: 'desktop',
|
||||||
|
chromeMediaSourceId: sources[i].id,
|
||||||
|
minWidth: 1280,
|
||||||
|
maxWidth: 1280,
|
||||||
|
minHeight: 720,
|
||||||
|
maxHeight: 720
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, gotStream, getUserMediaError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function gotStream(stream) {
|
||||||
|
document.querySelector('video').src = URL.createObjectURL(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserMediaError(e) {
|
||||||
|
console.log('getUserMediaError');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
The `desktopCapturer` module has the following methods:
|
||||||
|
|
||||||
|
### `desktopCapturer.getSources(options, callback)`
|
||||||
|
|
||||||
|
* `options` Object
|
||||||
|
* `types` Array - An array of String that lists the types of desktop sources
|
||||||
|
to be captured, available types are `screen` and `window`.
|
||||||
|
* `thumbnailSize` Object (optional) - The suggested size that thumbnail should
|
||||||
|
be scaled, it is `{width: 150, height: 150}` by default.
|
||||||
|
* `callback` Function
|
||||||
|
|
||||||
|
Starts a request to get all desktop sources, `callback` will be called with
|
||||||
|
`callback(error, sources)` when the request is completed.
|
||||||
|
|
||||||
|
The `sources` is an array of `Source` objects, each `Source` represents a
|
||||||
|
captured screen or individual window, and has following properties:
|
||||||
|
* `id` String - The id of the captured window or screen used in
|
||||||
|
`navigator.webkitGetUserMedia`. The format looks like `window:XX` or
|
||||||
|
`screen:XX` where `XX` is a random generated number.
|
||||||
|
* `name` String - The described name of the capturing screen or window. If the
|
||||||
|
source is a screen, the name will be `Entire Screen` or `Screen <index>`; if
|
||||||
|
it is a window, the name will be the window's title.
|
||||||
|
* `thumbnail` [NativeImage](NativeImage.md) - A thumbnail image.
|
||||||
|
|
||||||
|
**Note:** There is no guarantee that the size of `source.thumbnail` is always
|
||||||
|
the same as the `thumnbailSize` in `options`. It also depends on the scale of
|
||||||
|
the screen or window.
|
|
@ -31,6 +31,7 @@
|
||||||
'atom/browser/api/lib/tray.coffee',
|
'atom/browser/api/lib/tray.coffee',
|
||||||
'atom/browser/api/lib/web-contents.coffee',
|
'atom/browser/api/lib/web-contents.coffee',
|
||||||
'atom/browser/lib/chrome-extension.coffee',
|
'atom/browser/lib/chrome-extension.coffee',
|
||||||
|
'atom/browser/lib/desktop-capturer.coffee',
|
||||||
'atom/browser/lib/guest-view-manager.coffee',
|
'atom/browser/lib/guest-view-manager.coffee',
|
||||||
'atom/browser/lib/guest-window-manager.coffee',
|
'atom/browser/lib/guest-window-manager.coffee',
|
||||||
'atom/browser/lib/init.coffee',
|
'atom/browser/lib/init.coffee',
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
'atom/renderer/lib/web-view/web-view.coffee',
|
'atom/renderer/lib/web-view/web-view.coffee',
|
||||||
'atom/renderer/lib/web-view/web-view-attributes.coffee',
|
'atom/renderer/lib/web-view/web-view-attributes.coffee',
|
||||||
'atom/renderer/lib/web-view/web-view-constants.coffee',
|
'atom/renderer/lib/web-view/web-view-constants.coffee',
|
||||||
|
'atom/renderer/api/lib/desktop-capturer.coffee',
|
||||||
'atom/renderer/api/lib/exports/electron.coffee',
|
'atom/renderer/api/lib/exports/electron.coffee',
|
||||||
'atom/renderer/api/lib/ipc.coffee',
|
'atom/renderer/api/lib/ipc.coffee',
|
||||||
'atom/renderer/api/lib/ipc-renderer.coffee',
|
'atom/renderer/api/lib/ipc-renderer.coffee',
|
||||||
|
@ -81,6 +83,8 @@
|
||||||
'atom/browser/api/atom_api_content_tracing.cc',
|
'atom/browser/api/atom_api_content_tracing.cc',
|
||||||
'atom/browser/api/atom_api_cookies.cc',
|
'atom/browser/api/atom_api_cookies.cc',
|
||||||
'atom/browser/api/atom_api_cookies.h',
|
'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.cc',
|
||||||
'atom/browser/api/atom_api_download_item.h',
|
'atom/browser/api/atom_api_download_item.h',
|
||||||
'atom/browser/api/atom_api_dialog.cc',
|
'atom/browser/api/atom_api_dialog.cc',
|
||||||
|
@ -374,6 +378,10 @@
|
||||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h',
|
'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.cc',
|
||||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h',
|
'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.cc',
|
||||||
'chromium_src/chrome/browser/printing/print_job.h',
|
'chromium_src/chrome/browser/printing/print_job.h',
|
||||||
'chromium_src/chrome/browser/printing/print_job_manager.cc',
|
'chromium_src/chrome/browser/printing/print_job_manager.cc',
|
||||||
|
|
|
@ -17,6 +17,7 @@ LINUX_DEPS = [
|
||||||
'libgtk2.0-dev',
|
'libgtk2.0-dev',
|
||||||
'libnotify-dev',
|
'libnotify-dev',
|
||||||
'libnss3-dev',
|
'libnss3-dev',
|
||||||
|
'libxtst-dev',
|
||||||
'gcc-multilib',
|
'gcc-multilib',
|
||||||
'g++-multilib',
|
'g++-multilib',
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,8 +7,8 @@ import sys
|
||||||
|
|
||||||
|
|
||||||
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
||||||
'http://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent'
|
||||||
LIBCHROMIUMCONTENT_COMMIT = '451ea93cc3090f7000f8f0daa4cb84e90ad6c842'
|
LIBCHROMIUMCONTENT_COMMIT = 'cfbe8ec7e14af4cabd1474386f54e197db1f7ac1'
|
||||||
|
|
||||||
PLATFORM = {
|
PLATFORM = {
|
||||||
'cygwin': 'win32',
|
'cygwin': 'win32',
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# Do NOT CHANGE this if you don't know what you're doing -- see
|
# Do NOT CHANGE this if you don't know what you're doing -- see
|
||||||
# https://code.google.com/p/chromium/wiki/UpdatingClang
|
# https://code.google.com/p/chromium/wiki/UpdatingClang
|
||||||
# Reverting problematic clang rolls is safe, though.
|
# Reverting problematic clang rolls is safe, though.
|
||||||
CLANG_REVISION=245965
|
CLANG_REVISION=247874
|
||||||
|
|
||||||
# This is incremented when pushing a new build of Clang at the same revision.
|
# This is incremented when pushing a new build of Clang at the same revision.
|
||||||
CLANG_SUB_REVISION=1
|
CLANG_SUB_REVISION=1
|
||||||
|
|
9
spec/api-desktop-capturer.coffee
Normal file
9
spec/api-desktop-capturer.coffee
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
assert = require 'assert'
|
||||||
|
{desktopCapturer} = require 'electron'
|
||||||
|
|
||||||
|
describe 'desktopCapturer', ->
|
||||||
|
it 'should returns something', (done) ->
|
||||||
|
desktopCapturer.getSources {types: ['window', 'screen']}, (error, sources) ->
|
||||||
|
assert.equal error, null
|
||||||
|
assert.notEqual sources.length, 0
|
||||||
|
done()
|
2
vendor/brightray
vendored
2
vendor/brightray
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 9b4d052d2af716c340034ed7815d9d758cd7804d
|
Subproject commit 814923b77d21261a9d1780bff0bd1beb55203843
|
Loading…
Add table
Reference in a new issue