2017-04-19 07:56:45 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
|
|
|
* in the file PATENTS. All contributing project authors may
|
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "webrtc/modules/video_capture/video_capture_impl.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "webrtc/common_video/libyuv/include/scaler.h"
|
|
|
|
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
|
|
|
#include "webrtc/modules/interface/module_common_types.h"
|
|
|
|
#include "webrtc/modules/video_capture/video_capture_config.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/clock.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/ref_count.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/tick_util.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/trace.h"
|
|
|
|
#include "webrtc/system_wrappers/interface/trace_event.h"
|
|
|
|
#include "webrtc/video_engine/desktop_capture_impl.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/desktop_device_info.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/app_capturer.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/window_capturer.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
|
|
|
|
|
|
|
|
namespace webrtc {
|
|
|
|
|
|
|
|
ScreenDeviceInfoImpl::ScreenDeviceInfoImpl(const int32_t id) : _id(id) {
|
|
|
|
}
|
|
|
|
|
|
|
|
ScreenDeviceInfoImpl::~ScreenDeviceInfoImpl(void) {
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::Init() {
|
|
|
|
desktop_device_info_.reset(DesktopDeviceInfoImpl::Create());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::Refresh() {
|
|
|
|
desktop_device_info_->Refresh();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t ScreenDeviceInfoImpl::NumberOfDevices() {
|
|
|
|
return desktop_device_info_->getDisplayDeviceCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::GetDeviceName(uint32_t deviceNumber,
|
|
|
|
char* deviceNameUTF8,
|
|
|
|
uint32_t deviceNameUTF8Length,
|
|
|
|
char* deviceUniqueIdUTF8,
|
|
|
|
uint32_t deviceUniqueIdUTF8Length,
|
|
|
|
char* productUniqueIdUTF8,
|
|
|
|
uint32_t productUniqueIdUTF8Length) {
|
|
|
|
|
|
|
|
DesktopDisplayDevice desktopDisplayDevice;
|
|
|
|
|
|
|
|
// always initialize output
|
|
|
|
if (deviceNameUTF8 && deviceNameUTF8Length > 0) {
|
|
|
|
memset(deviceNameUTF8, 0, deviceNameUTF8Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deviceUniqueIdUTF8 && deviceUniqueIdUTF8Length > 0) {
|
|
|
|
memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
|
|
|
|
memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desktop_device_info_->getDesktopDisplayDeviceInfo(deviceNumber,
|
|
|
|
desktopDisplayDevice) == 0) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
const char *deviceName = desktopDisplayDevice.getDeviceName();
|
|
|
|
len = deviceName ? strlen(deviceName) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && deviceNameUTF8 && len < deviceNameUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceNameUTF8,
|
|
|
|
deviceName,
|
|
|
|
len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *deviceUniqueId = desktopDisplayDevice.getUniqueIdName();
|
|
|
|
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceUniqueIdUTF8,
|
|
|
|
deviceUniqueId,
|
|
|
|
len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
|
|
|
|
const char* dialogTitleUTF8,
|
|
|
|
void* parentWindow,
|
|
|
|
uint32_t positionX,
|
|
|
|
uint32_t positionY) {
|
|
|
|
// no device properties to change
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8,
|
|
|
|
const uint32_t deviceCapabilityNumber,
|
|
|
|
VideoCaptureCapability& capability) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::GetBestMatchedCapability(const char* deviceUniqueIdUTF8,
|
|
|
|
const VideoCaptureCapability& requested,
|
|
|
|
VideoCaptureCapability& resulting) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ScreenDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
|
|
|
|
VideoRotation& orientation) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AppDeviceInfoImpl::AppDeviceInfoImpl(const int32_t id) {
|
|
|
|
}
|
|
|
|
|
|
|
|
AppDeviceInfoImpl::~AppDeviceInfoImpl(void) {
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::Init() {
|
|
|
|
desktop_device_info_.reset(DesktopDeviceInfoImpl::Create());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::Refresh() {
|
|
|
|
desktop_device_info_->Refresh();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t AppDeviceInfoImpl::NumberOfDevices() {
|
|
|
|
return desktop_device_info_->getApplicationCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::GetDeviceName(uint32_t deviceNumber,
|
|
|
|
char* deviceNameUTF8,
|
|
|
|
uint32_t deviceNameUTF8Length,
|
|
|
|
char* deviceUniqueIdUTF8,
|
|
|
|
uint32_t deviceUniqueIdUTF8Length,
|
|
|
|
char* productUniqueIdUTF8,
|
|
|
|
uint32_t productUniqueIdUTF8Length) {
|
|
|
|
|
|
|
|
DesktopApplication desktopApplication;
|
|
|
|
|
|
|
|
// always initialize output
|
|
|
|
if (deviceNameUTF8Length && deviceNameUTF8Length > 0) {
|
|
|
|
memset(deviceNameUTF8, 0, deviceNameUTF8Length);
|
|
|
|
}
|
|
|
|
if (deviceUniqueIdUTF8 && deviceUniqueIdUTF8Length > 0) {
|
|
|
|
memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
|
|
|
|
memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desktop_device_info_->getApplicationInfo(deviceNumber,desktopApplication) == 0) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
const char *deviceName = desktopApplication.getProcessAppName();
|
|
|
|
len = deviceName ? strlen(deviceName) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && len < deviceNameUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceNameUTF8, deviceName, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *deviceUniqueId = desktopApplication.getUniqueIdName();
|
|
|
|
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceUniqueIdUTF8,
|
|
|
|
deviceUniqueId,
|
|
|
|
len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
|
|
|
|
const char* dialogTitleUTF8,
|
|
|
|
void* parentWindow,
|
|
|
|
uint32_t positionX,
|
|
|
|
uint32_t positionY) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::GetCapability(
|
|
|
|
const char* deviceUniqueIdUTF8,
|
|
|
|
const uint32_t deviceCapabilityNumber,
|
|
|
|
VideoCaptureCapability& capability) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::GetBestMatchedCapability(
|
|
|
|
const char* deviceUniqueIdUTF8,
|
|
|
|
const VideoCaptureCapability& requested,
|
|
|
|
VideoCaptureCapability& resulting) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t AppDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
|
|
|
|
VideoRotation& orientation) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoCaptureModule* DesktopCaptureImpl::Create(const int32_t id,
|
|
|
|
const char* uniqueId,
|
|
|
|
const CaptureDeviceType type) {
|
|
|
|
RefCountImpl<DesktopCaptureImpl>* capture = new RefCountImpl<DesktopCaptureImpl>(id);
|
|
|
|
|
|
|
|
//create real screen capturer.
|
|
|
|
if (capture->Init(uniqueId, type)) {
|
|
|
|
delete capture;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return capture;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::Init() {
|
|
|
|
desktop_device_info_.reset(DesktopDeviceInfoImpl::Create());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::Refresh() {
|
|
|
|
desktop_device_info_->Refresh();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WindowDeviceInfoImpl::NumberOfDevices() {
|
|
|
|
return desktop_device_info_->getWindowCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::GetDeviceName(uint32_t deviceNumber,
|
|
|
|
char* deviceNameUTF8,
|
|
|
|
uint32_t deviceNameUTF8Length,
|
|
|
|
char* deviceUniqueIdUTF8,
|
|
|
|
uint32_t deviceUniqueIdUTF8Length,
|
|
|
|
char* productUniqueIdUTF8,
|
|
|
|
uint32_t productUniqueIdUTF8Length) {
|
|
|
|
|
|
|
|
DesktopDisplayDevice desktopDisplayDevice;
|
|
|
|
|
|
|
|
// always initialize output
|
|
|
|
if (deviceNameUTF8 && deviceNameUTF8Length > 0) {
|
|
|
|
memset(deviceNameUTF8, 0, deviceNameUTF8Length);
|
|
|
|
}
|
|
|
|
if (deviceUniqueIdUTF8 && deviceUniqueIdUTF8Length > 0) {
|
|
|
|
memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
|
|
|
|
memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desktop_device_info_->getWindowInfo(deviceNumber,
|
|
|
|
desktopDisplayDevice) == 0) {
|
|
|
|
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
const char *deviceName = desktopDisplayDevice.getDeviceName();
|
|
|
|
len = deviceName ? strlen(deviceName) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && deviceNameUTF8 && len < deviceNameUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceNameUTF8,
|
|
|
|
deviceName,
|
|
|
|
len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *deviceUniqueId = desktopDisplayDevice.getUniqueIdName();
|
|
|
|
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
|
2020-02-08 04:23:59 +00:00
|
|
|
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
|
2017-04-19 07:56:45 +00:00
|
|
|
memcpy(deviceUniqueIdUTF8,
|
|
|
|
deviceUniqueId,
|
|
|
|
len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
|
|
|
|
const char* dialogTitleUTF8,
|
|
|
|
void* parentWindow,
|
|
|
|
uint32_t positionX,
|
|
|
|
uint32_t positionY) {
|
|
|
|
// no device properties to change
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8,
|
|
|
|
const uint32_t deviceCapabilityNumber,
|
|
|
|
VideoCaptureCapability& capability) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::GetBestMatchedCapability(const char* deviceUniqueIdUTF8,
|
|
|
|
const VideoCaptureCapability& requested,
|
|
|
|
VideoCaptureCapability& resulting) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WindowDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
|
|
|
|
VideoRotation& orientation) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoCaptureModule::DeviceInfo* DesktopCaptureImpl::CreateDeviceInfo(const int32_t id,
|
|
|
|
const CaptureDeviceType type) {
|
|
|
|
if (type == Application) {
|
|
|
|
AppDeviceInfoImpl * pAppDeviceInfoImpl = new AppDeviceInfoImpl(id);
|
|
|
|
if (!pAppDeviceInfoImpl || pAppDeviceInfoImpl->Init()) {
|
|
|
|
delete pAppDeviceInfoImpl;
|
|
|
|
pAppDeviceInfoImpl = NULL;
|
|
|
|
}
|
|
|
|
return pAppDeviceInfoImpl;
|
|
|
|
} else if (type == Screen) {
|
|
|
|
ScreenDeviceInfoImpl * pScreenDeviceInfoImpl = new ScreenDeviceInfoImpl(id);
|
|
|
|
if (!pScreenDeviceInfoImpl || pScreenDeviceInfoImpl->Init()) {
|
|
|
|
delete pScreenDeviceInfoImpl;
|
|
|
|
pScreenDeviceInfoImpl = NULL;
|
|
|
|
}
|
|
|
|
return pScreenDeviceInfoImpl;
|
|
|
|
} else if (type == Window) {
|
|
|
|
WindowDeviceInfoImpl * pWindowDeviceInfoImpl = new WindowDeviceInfoImpl(id);
|
|
|
|
if (!pWindowDeviceInfoImpl || pWindowDeviceInfoImpl->Init()) {
|
|
|
|
delete pWindowDeviceInfoImpl;
|
|
|
|
pWindowDeviceInfoImpl = NULL;
|
|
|
|
}
|
|
|
|
return pWindowDeviceInfoImpl;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* DesktopCaptureImpl::CurrentDeviceName() const {
|
|
|
|
return _deviceUniqueId.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::Init(const char* uniqueId,
|
|
|
|
const CaptureDeviceType type) {
|
|
|
|
DesktopCaptureOptions options = DesktopCaptureOptions::CreateDefault();
|
|
|
|
// Leave desktop effects enabled during WebRTC captures.
|
|
|
|
options.set_disable_effects(false);
|
|
|
|
|
|
|
|
if (type == Application) {
|
|
|
|
AppCapturer *pAppCapturer = AppCapturer::Create(options);
|
|
|
|
if (!pAppCapturer) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessId pid = atoi(uniqueId);
|
|
|
|
pAppCapturer->SelectApp(pid);
|
|
|
|
|
|
|
|
MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, webrtc::kFullDesktopScreenId);
|
|
|
|
desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pAppCapturer, pMouseCursorMonitor));
|
|
|
|
} else if (type == Screen) {
|
|
|
|
ScreenCapturer *pScreenCapturer = ScreenCapturer::Create(options);
|
|
|
|
if (!pScreenCapturer) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScreenId screenid = atoi(uniqueId);
|
|
|
|
pScreenCapturer->SelectScreen(screenid);
|
|
|
|
pScreenCapturer->SetMouseShapeObserver(this);
|
|
|
|
|
|
|
|
MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, screenid);
|
|
|
|
desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pScreenCapturer, pMouseCursorMonitor));
|
|
|
|
} else if (type == Window) {
|
|
|
|
WindowCapturer *pWindowCapturer = WindowCapturer::Create();
|
|
|
|
if (!pWindowCapturer) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
WindowId winId = atoi(uniqueId);
|
|
|
|
pWindowCapturer->SelectWindow(winId);
|
|
|
|
|
|
|
|
MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForWindow(webrtc::DesktopCaptureOptions::CreateDefault(), winId);
|
|
|
|
desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pWindowCapturer, pMouseCursorMonitor));
|
|
|
|
}
|
|
|
|
_deviceUniqueId = uniqueId;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the number of milliseconds until the module want a worker thread to call Process
|
|
|
|
int64_t DesktopCaptureImpl::TimeUntilNextProcess() {
|
|
|
|
CriticalSectionScoped cs(&_callBackCs);
|
|
|
|
|
|
|
|
int64_t timeToNormalProcess = 300
|
|
|
|
- (int64_t)((TickTime::Now() - _lastProcessTime).Milliseconds());
|
|
|
|
|
|
|
|
return timeToNormalProcess;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process any pending tasks such as timeouts
|
|
|
|
int32_t DesktopCaptureImpl::Process() {
|
|
|
|
CriticalSectionScoped cs(&_callBackCs);
|
|
|
|
|
|
|
|
const TickTime now = TickTime::Now();
|
|
|
|
_lastProcessTime = TickTime::Now();
|
|
|
|
|
|
|
|
// Handle No picture alarm
|
|
|
|
if (_lastProcessFrameCount.Ticks() == _incomingFrameTimes[0].Ticks() &&
|
|
|
|
_captureAlarm != Raised) {
|
|
|
|
if (_noPictureAlarmCallBack && _captureCallBack) {
|
|
|
|
_captureAlarm = Raised;
|
|
|
|
_captureCallBack->OnNoPictureAlarm(_id, _captureAlarm);
|
|
|
|
}
|
|
|
|
} else if (_lastProcessFrameCount.Ticks() != _incomingFrameTimes[0].Ticks() &&
|
|
|
|
_captureAlarm != Cleared) {
|
|
|
|
if (_noPictureAlarmCallBack && _captureCallBack) {
|
|
|
|
_captureAlarm = Cleared;
|
|
|
|
_captureCallBack->OnNoPictureAlarm(_id, _captureAlarm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle frame rate callback
|
|
|
|
if ((now - _lastFrameRateCallbackTime).Milliseconds()
|
|
|
|
> kFrameRateCallbackInterval) {
|
|
|
|
if (_frameRateCallBack && _captureCallBack) {
|
|
|
|
const uint32_t frameRate = CalculateFrameRate(now);
|
|
|
|
_captureCallBack->OnCaptureFrameRate(_id, frameRate);
|
|
|
|
}
|
|
|
|
_lastFrameRateCallbackTime = now; // Can be set by EnableFrameRateCallback
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastProcessFrameCount = _incomingFrameTimes[0];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id)
|
|
|
|
: _id(id),
|
|
|
|
_deviceUniqueId(""),
|
|
|
|
_apiCs(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
|
|
_captureDelay(0),
|
|
|
|
_requestedCapability(),
|
|
|
|
_callBackCs(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
|
|
_lastProcessTime(TickTime::Now()),
|
|
|
|
_lastFrameRateCallbackTime(TickTime::Now()),
|
|
|
|
_frameRateCallBack(false),
|
|
|
|
_noPictureAlarmCallBack(false),
|
|
|
|
_captureAlarm(Cleared),
|
|
|
|
_setCaptureDelay(0),
|
|
|
|
_dataCallBack(NULL),
|
|
|
|
_captureCallBack(NULL),
|
|
|
|
_lastProcessFrameCount(TickTime::Now()),
|
|
|
|
_rotateFrame(kVideoRotation_0),
|
|
|
|
last_capture_time_(TickTime::MillisecondTimestamp()),
|
|
|
|
delta_ntp_internal_ms_(
|
|
|
|
Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() -
|
|
|
|
TickTime::MillisecondTimestamp()),
|
|
|
|
time_event_(EventWrapper::Create()),
|
|
|
|
#if defined(_WIN32)
|
|
|
|
capturer_thread_(ThreadWrapper::CreateUIThread(Run, this, "ScreenCaptureThread")) {
|
|
|
|
#else
|
|
|
|
capturer_thread_(ThreadWrapper::CreateThread(Run, this, "ScreenCaptureThread")) {
|
|
|
|
#endif
|
|
|
|
capturer_thread_->SetPriority(kHighPriority);
|
|
|
|
_requestedCapability.width = kDefaultWidth;
|
|
|
|
_requestedCapability.height = kDefaultHeight;
|
|
|
|
_requestedCapability.maxFPS = 30;
|
|
|
|
_requestedCapability.rawType = kVideoI420;
|
|
|
|
_requestedCapability.codecType = kVideoCodecUnknown;
|
|
|
|
memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
|
|
|
|
}
|
|
|
|
|
|
|
|
DesktopCaptureImpl::~DesktopCaptureImpl() {
|
|
|
|
time_event_->Set();
|
|
|
|
capturer_thread_->Stop();
|
|
|
|
|
|
|
|
DeRegisterCaptureDataCallback();
|
|
|
|
DeRegisterCaptureCallback();
|
|
|
|
delete &_callBackCs;
|
|
|
|
delete &_apiCs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::RegisterCaptureDataCallback(
|
|
|
|
VideoCaptureDataCallback& dataCallBack)
|
|
|
|
{
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_dataCallBack = &dataCallBack;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::DeRegisterCaptureDataCallback()
|
|
|
|
{
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_dataCallBack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::RegisterCaptureCallback(VideoCaptureFeedBack& callBack)
|
|
|
|
{
|
|
|
|
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_captureCallBack = &callBack;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::DeRegisterCaptureCallback()
|
|
|
|
{
|
|
|
|
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_captureCallBack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::SetCaptureDelay(int32_t delayMS)
|
|
|
|
{
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
_captureDelay = delayMS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::CaptureDelay()
|
|
|
|
{
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
return _setCaptureDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::DeliverCapturedFrame(I420VideoFrame& captureFrame,
|
|
|
|
int64_t capture_time) {
|
|
|
|
UpdateFrameCount(); // frame count used for local frame rate callback.
|
|
|
|
|
|
|
|
const bool callOnCaptureDelayChanged = _setCaptureDelay != _captureDelay;
|
|
|
|
// Capture delay changed
|
|
|
|
if (_setCaptureDelay != _captureDelay) {
|
|
|
|
_setCaptureDelay = _captureDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the capture time
|
|
|
|
if (capture_time != 0) {
|
|
|
|
captureFrame.set_render_time_ms(capture_time - delta_ntp_internal_ms_);
|
|
|
|
} else {
|
|
|
|
captureFrame.set_render_time_ms(TickTime::MillisecondTimestamp());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (captureFrame.render_time_ms() == last_capture_time_) {
|
|
|
|
// We don't allow the same capture time for two frames, drop this one.
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
last_capture_time_ = captureFrame.render_time_ms();
|
|
|
|
|
|
|
|
if (_dataCallBack) {
|
|
|
|
if (callOnCaptureDelayChanged) {
|
|
|
|
_dataCallBack->OnCaptureDelayChanged(_id, _captureDelay);
|
|
|
|
}
|
|
|
|
_dataCallBack->OnIncomingCapturedFrame(_id, captureFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copied from VideoCaptureImpl::IncomingFrame. See Bug 1038324
|
|
|
|
int32_t DesktopCaptureImpl::IncomingFrame(uint8_t* videoFrame,
|
|
|
|
size_t videoFrameLength,
|
|
|
|
const VideoCaptureCapability& frameInfo,
|
|
|
|
int64_t captureTime/*=0*/)
|
|
|
|
{
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"IncomingFrame width %d, height %d", (int) frameInfo.width,
|
|
|
|
(int) frameInfo.height);
|
|
|
|
|
|
|
|
TickTime startProcessTime = TickTime::Now();
|
|
|
|
|
|
|
|
CriticalSectionScoped cs(&_callBackCs);
|
|
|
|
|
|
|
|
const int32_t width = frameInfo.width;
|
|
|
|
const int32_t height = frameInfo.height;
|
|
|
|
|
|
|
|
TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
|
|
|
|
|
|
|
|
if (frameInfo.codecType == kVideoCodecUnknown) {
|
|
|
|
// Not encoded, convert to I420.
|
|
|
|
const VideoType commonVideoType =
|
|
|
|
RawVideoTypeToCommonVideoVideoType(frameInfo.rawType);
|
|
|
|
|
|
|
|
if (frameInfo.rawType != kVideoMJPEG &&
|
|
|
|
CalcBufferSize(commonVideoType, width,
|
|
|
|
abs(height)) != videoFrameLength) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Wrong incoming frame length.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int stride_y = width;
|
|
|
|
int stride_uv = (width + 1) / 2;
|
|
|
|
int target_width = width;
|
|
|
|
int target_height = abs(height);
|
|
|
|
// Rotating resolution when for 90/270 degree rotations.
|
|
|
|
if (_rotateFrame == kVideoRotation_90 || _rotateFrame == kVideoRotation_270) {
|
|
|
|
target_height = width;
|
|
|
|
target_width = abs(height);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setting absolute height (in case it was negative).
|
|
|
|
// In Windows, the image starts bottom left, instead of top left.
|
|
|
|
// Setting a negative source height, inverts the image (within LibYuv).
|
|
|
|
int ret = _captureFrame.CreateEmptyFrame(target_width,
|
|
|
|
target_height,
|
|
|
|
stride_y,
|
|
|
|
stride_uv, stride_uv);
|
|
|
|
if (ret < 0) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Failed to allocate I420 frame.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const int conversionResult = ConvertToI420(commonVideoType,
|
|
|
|
videoFrame,
|
|
|
|
0, 0, // No cropping
|
|
|
|
width, height,
|
|
|
|
videoFrameLength,
|
|
|
|
_rotateFrame,
|
|
|
|
&_captureFrame);
|
|
|
|
if (conversionResult < 0) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Failed to convert capture frame from type %d to I420",
|
|
|
|
frameInfo.rawType);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t req_max_width = _requestedCapability.width & 0xffff;
|
|
|
|
int32_t req_max_height = _requestedCapability.height & 0xffff;
|
|
|
|
int32_t req_ideal_width = (_requestedCapability.width >> 16) & 0xffff;
|
|
|
|
int32_t req_ideal_height = (_requestedCapability.height >> 16) & 0xffff;
|
|
|
|
|
|
|
|
int32_t dest_max_width = std::min(req_max_width, target_width);
|
|
|
|
int32_t dest_max_height = std::min(req_max_height, target_height);
|
|
|
|
int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : target_width, dest_max_width);
|
|
|
|
int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : target_height, dest_max_height);
|
|
|
|
|
|
|
|
// scale to average of portrait and landscape
|
|
|
|
float scale_width = (float)dst_width / (float)target_width;
|
|
|
|
float scale_height = (float)dst_height / (float)target_height;
|
|
|
|
float scale = (scale_width + scale_height) / 2;
|
|
|
|
dst_width = (int)(scale * target_width);
|
|
|
|
dst_height = (int)(scale * target_height);
|
|
|
|
|
|
|
|
// if scaled rectangle exceeds max rectangle, scale to minimum of portrait and landscape
|
|
|
|
if (dst_width > dest_max_width || dst_height > dest_max_height) {
|
|
|
|
scale_width = (float)dest_max_width / (float)dst_width;
|
|
|
|
scale_height = (float)dest_max_height / (float)dst_height;
|
|
|
|
scale = std::min(scale_width, scale_height);
|
|
|
|
dst_width = (int)(scale * dst_width);
|
|
|
|
dst_height = (int)(scale * dst_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dst_width == target_width && dst_height == target_height) {
|
|
|
|
DeliverCapturedFrame(_captureFrame, captureTime);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
I420VideoFrame scaledFrame;
|
|
|
|
ret = scaledFrame.CreateEmptyFrame(dst_width,
|
|
|
|
dst_height,
|
|
|
|
stride_y,
|
|
|
|
stride_uv, stride_uv);
|
|
|
|
if (ret < 0) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Failed to allocate I420 frame.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
webrtc::Scaler s;
|
|
|
|
s.Set(target_width, target_height, dst_width, dst_height, kI420, kI420, kScaleBox);
|
|
|
|
const int scaleResult = s.Scale(_captureFrame, &scaledFrame);
|
|
|
|
|
|
|
|
if (scaleResult != 0) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Failed to scale capture frame from type %d",
|
|
|
|
frameInfo.rawType);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeliverCapturedFrame(scaledFrame, captureTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
assert(false);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t processTime =
|
|
|
|
(uint32_t)(TickTime::Now() - startProcessTime).Milliseconds();
|
|
|
|
|
|
|
|
if (processTime > 10) {
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
|
|
|
|
"Too long processing time of Incoming frame: %ums",
|
|
|
|
(unsigned int) processTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::SetCaptureRotation(VideoRotation rotation) {
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_rotateFrame = rotation;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DesktopCaptureImpl::SetApplyRotation(bool enable) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::EnableFrameRateCallback(const bool enable) {
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_frameRateCallBack = enable;
|
|
|
|
if (enable) {
|
|
|
|
_lastFrameRateCallbackTime = TickTime::Now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::EnableNoPictureAlarm(const bool enable) {
|
|
|
|
CriticalSectionScoped cs(&_apiCs);
|
|
|
|
CriticalSectionScoped cs2(&_callBackCs);
|
|
|
|
_noPictureAlarmCallBack = enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::UpdateFrameCount() {
|
|
|
|
if (_incomingFrameTimes[0].MicrosecondTimestamp() == 0) {
|
|
|
|
// first no shift
|
|
|
|
} else {
|
|
|
|
// shift
|
|
|
|
for (int i = (kFrameRateCountHistorySize - 2); i >= 0; i--) {
|
|
|
|
_incomingFrameTimes[i + 1] = _incomingFrameTimes[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_incomingFrameTimes[0] = TickTime::Now();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t DesktopCaptureImpl::CalculateFrameRate(const TickTime& now) {
|
|
|
|
int32_t num = 0;
|
|
|
|
int32_t nrOfFrames = 0;
|
|
|
|
for (num = 1; num < (kFrameRateCountHistorySize - 1); num++) {
|
|
|
|
// don't use data older than 2sec
|
|
|
|
if (_incomingFrameTimes[num].Ticks() <= 0
|
|
|
|
|| (now - _incomingFrameTimes[num]).Milliseconds() > kFrameRateHistoryWindowMs) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
nrOfFrames++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (num > 1) {
|
|
|
|
int64_t diff = (now - _incomingFrameTimes[num - 1]).Milliseconds();
|
|
|
|
if (diff > 0) {
|
|
|
|
// round to nearest value rather than return minimumid_
|
|
|
|
return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nrOfFrames;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::StartCapture(const VideoCaptureCapability& capability) {
|
|
|
|
_requestedCapability = capability;
|
|
|
|
#if defined(_WIN32)
|
|
|
|
uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
|
|
|
|
capturer_thread_->RequestCallbackTimer(maxFPSNeeded);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
desktop_capturer_cursor_composer_->Start(this);
|
|
|
|
capturer_thread_->Start();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::StopCapture() {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DesktopCaptureImpl::CaptureStarted() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t DesktopCaptureImpl::CaptureSettings(VideoCaptureCapability& settings) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::OnCaptureCompleted(DesktopFrame* frame) {
|
|
|
|
if (frame == NULL) return;
|
|
|
|
uint8_t * videoFrame = frame->data();
|
|
|
|
VideoCaptureCapability frameInfo;
|
|
|
|
frameInfo.width = frame->size().width();
|
|
|
|
frameInfo.height = frame->size().height();
|
|
|
|
frameInfo.rawType = kVideoARGB;
|
|
|
|
|
|
|
|
// combine cursor in frame
|
|
|
|
// Latest WebRTC already support it by DesktopFrameWithCursor/DesktopAndCursorComposer.
|
|
|
|
|
|
|
|
size_t videoFrameLength = frameInfo.width * frameInfo.height * DesktopFrame::kBytesPerPixel;
|
|
|
|
IncomingFrame(videoFrame, videoFrameLength, frameInfo);
|
|
|
|
delete frame; //handled it, so we need delete it
|
|
|
|
}
|
|
|
|
|
|
|
|
SharedMemory* DesktopCaptureImpl::CreateSharedMemory(size_t size) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesktopCaptureImpl::process() {
|
|
|
|
DesktopRect desktop_rect;
|
|
|
|
DesktopRegion desktop_region;
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
TickTime startProcessTime = TickTime::Now();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
desktop_capturer_cursor_composer_->Capture(DesktopRegion());
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
const uint32_t processTime =
|
|
|
|
(uint32_t)(TickTime::Now() - startProcessTime).Milliseconds();
|
|
|
|
// Use at most x% CPU or limit framerate
|
|
|
|
const uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
|
|
|
|
const float sleepTimeFactor = (100.0f / kMaxDesktopCaptureCpuUsage) - 1.0f;
|
|
|
|
const uint32_t sleepTime = sleepTimeFactor * processTime;
|
|
|
|
time_event_->Wait(std::max<uint32_t>(maxFPSNeeded, sleepTime));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace webrtc
|
|
|
|
|