Add a display_id parameter to the desktopCapturer API. (#12417)

* Add a screen_api_id parameter to the desktopCapturer API.

When using the DirectX capturer on Windows, there was previously no way
to associate desktopCapturer/getUserMedia and electron.screen API
screens. This new parameter provides the association.

* Fix non-Windows build.

* Fix Mac.

* Fix Mac harder.

* JS lint

* clang-format C++ code.

* IWYU

* display_id, Linux comment, better test

* lint

* Fix tests on Linux.

* Add display_id documentation.
This commit is contained in:
Andrew MacDonald 2018-04-08 22:43:35 -07:00 коммит произвёл Cheng Zhao
Родитель 89f2eb1023
Коммит 6bfb122cd1
6 изменённых файлов: 119 добавлений и 14 удалений

Просмотреть файл

@ -4,32 +4,41 @@
#include "atom/browser/api/atom_api_desktop_capturer.h"
#include <vector>
using base::PlatformThreadRef;
#include "atom/common/api/atom_api_native_image.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/media/desktop_media_list.h"
#include "content/public/browser/desktop_capture.h"
#include "native_mate/dictionary.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#if defined(OS_WIN)
#include "third_party/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h"
#include "ui/display/win/display_info.h"
#endif // defined(OS_WIN)
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<DesktopMediaList::Source> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const DesktopMediaList::Source& source) {
template <>
struct Converter<atom::api::DesktopCapturer::Source> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const atom::api::DesktopCapturer::Source& source) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
content::DesktopMediaID id = source.id;
dict.Set("name", base::UTF16ToUTF8(source.name));
content::DesktopMediaID id = source.media_list_source.id;
dict.Set("name", base::UTF16ToUTF8(source.media_list_source.name));
dict.Set("id", id.ToString());
dict.Set(
"thumbnail",
atom::api::NativeImage::Create(isolate, gfx::Image(source.thumbnail)));
dict.Set("thumbnail",
atom::api::NativeImage::Create(
isolate, gfx::Image(source.media_list_source.thumbnail)));
dict.Set("display_id", source.display_id);
return ConvertToV8(isolate, dict);
}
};
@ -52,6 +61,9 @@ void DesktopCapturer::StartHandling(bool capture_window,
const gfx::Size& thumbnail_size) {
webrtc::DesktopCaptureOptions options =
content::CreateDesktopCaptureOptions();
#if defined(OS_WIN)
using_directx_capturer_ = options.allow_directx_capturer();
#endif // defined(OS_WIN)
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer(
capture_screen ? webrtc::DesktopCapturer::CreateScreenCapturer(options)
@ -82,7 +94,53 @@ void DesktopCapturer::OnSourceThumbnailChanged(int index) {
}
bool DesktopCapturer::OnRefreshFinished() {
Emit("finished", media_list_->GetSources());
const auto media_list_sources = media_list_->GetSources();
std::vector<DesktopCapturer::Source> sources;
for (const auto& media_list_source : media_list_sources) {
sources.emplace_back(
DesktopCapturer::Source{media_list_source, std::string()});
}
#if defined(OS_WIN)
// Gather the same unique screen IDs used by the electron.screen API in order
// to provide an association between it and desktopCapturer/getUserMedia.
// This is only required when using the DirectX capturer, otherwise the IDs
// across the APIs already match.
if (using_directx_capturer_) {
std::vector<std::string> device_names;
// Crucially, this list of device names will be in the same order as
// |media_list_sources|.
webrtc::DxgiDuplicatorController::Instance()->GetDeviceNames(&device_names);
int device_name_index = 0;
for (auto& source : sources) {
if (source.media_list_source.id.type ==
content::DesktopMediaID::TYPE_SCREEN) {
const auto& device_name = device_names[device_name_index++];
std::wstring wide_device_name;
base::UTF8ToWide(device_name.c_str(), device_name.size(),
&wide_device_name);
const int64_t device_id =
display::win::DisplayInfo::DeviceIdFromDeviceName(
wide_device_name.c_str());
source.display_id = base::Int64ToString(device_id);
}
}
}
#elif defined(OS_MACOSX)
// On Mac, the IDs across the APIs match.
for (auto& source : sources) {
if (source.media_list_source.id.type ==
content::DesktopMediaID::TYPE_SCREEN) {
source.display_id =
base::Int64ToString(source.media_list_source.id.id);
}
}
#endif // defined(OS_WIN)
// TODO(ajmacd): Add Linux support. The IDs across APIs differ but Chrome only
// supports capturing the entire desktop on Linux. Revisit this if individual
// screen support is added.
Emit("finished", sources);
return false;
}

Просмотреть файл

@ -5,6 +5,8 @@
#ifndef ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_
#define ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_
#include <string>
#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"
@ -17,6 +19,12 @@ namespace api {
class DesktopCapturer: public mate::EventEmitter<DesktopCapturer>,
public DesktopMediaListObserver {
public:
struct Source {
DesktopMediaList::Source media_list_source;
// Will be an empty string if not available.
std::string display_id;
};
static mate::Handle<DesktopCapturer> Create(v8::Isolate* isolate);
static void BuildPrototype(v8::Isolate* isolate,
@ -40,6 +48,9 @@ class DesktopCapturer: public mate::EventEmitter<DesktopCapturer>,
private:
std::unique_ptr<DesktopMediaList> media_list_;
#if defined(OS_WIN)
bool using_directx_capturer_;
#endif // defined(OS_WIN)
DISALLOW_COPY_AND_ASSIGN(DesktopCapturer);
};

Просмотреть файл

@ -12,3 +12,8 @@
`thumbnailSize` specified in the `options` passed to
`desktopCapturer.getSources`. The actual size depends on the scale of the
screen or window.
* `display_id` String - A unique identifier that will correspond to the `id` of
the matching [Display](display.md) returned by the [Screen API](../screen.md).
On some platforms, this is equivalent to the `XX` portion of the `id` field
above and on others it will differ. It will be an empty string if not
available.

Просмотреть файл

@ -43,7 +43,8 @@ desktopCapturer.emit = (event, name, sources) => {
return {
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL()
thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id
}
})

Просмотреть файл

@ -35,7 +35,8 @@ exports.getSources = function (options, callback) {
results.push({
id: source.id,
name: source.name,
thumbnail: nativeImage.createFromDataURL(source.thumbnail)
thumbnail: nativeImage.createFromDataURL(source.thumbnail),
display_id: source.display_id
})
})
return results

Просмотреть файл

@ -1,5 +1,5 @@
const assert = require('assert')
const {desktopCapturer, remote} = require('electron')
const {desktopCapturer, remote, screen} = require('electron')
const isCI = remote.getGlobal('isCi')
@ -40,7 +40,7 @@ describe('desktopCapturer', () => {
desktopCapturer.getSources({types: ['window', 'screen']}, callback)
})
it('responds to subsequest calls of different options', (done) => {
it('responds to subsequent calls of different options', (done) => {
let callCount = 0
const callback = (error, sources) => {
callCount++
@ -51,4 +51,33 @@ describe('desktopCapturer', () => {
desktopCapturer.getSources({types: ['window']}, callback)
desktopCapturer.getSources({types: ['screen']}, callback)
})
it('returns an empty display_id for window sources on Windows and Mac', (done) => {
// Linux doesn't return any window sources.
if (process.platform !== 'win32' && process.platform !== 'darwin') {
return done()
}
desktopCapturer.getSources({types: ['window']}, (error, sources) => {
assert.equal(error, null)
assert.notEqual(sources.length, 0)
sources.forEach((source) => { assert.equal(source.display_id.length, 0) })
done()
})
})
it('returns display_ids matching the Screen API on Windows and Mac', (done) => {
if (process.platform !== 'win32' && process.platform !== 'darwin') {
return done()
}
const displays = screen.getAllDisplays()
desktopCapturer.getSources({types: ['screen']}, (error, sources) => {
assert.equal(error, null)
assert.notEqual(sources.length, 0)
assert.equal(sources.length, displays.length)
for (let i = 0; i < sources.length; i++) {
assert.equal(sources[i].display_id, displays[i].id)
}
done()
})
})
})