tenfourfox/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc

845 lines
29 KiB
C++

/*
* 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;
if (len && deviceNameUTF8 && len < deviceNameUTF8Length) {
memcpy(deviceNameUTF8,
deviceName,
len);
}
const char *deviceUniqueId = desktopDisplayDevice.getUniqueIdName();
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
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;
if (len && len < deviceNameUTF8Length) {
memcpy(deviceNameUTF8, deviceName, len);
}
const char *deviceUniqueId = desktopApplication.getUniqueIdName();
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
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;
if (len && deviceNameUTF8 && len < deviceNameUTF8Length) {
memcpy(deviceNameUTF8,
deviceName,
len);
}
const char *deviceUniqueId = desktopDisplayDevice.getUniqueIdName();
len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
if (len && deviceUniqueIdUTF8 && len < deviceUniqueIdUTF8Length) {
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