webrtc/video_engine/main/source/vie_capturer.cc

1093 lines
37 KiB
C++

/*
* Copyright (c) 2011 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.
*/
/*
* vie_capturer.cc
*/
#include "vie_capturer.h"
#include "vie_defines.h"
#include "critical_section_wrapper.h"
#include "event_wrapper.h"
#include "module_common_types.h"
#include "video_capture.h"
#include "video_capture.h"
#include "video_processing.h"
#include "video_render_defines.h"
#include "thread_wrapper.h"
#include "vie_image_process.h"
#include "vie_encoder.h"
#include "process_thread.h"
#include "trace.h"
namespace webrtc {
// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------
ViECapturer::ViECapturer(int captureId,
int engineId,
ProcessThread& moduleProcessThread)
: ViEFrameProviderBase(captureId, engineId),
_captureCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
_deliverCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
_captureModule(NULL), _useExternalModule(false),
_externalCaptureModule(NULL),
_moduleProcessThread(moduleProcessThread),
_captureId(captureId),
_vieCaptureThread(*ThreadWrapper::CreateThread(ViECaptureThreadFunction,
this, kHighPriority,
"ViECaptureThread")),
_vieCaptureEvent(*EventWrapper::Create()),
_vieDeliverEvent(*EventWrapper::Create()), _capturedFrame(),
_deliverFrame(),
_observerCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
_observer(NULL), _effectFilter(NULL), _imageProcModule(NULL),
_imageProcModuleRefCounter(0), _deflickerFrameStats(NULL),
_brightnessFrameStats(NULL), _currentBrightnessLevel(Normal),
_reportedBrightnessLevel(Normal), _denoisingEnabled(false),
_encodingCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
_captureEncoder(NULL), _encodeCompleteCallback(NULL),
_vieEncoder(NULL), _vcm(NULL), _decodeBuffer(),
_decoderInitialized(false), _requestedCapability()
{
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engineId, captureId),
"ViECapturer::ViECapturer(captureId: %d, engineId: %d) - "
"Constructor", captureId, engineId);
unsigned int tId = 0;
if (_vieCaptureThread.Start(tId))
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engineId, captureId),
"%s: thread started: %u", __FUNCTION__, tId);
} else
{
assert(false);
}
}
// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------
ViECapturer::~ViECapturer()
{
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo,
ViEId(_engineId, _captureId),
"ViECapturer Destructor, captureId: %d, engineId: %d",
_captureId, _engineId);
// Stop the thread
_deliverCritsect.Enter();
_captureCritsect.Enter();
_vieCaptureThread.SetNotAlive();
_vieCaptureEvent.Set();
_captureCritsect.Leave();
_deliverCritsect.Leave();
_providerCritSect.Enter();
if (_vieEncoder)
{
_vieEncoder->DeRegisterExternalEncoder(_codec.plType);
}
_providerCritSect.Leave();
// Stop the camera input
if (_captureModule)
{
_moduleProcessThread.DeRegisterModule(_captureModule);
_captureModule->DeRegisterCaptureDataCallback();
}
if (_vieCaptureThread.Stop())
{
// Thread stopped
delete &_vieCaptureThread;
delete &_vieCaptureEvent;
delete &_vieDeliverEvent;
} else
{
assert(false);
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoRenderer, ViEId(_engineId, _captureId),
"%s: Not able to stop capture thread for device %d, leaking",
__FUNCTION__, _captureId);
// Not possible to stop the thread, leak it...
}
if (!_useExternalModule)
{
VideoCaptureModule::Destroy(_captureModule);
}
_captureModule = NULL;
if (_imageProcModule)
{
VideoProcessingModule::Destroy(_imageProcModule);
}
if (_deflickerFrameStats)
{
delete _deflickerFrameStats;
_deflickerFrameStats = NULL;
}
delete _brightnessFrameStats;
if (_vcm)
{
delete _vcm;
}
delete &_captureCritsect;
delete &_deliverCritsect;
delete &_encodingCritsect;
delete &_observerCritsect;
}
// ----------------------------------------------------------------------------
// Static factory class
// ----------------------------------------------------------------------------
ViECapturer* ViECapturer::CreateViECapture(int captureId,
int engineId,
VideoCaptureModule& captureModule,
ProcessThread& moduleProcessThread)
{
ViECapturer* capture = new ViECapturer(captureId, engineId,
moduleProcessThread);
if (!capture || capture->Init(captureModule) != 0)
{
delete capture;
capture = NULL;
}
return capture;
}
WebRtc_Word32 ViECapturer::Init(VideoCaptureModule& captureModule)
{
_captureModule = &captureModule;
_useExternalModule = true;
_captureModule->RegisterCaptureDataCallback(*this);
if (_moduleProcessThread.RegisterModule(_captureModule) != 0)
{
return -1;
}
return 0;
}
ViECapturer* ViECapturer::CreateViECapture(int captureId,
int engineId,
const WebRtc_UWord8* deviceUniqueIdUTF8,
const WebRtc_UWord32 deviceUniqueIdUTF8Length,
ProcessThread& moduleProcessThread)
{
ViECapturer* capture = new ViECapturer(captureId, engineId, moduleProcessThread);
if (!capture ||
capture->Init(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length) != 0)
{
delete capture;
capture = NULL;
}
return capture;
}
WebRtc_Word32 ViECapturer::Init(const WebRtc_UWord8* deviceUniqueIdUTF8,
const WebRtc_UWord32 deviceUniqueIdUTF8Length)
{
#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER
if (deviceUniqueIdUTF8 == NULL)
{
_captureModule = VideoCaptureModule::Create(
ViEModuleId(_engineId, _captureId),
_externalCaptureModule);
} else
{
_captureModule = VideoCaptureModule::Create(
ViEModuleId(_engineId, _captureId),
deviceUniqueIdUTF8);
}
#endif
if (!_captureModule)
return -1;
_captureModule->RegisterCaptureDataCallback(*this);
if (_moduleProcessThread.RegisterModule(_captureModule) != 0)
{
return -1;
}
return 0;
}
// ----------------------------------------------------------------------------
// FrameCallbackChanged
// ----------------------------------------------------------------------------
int ViECapturer::FrameCallbackChanged()
{
if (Started() && !EncoderActive() && !CaptureCapabilityFixed()) // Reconfigure the camera if a new size is required and the capture device does not provide encoded frames.
{
int bestWidth;
int bestHeight;
int bestFrameRate;
VideoCaptureCapability captureSettings;
_captureModule->CaptureSettings(captureSettings);
GetBestFormat(bestWidth, bestHeight, bestFrameRate);
if (bestWidth != 0 && bestHeight != 0 && bestFrameRate != 0)
{
if (bestWidth != captureSettings.width ||
bestHeight != captureSettings.height ||
bestFrameRate != captureSettings.maxFPS ||
captureSettings.codecType != kVideoCodecUnknown)
{
Stop();
Start(_requestedCapability);
}
}
}
return 0;
}
// ----------------------------------------------------------------------------
// Start
//
// Starts the capture device.
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::Start(const CaptureCapability captureCapability)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s", __FUNCTION__);
int width;
int height;
int frameRate;
VideoCaptureCapability capability;
_requestedCapability = captureCapability;
if (EncoderActive())
{
CriticalSectionScoped cs(_encodingCritsect);
capability.width = _codec.width;
capability.height = _codec.height;
capability.maxFPS = _codec.maxFramerate;
capability.codecType = _codec.codecType;
capability.rawType = kVideoI420;
} else if (!CaptureCapabilityFixed())
{
GetBestFormat(width, height, frameRate); // Ask the observers for best size
if (width == 0)
{
width = kViECaptureDefaultWidth;
}
if (height == 0)
{
height = kViECaptureDefaultHeight;
}
if (frameRate == 0)
{
frameRate = kViECaptureDefaultFramerate;
}
capability.height = height;
capability.width = width;
capability.maxFPS = frameRate;
capability.rawType = kVideoI420;
capability.codecType = kVideoCodecUnknown;
} else // Width and heigh and type specified with call to Start - not set by observers.
{
capability.width = _requestedCapability.width;
capability.height = _requestedCapability.height;
capability.maxFPS = _requestedCapability.maxFPS;
capability.rawType = _requestedCapability.rawType;
capability.interlaced = _requestedCapability.interlaced;
}
return _captureModule->StartCapture(capability);
}
// ----------------------------------------------------------------------------
// Stop
//
// Stops the capture device
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::Stop()
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s", __FUNCTION__);
_requestedCapability = CaptureCapability();
return _captureModule->StopCapture();
}
// ----------------------------------------------------------------------------
// Started
//
// Returns true if the capture device is started, false otherwise
// ----------------------------------------------------------------------------
bool ViECapturer::Started()
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s", __FUNCTION__);
return _captureModule->CaptureStarted();
}
const WebRtc_UWord8* ViECapturer::CurrentDeviceName() const
{
return _captureModule->CurrentDeviceName();
}
// ----------------------------------------------------------------------------
// SetCaptureDelay
//
// Overrides the capture delay
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::SetCaptureDelay(WebRtc_Word32 delayMs)
{
return _captureModule->SetCaptureDelay(delayMs);
}
// ----------------------------------------------------------------------------
// SetCapturedFrameRotation
//
// Tell the capture module whether or not to rotate a frame when it is captured
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::SetRotateCapturedFrames(
const RotateCapturedFrame rotation)
{
VideoCaptureRotation convertedRotation = kCameraRotate0;
switch (rotation)
{
case RotateCapturedFrame_0:
convertedRotation = kCameraRotate0;
break;
case RotateCapturedFrame_90:
convertedRotation = kCameraRotate90;
break;
case RotateCapturedFrame_180:
convertedRotation = kCameraRotate180;
break;
case RotateCapturedFrame_270:
convertedRotation = kCameraRotate270;
break;
default:
break;
}
return _captureModule->SetCaptureRotation(convertedRotation);
}
// ----------------------------------------------------------------------------
// IncomingFrame
//
// Inherited from ExternalCapture, will deliver a new captured
// frame to the capture module.
// ----------------------------------------------------------------------------
int ViECapturer::IncomingFrame(unsigned char* videoFrame,
unsigned int videoFrameLength,
unsigned short width, unsigned short height,
RawVideoType videoType,
unsigned long long captureTime)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%ExternalCapture::IncomingFrame width %d, height %d, captureTime %u",
width, height, captureTime);
if (!_externalCaptureModule)
{
return -1;
}
VideoCaptureCapability capability;
capability.width = width;
capability.height = height;
capability.rawType = videoType;
return _externalCaptureModule->IncomingFrame(videoFrame, videoFrameLength,
capability, captureTime);
}
// ----------------------------------------------------------------------------
// OnIncomingCapturedFrame
//
// Inherited from VideoCaptureDataCallback, will deliver a new captured
// frame
// ----------------------------------------------------------------------------
void ViECapturer::OnIncomingCapturedFrame(const WebRtc_Word32 captureId,
VideoFrame& videoFrame,
VideoCodecType codecType)
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureId: %d)", __FUNCTION__, captureId);
CriticalSectionScoped cs(_captureCritsect);
if (codecType != kVideoCodecUnknown)
{
if (_encodedFrame.Length() != 0) // The last encoded frame has not been sent yet. Need to wait
{
_vieDeliverEvent.Reset();
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureId: %d) Last encoded frame not yet delivered.",
__FUNCTION__, captureId);
_captureCritsect.Leave();
_vieDeliverEvent.Wait(500); // Wait 500ms for the coded frame to be sent before unblocking this.
assert(_encodedFrame.Length()==0);
_captureCritsect.Enter();
}
_encodedFrame.SwapFrame(videoFrame);
} else
{
_capturedFrame.SwapFrame(videoFrame);
}
_vieCaptureEvent.Set();
return;
}
void ViECapturer::OnCaptureDelayChanged(const WebRtc_Word32 id,
const WebRtc_Word32 delay)
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo,
ViEId(_engineId, _captureId),
"%s(captureId: %d) delay %d", __FUNCTION__, _captureId,
delay);
// Deliver the network delay to all registered callbacks
ViEFrameProviderBase::SetFrameDelay(delay);
CriticalSectionScoped cs(_encodingCritsect);
if (_vieEncoder)
{
_vieEncoder->DelayChanged(id, delay);
}
}
WebRtc_Word32 ViECapturer::RegisterEffectFilter(ViEEffectFilter* effectFilter)
{
CriticalSectionScoped cs(_deliverCritsect);
if (effectFilter == NULL)
{
if (_effectFilter == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: no effect filter added for capture device %d",
__FUNCTION__, _captureId);
return -1;
}
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId,_captureId),
"%s: deregister effect filter for device %d", __FUNCTION__,
_captureId);
} else
{
if (_effectFilter)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId,_captureId),
"%s: effect filter already added for capture device %d",
__FUNCTION__, _captureId);
return -1;
}
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: register effect filter for device %d", __FUNCTION__,
_captureId);
}
_effectFilter = effectFilter;
return 0;
}
//------------------------------------------------------------------------------------------------
//
// IncImageProcRefCount
// Help function used for keeping track of VideoImageProcesingModule. Creates the module if it is needed.
// Return 0 on success and guarantee that the image proc module exist.
//------------------------------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::IncImageProcRefCount()
{
if (!_imageProcModule)
{
assert(_imageProcModuleRefCounter==0);
_imageProcModule = VideoProcessingModule::Create(ViEModuleId(_engineId, _captureId));
if (_imageProcModule == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: could not create video processing module",
__FUNCTION__);
return -1;
}
}
_imageProcModuleRefCounter++;
return 0;
}
WebRtc_Word32 ViECapturer::DecImageProcRefCount()
{
_imageProcModuleRefCounter--;
// Destroy module
if (_imageProcModuleRefCounter == 0)
{
VideoProcessingModule::Destroy(_imageProcModule);
_imageProcModule = NULL;
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableDenoising(bool enable)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d, enable: %d)", __FUNCTION__,
_captureId, enable);
CriticalSectionScoped cs(_deliverCritsect);
if (enable)
{
// Sanity check
if (_denoisingEnabled)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: denoising already enabled", __FUNCTION__);
return -1;
}
_denoisingEnabled = true;
if (IncImageProcRefCount() != 0)
{
return -1;
}
} else
{
// Sanity check
if (_denoisingEnabled == false)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: denoising not enabled", __FUNCTION__);
return -1;
}
_denoisingEnabled = false;
DecImageProcRefCount();
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableDeflickering(bool enable)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d, enable: %d)", __FUNCTION__,
_captureId, enable);
CriticalSectionScoped cs(_deliverCritsect);
if (enable)
{
// Sanity check
if (_deflickerFrameStats)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: deflickering already enabled", __FUNCTION__);
return -1;
}
// Create the module
if (IncImageProcRefCount() != 0)
{
return -1;
}
_deflickerFrameStats = new VideoProcessingModule::FrameStats();
} else
{
// Sanity check
if (_deflickerFrameStats == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: deflickering not enabled", __FUNCTION__);
return -1;
}
// Destroy module
DecImageProcRefCount();
delete _deflickerFrameStats;
_deflickerFrameStats = NULL;
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableBrightnessAlarm(bool enable)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d, enable: %d)", __FUNCTION__,
_captureId, enable);
CriticalSectionScoped cs(_deliverCritsect);
if (enable)
{
// Sanity check
if (_brightnessFrameStats)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: BrightnessAlarm already enabled", __FUNCTION__);
return -1;
}
if (IncImageProcRefCount() != 0)
{
return -1;
}
_brightnessFrameStats = new VideoProcessingModule::FrameStats();
} else
{
DecImageProcRefCount();
// Sanity check
if (_brightnessFrameStats == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: deflickering not enabled", __FUNCTION__);
return -1;
}
delete _brightnessFrameStats;
_brightnessFrameStats = NULL;
}
return 0;
}
bool ViECapturer::ViECaptureThreadFunction(void* obj)
{
return static_cast<ViECapturer*> (obj)->ViECaptureProcess();
}
bool ViECapturer::ViECaptureProcess()
{
if (_vieCaptureEvent.Wait(kThreadWaitTimeMs) == kEventSignaled)
{
_deliverCritsect.Enter();
if (_capturedFrame.Length() > 0) // New I420 frame
{
_captureCritsect.Enter();
_deliverFrame.SwapFrame(_capturedFrame);
_capturedFrame.SetLength(0);
_captureCritsect.Leave();
DeliverI420Frame(_deliverFrame);
}
if (_encodedFrame.Length() > 0)
{
_captureCritsect.Enter();
_deliverFrame.SwapFrame(_encodedFrame);
_encodedFrame.SetLength(0);
_vieDeliverEvent.Set();
_captureCritsect.Leave();
DeliverCodedFrame(_deliverFrame);
}
_deliverCritsect.Leave();
if (_currentBrightnessLevel != _reportedBrightnessLevel)
{
CriticalSectionScoped cs(_observerCritsect);
if (_observer)
{
_observer->BrightnessAlarm(_id, _currentBrightnessLevel);
_reportedBrightnessLevel = _currentBrightnessLevel;
}
}
}
// We're done!
return true;
}
void ViECapturer::DeliverI420Frame(VideoFrame& videoFrame)
{
// Apply image enhancement and effect filter
if (_deflickerFrameStats)
{
if (_imageProcModule->GetFrameStats(*_deflickerFrameStats, videoFrame) == 0)
{
_imageProcModule->Deflickering(videoFrame, *_deflickerFrameStats);
} else
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s: could not get frame stats for captured frame", __FUNCTION__);
}
}
if (_denoisingEnabled)
{
_imageProcModule->Denoising(videoFrame);
}
if (_brightnessFrameStats)
{
if (_imageProcModule->GetFrameStats (*_brightnessFrameStats, videoFrame) == 0)
{
WebRtc_Word32 brightness = _imageProcModule->BrightnessDetection(
videoFrame,
*_brightnessFrameStats);
switch (brightness)
{
case VideoProcessingModule::kNoWarning:
_currentBrightnessLevel = Normal;
break;
case VideoProcessingModule::kDarkWarning:
_currentBrightnessLevel = Dark;
break;
case VideoProcessingModule::kBrightWarning:
_currentBrightnessLevel = Bright;
break;
default:
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo,
ViEId(_engineId, _captureId),
"%s: Brightness detection failed", __FUNCTION__);
}
}
}
if (_effectFilter)
{
_effectFilter->Transform(videoFrame.Length(), videoFrame.Buffer(),
videoFrame.TimeStamp(), videoFrame.Width(),
videoFrame.Height());
}
// Deliver the captured frame to all observers (channels,renderer or file)
ViEFrameProviderBase::DeliverFrame(videoFrame);
}
void ViECapturer::DeliverCodedFrame(VideoFrame& videoFrame)
{
if (_encodeCompleteCallback)
{
EncodedImage encodedImage(videoFrame.Buffer(),
videoFrame.Length(),
videoFrame.Size());
encodedImage._timeStamp = 90*(WebRtc_UWord32) videoFrame.RenderTimeMs();
_encodeCompleteCallback->Encoded(encodedImage);
}
if (NumberOfRegistersFrameCallbacks() > 0 && _decoderInitialized)
{
videoFrame.Swap(_decodeBuffer.payloadData, _decodeBuffer.bufferSize,
_decodeBuffer.payloadSize);
_decodeBuffer.encodedHeight = videoFrame.Height();
_decodeBuffer.encodedWidth = videoFrame.Width();
_decodeBuffer.renderTimeMs = videoFrame.RenderTimeMs();
_decodeBuffer.timeStamp = 90*(WebRtc_UWord32) videoFrame.RenderTimeMs();
_decodeBuffer.payloadType = _codec.plType;
_vcm->DecodeFromStorage(_decodeBuffer);
}
}
/*
* DeregisterFrameCallback - Overrides ViEFrameProvider
*/
int ViECapturer::DeregisterFrameCallback(const ViEFrameCallback* callbackObject)
{
_providerCritSect.Enter();
if (callbackObject == _vieEncoder) //Don't use this camera as encoder anymore. Need to tell the ViEEncoder.
{
ViEEncoder* vieEncoder = NULL;
vieEncoder = _vieEncoder;
_vieEncoder = NULL;
_providerCritSect.Leave();
_deliverCritsect.Enter(); //Need to take this here in order to avoid deadlock with VCM. The reason is that VCM will call ::Release and a deadlock can occure.
vieEncoder->DeRegisterExternalEncoder(_codec.plType);
_deliverCritsect.Leave();
return 0;
}
_providerCritSect.Leave();
return ViEFrameProviderBase::DeregisterFrameCallback(callbackObject);
}
/*
* IsFrameCallbackRegistered - Overrides ViEFrameProvider
*/
bool ViECapturer::IsFrameCallbackRegistered(const ViEFrameCallback* callbackObject)
{
CriticalSectionScoped cs(_providerCritSect);
if (callbackObject == _vieEncoder)
{
return true;
}
return ViEFrameProviderBase::IsFrameCallbackRegistered(callbackObject);
}
// ----------------------------------------------------------------------------
// External encoder using attached capture device.
// Implements VideoEncoderInterface
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::PreEncodeToViEEncoder(const VideoCodec& codec,
ViEEncoder& vieEncoder,
WebRtc_Word32 vieEncoderId)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
{
if (_vieEncoder && &vieEncoder != _vieEncoder)
{
WEBRTC_TRACE( webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d Capture device already encoding)",
__FUNCTION__, _captureId);
return -1;
}
}
CriticalSectionScoped cs(_encodingCritsect);
VideoCaptureModule::VideoCaptureEncodeInterface* captureEncoder =
_captureModule->GetEncodeInterface(codec);
if (!captureEncoder)
return -1; // Encoding not supported?
_captureEncoder = captureEncoder;
// Create VCM module used for decoding frames if needed.
if (!_vcm)
{
_vcm = VideoCodingModule::Create(_captureId);
}
if (vieEncoder.RegisterExternalEncoder(this, codec.plType) != 0)
{
return -1;
}
if (vieEncoder.SetEncoder(codec) != 0)
{
vieEncoder.DeRegisterExternalEncoder(codec.plType);
return -1;
}
// Make sure the encoder is not an I420 observer.
ViEFrameProviderBase::DeregisterFrameCallback(&vieEncoder);
_vieEncoder = &vieEncoder; // Store the vieEncoder that is using this capture device.
_vieEncoderId = vieEncoderId;
memcpy(&_codec, &codec, sizeof(webrtc::VideoCodec));
return 0;
}
bool ViECapturer::EncoderActive()
{
return _vieEncoder != NULL;
}
/*
* CaptureCapabilityFixed
* Returns true if width, height and framerate was specified when the Start() was called.
*/
bool ViECapturer::CaptureCapabilityFixed()
{
return _requestedCapability.width != 0 && _requestedCapability.height != 0
&& _requestedCapability.maxFPS != 0;
}
// ----------------------------------------------------------------------------
// Implements VideoEncoder
//
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::Version(WebRtc_Word8 *version, WebRtc_Word32 length) const
{
return 0;
}
WebRtc_Word32 ViECapturer::InitEncode(const VideoCodec* codecSettings,
WebRtc_Word32 numberOfCores,
WebRtc_UWord32 maxPayloadSize)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
CriticalSectionScoped cs(_encodingCritsect);
if (!_captureEncoder || !codecSettings)
return WEBRTC_VIDEO_CODEC_ERROR;
if (_vcm) // Initialize VCM to be able to decode frames if needed.
{
if (_vcm->InitializeReceiver() == 0)
{
if (_vcm->RegisterReceiveCallback(this) == 0)
{
if (_vcm->RegisterReceiveCodec(codecSettings, numberOfCores,
false) == 0)
{
_decoderInitialized = true;
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d) VCM Decoder initialized",
__FUNCTION__, _captureId);
}
}
}
}
return _captureEncoder->ConfigureEncoder(*codecSettings, maxPayloadSize);
}
/*
* Encode
* Orders the Capture device to create a certain frame type.
*/
WebRtc_Word32 ViECapturer::Encode(const RawImage& inputImage,
const void* codecSpecificInfo, /*= NULL,*/
VideoFrameType frameType /*= kDeltaFrame*/)
{
CriticalSectionScoped cs(_encodingCritsect);
if (!_captureEncoder)
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
if (frameType == kKeyFrame)
return _captureEncoder->EncodeFrameType(kVideoFrameKey);
if (frameType == kSkipFrame)
return _captureEncoder->EncodeFrameType(kFrameEmpty);
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
WebRtc_Word32 ViECapturer::RegisterEncodeCompleteCallback( EncodedImageCallback* callback)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
CriticalSectionScoped cs(_deliverCritsect);
if (!_captureEncoder)
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
_encodeCompleteCallback = callback;
return 0;
}
WebRtc_Word32 ViECapturer::Release()
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
{
CriticalSectionScoped cs(_deliverCritsect);
_encodeCompleteCallback = NULL;
}
{
CriticalSectionScoped cs(_encodingCritsect);
_decoderInitialized = false;
_codec.codecType = kVideoCodecUnknown;
_captureEncoder->ConfigureEncoder(_codec, 0); // Reset the camera to output I420.
if (_vieEncoder) // Need to add the encoder as an observer of I420.
{
ViEFrameProviderBase::RegisterFrameCallback(_vieEncoderId,
_vieEncoder);
}
_vieEncoder = NULL;
}
return 0;
}
/*
* Reset
* Should reset the capture device to the state it was in after the InitEncode function.
* Current implementation do nothing.
*/
WebRtc_Word32 ViECapturer::Reset()
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
return 0;
}
WebRtc_Word32 ViECapturer::SetPacketLoss(WebRtc_UWord32 packetLoss)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
CriticalSectionScoped cs(_encodingCritsect);
if (!_captureEncoder)
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
return _captureEncoder->SetPacketLoss(packetLoss);
}
WebRtc_Word32 ViECapturer::SetRates(WebRtc_UWord32 newBitRate,
WebRtc_UWord32 frameRate)
{
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s(captureDeviceId: %d)", __FUNCTION__, _captureId);
CriticalSectionScoped cs(_encodingCritsect);
if (!_captureEncoder)
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
return _captureEncoder->SetRates(newBitRate, frameRate);
}
/*
* FrameToRender - implements VCMReceiveCallback.
* (VCM decode callback) Used in order to be able to provide I420 frames to renderer etc.
*/
WebRtc_Word32 ViECapturer::FrameToRender(VideoFrame& videoFrame)
{
_deliverCritsect.Enter();
DeliverI420Frame(videoFrame);
_deliverCritsect.Leave();
return 0;
}
/******************************************************************************/
//
// Statistics Observer functions
//
WebRtc_Word32 ViECapturer::RegisterObserver(ViECaptureObserver& observer)
{
if (_observer)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId,
_captureId),
"%s Observer already registered", __FUNCTION__,
_captureId);
return -1;
}
if (_captureModule->RegisterCaptureCallback(*this) != 0)
{
return -1;
}
_captureModule->EnableFrameRateCallback(true);
_captureModule->EnableNoPictureAlarm(true);
_observer = &observer;
return 0;
}
WebRtc_Word32 ViECapturer::DeRegisterObserver()
{
CriticalSectionScoped cs(_observerCritsect);
if (!_observer)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, _captureId),
"%s No observer registered", __FUNCTION__, _captureId);
return -1;
}
_captureModule->EnableFrameRateCallback(false);
_captureModule->EnableNoPictureAlarm(false);
_captureModule->DeRegisterCaptureCallback();
_observer = NULL;
return 0;
}
bool ViECapturer::IsObserverRegistered()
{
CriticalSectionScoped cs(_observerCritsect);
return _observer != NULL;
}
// ----------------------------------------------------------------------------
// Implements VideoCaptureFeedBack
//
// ----------------------------------------------------------------------------
void ViECapturer::OnCaptureFrameRate(const WebRtc_Word32 id,
const WebRtc_UWord32 frameRate)
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo,
ViEId(_engineId, _captureId), "OnCaptureFrameRate %d",
frameRate);
CriticalSectionScoped cs(_observerCritsect);
_observer->CapturedFrameRate(_id, (WebRtc_UWord8) frameRate);
}
void ViECapturer::OnNoPictureAlarm(const WebRtc_Word32 id,
const VideoCaptureAlarm alarm)
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo,
ViEId(_engineId, _captureId), "OnNoPictureAlarm %d", alarm);
CriticalSectionScoped cs(_observerCritsect);
CaptureAlarm vieAlarm = (alarm == Raised) ? AlarmRaised : AlarmCleared;
_observer->NoPictureAlarm(id, vieAlarm);
}
// ----------------------------------------------------------------------------
WebRtc_Word32 ViECapturer::SetCaptureDeviceImage(const VideoFrame& captureDeviceImage)
{
return _captureModule->StartSendImage(captureDeviceImage, 10);
}
} // namespace webrtc