Skip to content

Commit c396045

Browse files
committed
Merge pull request nwjs#3574 from redgecombe/screencap
Desktop capture monitor and desktop audio on Windows
2 parents c955de2 + 8b944e5 commit c396045

File tree

6 files changed

+310
-0
lines changed

6 files changed

+310
-0
lines changed

nw.gypi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@
207207
'src/api/event/event.cc',
208208
'src/api/screen/desktop_capture_api.h',
209209
'src/api/screen/desktop_capture_api.cc',
210+
'src/api/screen/desktop_capture_monitor.cc',
211+
'src/api/screen/desktop_capture_monitor.h',
210212
'<(DEPTH)/chrome/browser/media/desktop_media_list.h',
211213
'<(DEPTH)/chrome/browser/media/desktop_media_list_observer.h',
212214
'<(DEPTH)/chrome/browser/media/desktop_media_picker.h',

src/api/dispatcher_host.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "content/nw/src/api/menu/menu.h"
3535
#include "content/nw/src/api/menuitem/menuitem.h"
3636
#include "content/nw/src/api/screen/screen.h"
37+
#include "content/nw/src/api/screen/desktop_capture_monitor.h"
3738
#include "content/nw/src/api/shell/shell.h"
3839
#include "content/nw/src/api/shortcut/shortcut.h"
3940
#include "content/nw/src/api/tray/tray.h"
@@ -171,6 +172,8 @@ void DispatcherHost::OnAllocateObject(int object_id,
171172
objects_registry_.AddWithID(new Shortcut(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id);
172173
} else if (type == "Screen") {
173174
objects_registry_.AddWithID(new EventListener(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id);
175+
}else if (type == "DesktopCaptureMonitor") {
176+
objects_registry_.AddWithID(new DesktopCaptureMonitor(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id);
174177
} else {
175178
LOG(ERROR) << "Allocate an object of unknown type: " << type;
176179
objects_registry_.AddWithID(new Base(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id);
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright (c) 2012 Intel Corp
2+
// Copyright (c) 2012 The Chromium Authors
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
8+
// pies of the Software, and to permit persons to whom the Software is furnished
9+
// to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in al
12+
// l copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
15+
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
16+
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17+
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
18+
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
21+
#include "content/nw/src/api/screen/desktop_capture_monitor.h"
22+
23+
#include "base/values.h"
24+
#include "base/strings/utf_string_conversions.h"
25+
#include "base/strings/string16.h"
26+
#include "content/nw/src/api/dispatcher_host.h"
27+
#include "chrome/browser/media/desktop_streams_registry.h"
28+
#include "chrome/browser/media/media_capture_devices_dispatcher.h"
29+
30+
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
31+
#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
32+
#include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
33+
34+
#include "base/base64.h"
35+
#include "ui/gfx/codec/png_codec.h"
36+
#include "ui/gfx/image/image.h"
37+
#include "ui/gfx/image/image_skia.h"
38+
39+
#ifdef _WIN32
40+
#include <windows.h>
41+
#endif
42+
43+
namespace nwapi {
44+
45+
DesktopCaptureMonitor::DesktopCaptureMonitor(int id,
46+
const base::WeakPtr<DispatcherHost>& dispatcher_host,
47+
const base::DictionaryValue& option)
48+
: Base(id, dispatcher_host, option) {
49+
}
50+
51+
DesktopCaptureMonitor::~DesktopCaptureMonitor() {}
52+
53+
int DesktopCaptureMonitor::GetPrimaryMonitorIndex(){
54+
#ifdef _WIN32
55+
int count=0;
56+
for (int i = 0;; ++i) {
57+
DISPLAY_DEVICE device;
58+
device.cb = sizeof(device);
59+
BOOL ret = EnumDisplayDevices(NULL, i, &device, 0);
60+
if(!ret)
61+
break;
62+
if (device.StateFlags & DISPLAY_DEVICE_ACTIVE){
63+
if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE){
64+
return count;
65+
}
66+
count++;
67+
}
68+
}
69+
#endif
70+
return -1;
71+
}
72+
73+
void DesktopCaptureMonitor::CallSync(const std::string& method, const base::ListValue& arguments, base::ListValue* result){
74+
if (method == "start") {
75+
bool screens, windows;
76+
if (arguments.GetBoolean(0, &screens) && arguments.GetBoolean(1, &windows))
77+
Start(screens, windows);
78+
}else if (method == "stop") {
79+
Stop();
80+
}
81+
else {
82+
NOTREACHED() << "Invalid call to DesktopCapture method:" << method
83+
<< " arguments:" << arguments;
84+
}
85+
}
86+
87+
void DesktopCaptureMonitor::Start(bool screens, bool windows) {
88+
webrtc::DesktopCaptureOptions options = webrtc::DesktopCaptureOptions::CreateDefault();
89+
options.set_disable_effects(false);
90+
scoped_ptr<webrtc::ScreenCapturer> screen_capturer(screens ? webrtc::ScreenCapturer::Create(options) : NULL);
91+
scoped_ptr<webrtc::WindowCapturer> window_capturer(windows ? webrtc::WindowCapturer::Create(options) : NULL);
92+
93+
media_list_.reset(new NativeDesktopMediaList(screen_capturer.Pass(), window_capturer.Pass()));
94+
95+
media_list_->StartUpdating(this);
96+
}
97+
98+
void DesktopCaptureMonitor::Stop() {
99+
media_list_.reset();
100+
}
101+
102+
void DesktopCaptureMonitor::OnSourceAdded(int index){
103+
DesktopMediaList::Source src = media_list_->GetSource(index);
104+
105+
std::string type;
106+
if (src.id.type == content::DesktopMediaID::TYPE_AURA_WINDOW || src.id.type == content::DesktopMediaID::TYPE_WINDOW){
107+
type = "window";
108+
}
109+
else if (src.id.type == content::DesktopMediaID::TYPE_SCREEN){
110+
type = "screen";
111+
}
112+
else if (src.id.type == content::DesktopMediaID::TYPE_NONE){
113+
type = "none";
114+
}
115+
else{
116+
type = "unknown";
117+
}
118+
119+
base::ListValue param;
120+
param.AppendString(src.id.ToString());
121+
param.AppendString(src.name);
122+
param.AppendInteger(index);
123+
param.AppendString(type);
124+
if(src.id.type == content::DesktopMediaID::TYPE_SCREEN){
125+
param.AppendBoolean(GetPrimaryMonitorIndex()==index);
126+
}
127+
this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_added", param);
128+
}
129+
void DesktopCaptureMonitor::OnSourceRemoved(int index){
130+
base::ListValue param;
131+
param.AppendInteger(index); //pass by index here, because the information about which ID was at that index is lost before the removed callback is called. Its saved in the javascript though, so we can look it up there
132+
this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_removed", param);
133+
}
134+
void DesktopCaptureMonitor::OnSourceMoved(int old_index, int new_index){
135+
DesktopMediaList::Source src = media_list_->GetSource(new_index);
136+
base::ListValue param;
137+
param.AppendString(src.id.ToString());
138+
param.AppendInteger(new_index);
139+
param.AppendInteger(old_index);
140+
this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_moved", param);
141+
}
142+
void DesktopCaptureMonitor::OnSourceNameChanged(int index){
143+
DesktopMediaList::Source src = media_list_->GetSource(index);
144+
145+
base::ListValue param;
146+
param.AppendString(src.id.ToString());
147+
param.AppendString(src.name);
148+
this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_namechanged", param);
149+
}
150+
151+
void DesktopCaptureMonitor::OnSourceThumbnailChanged(int index){
152+
std::string base64;
153+
154+
DesktopMediaList::Source src = media_list_->GetSource(index);
155+
SkBitmap bitmap = src.thumbnail.GetRepresentation(1).sk_bitmap();
156+
SkAutoLockPixels lock_image(bitmap);
157+
std::vector<unsigned char> data;
158+
bool success = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data);
159+
if (success){
160+
base::StringPiece raw_str(reinterpret_cast<const char*>(&data[0]), data.size());
161+
base::Base64Encode(raw_str, &base64);
162+
}
163+
base::ListValue param;
164+
param.AppendString(src.id.ToString());
165+
param.AppendString(base64);
166+
this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_thumbnailchanged", param);
167+
}
168+
169+
} // namespace nwapi
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2012 Intel Corp
2+
// Copyright (c) 2012 The Chromium Authors
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
8+
// pies of the Software, and to permit persons to whom the Software is furnished
9+
// to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in al
12+
// l copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
15+
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
16+
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17+
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
18+
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
#ifndef CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_
21+
#define CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_
22+
#include "base/compiler_specific.h"
23+
#include "content/nw/src/api/base/base.h"
24+
#include "chrome/browser/media/desktop_media_list_observer.h"
25+
#include "chrome/browser/media/native_desktop_media_list.h"
26+
27+
namespace nwapi {
28+
class DesktopCaptureMonitor : public Base, public DesktopMediaListObserver {
29+
public:
30+
DesktopCaptureMonitor(int id,
31+
const base::WeakPtr<DispatcherHost>& dispatcher_host,
32+
const base::DictionaryValue& option);
33+
virtual ~DesktopCaptureMonitor() override;
34+
virtual void CallSync(const std::string& method, const base::ListValue& arguments, base::ListValue* result) override;
35+
virtual void OnSourceAdded(int index);
36+
virtual void OnSourceRemoved(int index);
37+
virtual void OnSourceMoved(int old_index, int new_index);
38+
virtual void OnSourceNameChanged(int index);
39+
virtual void OnSourceThumbnailChanged(int index);
40+
private:
41+
DesktopCaptureMonitor();
42+
DISALLOW_COPY_AND_ASSIGN(DesktopCaptureMonitor);
43+
int GetPrimaryMonitorIndex();
44+
void Start(bool screens, bool windows);
45+
void Stop();
46+
47+
scoped_ptr<DesktopMediaList> media_list_;
48+
};
49+
} // namespace api
50+
#endif // CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_

src/api/screen/screen.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,91 @@
1818
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1919
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020

21+
function DesktopCaptureMonitor() {
22+
nw.allocateObject(this, {});
23+
this.sources = new Array();
24+
this.started = false;
25+
}
26+
require('util').inherits(DesktopCaptureMonitor, exports.Base);
27+
28+
29+
DesktopCaptureMonitor.prototype.start = function (screens, windows) {
30+
if (this.started)
31+
return false;
32+
this.started = true;
33+
nw.callObjectMethodSync(this, 'start', [screens, windows]);
34+
return true;
35+
}
36+
37+
DesktopCaptureMonitor.prototype.stop = function () {
38+
if (!this.started)
39+
return false;
40+
nw.callObjectMethodSync(this, 'stop', []);
41+
this.started = false;
42+
this.sources = new Array();
43+
return true;
44+
}
45+
46+
DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_added', function (id, name, order, type, primaryindex) {
47+
if(this.sources.indexOf(id)!=-1)
48+
{
49+
//TODO: Find out what this event comes twice on some platforms
50+
return;
51+
}
52+
this.sources.splice(order, 0, id);
53+
this.emit("added", id, name, order, type, primaryindex);
54+
for (var i = order + 1; i <= this.sources.length - 1; i++) {
55+
this.emit("orderchanged", this.sources[i], i, i - 1);
56+
}
57+
});
58+
59+
60+
DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_removed', function (index) {
61+
var id = this.sources[index];
62+
if (index != -1) {
63+
this.sources.splice(index, 1);
64+
this.emit("removed", id);
65+
for (var i = index; i <= this.sources.length - 1; i++) {
66+
this.emit("orderchanged", this.sources[i], i, i + 1);
67+
}
68+
}
69+
});
70+
71+
DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_moved', function (id, new_index, old_index) {
72+
var temp = this.sources[old_index];
73+
this.sources.splice(old_index, 1);
74+
this.sources.splice(new_index, 0, temp);
75+
this.emit("orderchanged", temp, new_index, old_index);
76+
for (var i = new_index; i < old_index; i++)
77+
this.emit("orderchanged", this.sources[i + 1], i + 1, i);
78+
});
79+
80+
DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_namechanged', function (id, name) {
81+
this.emit("namechanged", id, name);
82+
});
83+
84+
DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_thumbnailchanged', function (id, thumbnail) {
85+
this.emit("thumbnailchanged", id, thumbnail);
86+
});
87+
88+
var listenerCount=0;
89+
DesktopCaptureMonitor.prototype.on = DesktopCaptureMonitor.prototype.addListener = function (ev, callback) {
90+
//throw except if unsupported event
91+
if (ev != "added" && ev != "removed" && ev != "orderchanged" && ev != "namechanged" && ev != "thumbnailchanged")
92+
throw new String("only following events can be listened: added, removed, moved, namechanged, thumbnailchanged");
93+
94+
process.EventEmitter.prototype.addListener.apply(this, arguments);
95+
}
96+
97+
98+
exports.DesktopCaptureMonitor=DesktopCaptureMonitor;
99+
21100
var screenInstance = null;
22101

23102
function Screen() {
24103
nw.allocateObject(this, {});
25104
this._numListener = 0;
105+
this.DesktopCaptureMonitor = new DesktopCaptureMonitor();
26106
}
27107
require('util').inherits(Screen, exports.Base);
28108

src/nw_shell.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
using nw::NativeWindowAura;
7979
#endif
8080
#include "content/public/browser/media_capture_devices.h"
81+
#include "media/audio/audio_manager_base.h"
8182

8283
#include "chrome/browser/printing/print_view_manager_basic.h"
8384
#include "extensions/common/extension_messages.h"
@@ -728,6 +729,11 @@ void Shell::RequestMediaAccessPermission(
728729
devices.push_back(content::MediaStreamDevice(
729730
content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
730731
}
732+
#if defined(OS_WIN)
733+
if (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) {
734+
devices.push_back(content::MediaStreamDevice(content::MEDIA_DESKTOP_AUDIO_CAPTURE, media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
735+
}
736+
#endif
731737
// TODO(jamescook): Should we show a recording icon somewhere? If so, where?
732738
scoped_ptr<MediaStreamUI> ui;
733739
callback.Run(devices,

0 commit comments

Comments
 (0)