blob: f976d10f53ba38f0011084d6a3bfcde8dbb11762 [file] [log] [blame]
/*
* Copyright (c) 2012 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 "video_capture_impl.h"
#include "common_video/libyuv/include/libyuv.h"
#include "critical_section_wrapper.h"
#include "module_common_types.h"
#include "ref_count.h"
#include "tick_util.h"
#include "trace.h"
#include "video_capture_config.h"
#ifdef WEBRTC_ANDROID
#include "video_capture_android.h" // Need inclusion here to set Java environment.
#endif
namespace webrtc
{
namespace videocapturemodule
{
VideoCaptureModule* VideoCaptureImpl::Create(
const WebRtc_Word32 id,
VideoCaptureExternal*& externalCapture)
{
RefCountImpl<VideoCaptureImpl>* implementation =
new RefCountImpl<VideoCaptureImpl>(id);
externalCapture = implementation;
return implementation;
}
const WebRtc_UWord8* VideoCaptureImpl::CurrentDeviceName() const
{
return _deviceUniqueId;
}
WebRtc_Word32 VideoCaptureImpl::ChangeUniqueId(const WebRtc_Word32 id)
{
_id = id;
return 0;
}
// returns the number of milliseconds until the module want a worker thread to call Process
WebRtc_Word32 VideoCaptureImpl::TimeUntilNextProcess()
{
TickTime timeNow = TickTime::Now();
WebRtc_Word32 timeToNormalProcess = kProcessInterval
- (WebRtc_Word32)((TickTime::Now() - _lastProcessTime).Milliseconds());
WebRtc_Word32 timeToStartImage = timeToNormalProcess;
if (_startImageFrameIntervall)
{
timeToStartImage = _startImageFrameIntervall
- (WebRtc_Word32)((timeNow - _lastSentStartImageTime).Milliseconds());
if (timeToStartImage < 0)
{
timeToStartImage = 0;
}
}
return (timeToStartImage < timeToNormalProcess)
? timeToStartImage : timeToNormalProcess;
}
// Process any pending tasks such as timeouts
WebRtc_Word32 VideoCaptureImpl::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 WebRtc_UWord32 frameRate = CalculateFrameRate(now);
_captureCallBack->OnCaptureFrameRate(_id, frameRate);
}
_lastFrameRateCallbackTime = now; // Can be set by EnableFrameRateCallback
}
_lastProcessFrameCount = _incomingFrameTimes[0];
// Handle start image frame rates.
if (_startImageFrameIntervall
&& (now - _lastSentStartImageTime).Milliseconds() >= _startImageFrameIntervall)
{
_lastSentStartImageTime = now;
if (_dataCallBack)
{
_captureFrame.CopyFrame(_startImage);
_captureFrame.SetRenderTime(TickTime::MillisecondTimestamp());
_dataCallBack->OnIncomingCapturedFrame(_id, _captureFrame,
kVideoCodecUnknown);
}
}
return 0;
}
VideoCaptureImpl::VideoCaptureImpl(const WebRtc_Word32 id)
: _id(id), _deviceUniqueId(NULL), _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),
_startImage(), _startImageFrameIntervall(0),
_lastSentStartImageTime(TickTime::Now()),
_lastProcessFrameCount(TickTime::Now()), _rotateFrame(kRotateNone),
last_capture_time_(TickTime::MillisecondTimestamp())
{
_requestedCapability.width = kDefaultWidth;
_requestedCapability.height = kDefaultHeight;
_requestedCapability.maxFPS = 30;
_requestedCapability.rawType = kVideoI420;
_requestedCapability.codecType = kVideoCodecUnknown;
memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
}
VideoCaptureImpl::~VideoCaptureImpl()
{
DeRegisterCaptureDataCallback();
DeRegisterCaptureCallback();
delete &_callBackCs;
delete &_apiCs;
if (_deviceUniqueId)
delete[] _deviceUniqueId;
}
WebRtc_Word32 VideoCaptureImpl::RegisterCaptureDataCallback(
VideoCaptureDataCallback& dataCallBack)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_dataCallBack = &dataCallBack;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::DeRegisterCaptureDataCallback()
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_dataCallBack = NULL;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::RegisterCaptureCallback(VideoCaptureFeedBack& callBack)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_captureCallBack = &callBack;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::DeRegisterCaptureCallback()
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_captureCallBack = NULL;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::SetCaptureDelay(WebRtc_Word32 delayMS)
{
CriticalSectionScoped cs(_apiCs);
_captureDelay = delayMS;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::CaptureDelay()
{
CriticalSectionScoped cs(_apiCs);
return _setCaptureDelay;
}
WebRtc_Word32 VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame,
WebRtc_Word32 width, WebRtc_Word32 height, WebRtc_Word64 capture_time,
VideoCodecType codec_type) {
UpdateFrameCount();// frame count used for local frame rate callback.
_startImageFrameIntervall = 0; // prevent the start image to be displayed.
const bool callOnCaptureDelayChanged = _setCaptureDelay != _captureDelay;
// Capture delay changed
if (_setCaptureDelay != _captureDelay) {
_setCaptureDelay = _captureDelay;
}
// Set the capture time
if (capture_time != 0) {
captureFrame.SetRenderTime(capture_time);
}
else {
captureFrame.SetRenderTime(TickTime::MillisecondTimestamp());
}
if (captureFrame.RenderTimeMs() == last_capture_time_) {
// We don't allow the same capture time for two frames, drop this one.
return -1;
}
last_capture_time_ = captureFrame.RenderTimeMs();
captureFrame.SetHeight(height);
captureFrame.SetWidth(width);
if (_dataCallBack) {
if (callOnCaptureDelayChanged) {
_dataCallBack->OnCaptureDelayChanged(_id, _captureDelay);
}
_dataCallBack->OnIncomingCapturedFrame(_id, captureFrame, codec_type);
}
return 0;
}
WebRtc_Word32 VideoCaptureImpl::IncomingFrame(
WebRtc_UWord8* videoFrame,
WebRtc_Word32 videoFrameLength,
const VideoCaptureCapability& frameInfo,
WebRtc_Word64 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 WebRtc_Word32 width = frameInfo.width;
const WebRtc_Word32 height = frameInfo.height;
if (frameInfo.codecType == kVideoCodecUnknown)
{
// Not encoded, convert to I420.
const VideoType commonVideoType =
RawVideoTypeToCommonVideoVideoType(frameInfo.rawType);
if (frameInfo.rawType != kVideoMJPEG &&
CalcBufferSize(commonVideoType, width, height) != videoFrameLength)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Wrong incoming frame length.");
return -1;
}
// Allocate I420 buffer.
int requiredLength = CalcBufferSize(kI420, width, height);
_captureFrame.VerifyAndAllocate(requiredLength);
if (!_captureFrame.Buffer())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to allocate frame buffer.");
return -1;
}
memset(_captureFrame.Buffer(), 0, _captureFrame.Size());
// Keeping stride = width for I420 destination.
int dstStride = width;
const int conversionResult = ConvertToI420(commonVideoType,
videoFrame,
0, 0, // No cropping
width, height,
videoFrameLength,
width, height, dstStride,
_rotateFrame,
_captureFrame.Buffer());
if (conversionResult < 0)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to convert capture frame from type %d to I420",
frameInfo.rawType);
return -1;
}
_captureFrame.SetLength(requiredLength);
}
else // Encoded format
{
if (_captureFrame.CopyFrame(videoFrameLength, videoFrame) != 0)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to copy captured frame of length %d", (int) videoFrameLength);
}
}
DeliverCapturedFrame(_captureFrame, width, height, captureTime, frameInfo.codecType);
const WebRtc_UWord32 processTime =
(WebRtc_UWord32)(TickTime::Now() - startProcessTime).Milliseconds();
if (processTime > 10) // If the process time is too long MJPG will not work well.
{
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
"Too long processing time of Incoming frame: %ums",
(unsigned int) processTime);
}
return 0;
}
WebRtc_Word32 VideoCaptureImpl::IncomingFrameI420(
const VideoFrameI420& video_frame, WebRtc_Word64 captureTime) {
CriticalSectionScoped cs(_callBackCs);
// Allocate I420 buffer
int frame_size = CalcBufferSize(kI420,
video_frame.width,
video_frame.height);
_captureFrame.VerifyAndAllocate(frame_size);
if (!_captureFrame.Buffer()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to allocate frame buffer.");
return -1;
}
// Copy planes to the _captureFrame
int y_width = video_frame.width;
int uv_width = video_frame.width / 2;
int y_rows = video_frame.height;
int uv_rows = video_frame.height / 2; // I420
unsigned char* current_pointer = _captureFrame.Buffer();
unsigned char* y_plane = video_frame.y_plane;
unsigned char* u_plane = video_frame.u_plane;
unsigned char* v_plane = video_frame.v_plane;
// Copy Y
for (int i = 0; i < y_rows; ++i) {
memcpy(current_pointer, y_plane, y_width);
current_pointer += video_frame.y_pitch;
y_plane += video_frame.y_pitch;
}
// Copy U
for (int i = 0; i < uv_rows; ++i) {
memcpy(current_pointer, u_plane, uv_width);
current_pointer += video_frame.u_pitch;
u_plane += video_frame.u_pitch;
}
// Copy V
for (int i = 0; i < uv_rows; ++i) {
memcpy(current_pointer, v_plane, uv_width);
current_pointer += video_frame.v_pitch;
v_plane += video_frame.v_pitch;
}
_captureFrame.SetLength(frame_size);
DeliverCapturedFrame(_captureFrame,
video_frame.width,
video_frame.height,
captureTime,
kVideoCodecUnknown);
return 0;
}
WebRtc_Word32 VideoCaptureImpl::SetCaptureRotation(VideoCaptureRotation rotation)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
switch (rotation)
{
case kCameraRotate0:
_rotateFrame = kRotateNone;
break;
case kCameraRotate90:
_rotateFrame = kRotate90;
break;
case kCameraRotate180:
_rotateFrame = kRotate180;
break;
case kCameraRotate270:
_rotateFrame = kRotate270;
break;
}
return 0;
}
WebRtc_Word32 VideoCaptureImpl::StartSendImage(const VideoFrame& videoFrame,
WebRtc_Word32 frameRate)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
if (frameRate < 1 || frameRate > kMaxFrameRate)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"StartSendImage Invalid parameter. frameRate %d", (int) frameRate);
return -1;;
}
_startImage.CopyFrame(videoFrame);
_startImageFrameIntervall = 1000 / frameRate;
_lastSentStartImageTime = TickTime::Now();
return 0;
}
WebRtc_Word32 VideoCaptureImpl::StopSendImage()
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_startImageFrameIntervall = 0;
return 0;
}
WebRtc_Word32 VideoCaptureImpl::EnableFrameRateCallback(const bool enable)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_frameRateCallBack = enable;
if (enable)
{
_lastFrameRateCallbackTime = TickTime::Now();
}
return 0;
}
WebRtc_Word32 VideoCaptureImpl::EnableNoPictureAlarm(const bool enable)
{
CriticalSectionScoped cs(_apiCs);
CriticalSectionScoped cs2(_callBackCs);
_noPictureAlarmCallBack = enable;
return 0;
}
void VideoCaptureImpl::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();
}
WebRtc_UWord32 VideoCaptureImpl::CalculateFrameRate(const TickTime& now)
{
WebRtc_Word32 num = 0;
WebRtc_Word32 nrOfFrames = 0;
for (num = 1; num < (kFrameRateCountHistorySize - 1); num++)
{
if (_incomingFrameTimes[num].Ticks() <= 0
|| (now - _incomingFrameTimes[num]).Milliseconds() > kFrameRateHistoryWindowMs) // don't use data older than 2sec
{
break;
}
else
{
nrOfFrames++;
}
}
if (num > 1)
{
WebRtc_Word64 diff = (now - _incomingFrameTimes[num - 1]).Milliseconds();
if (diff > 0)
{
return WebRtc_UWord32((nrOfFrames * 1000.0f / diff) + 0.5f);
}
}
return nrOfFrames;
}
} // namespace videocapturemodule
} // namespace webrtc