blob: cd9bd0c159e6a5d40f8d23ee433accaf312ed3ee [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_engine/vie_capturer.h"
#include "modules/interface/module_common_types.h"
#include "modules/utility/interface/process_thread.h"
#include "modules/video_capture/main/interface/video_capture_factory.h"
#include "modules/video_processing/main/interface/video_processing.h"
#include "modules/video_render/main/interface/video_render_defines.h"
#include "system_wrappers/interface/critical_section_wrapper.h"
#include "system_wrappers/interface/event_wrapper.h"
#include "system_wrappers/interface/thread_wrapper.h"
#include "system_wrappers/interface/trace.h"
#include "video_engine/include/vie_image_process.h"
#include "video_engine/vie_defines.h"
#include "video_engine/vie_encoder.h"
namespace webrtc {
const int kThreadWaitTimeMs = 100;
const int kMaxDeliverWaitTime = 500;
ViECapturer::ViECapturer(int capture_id,
int engine_id,
ProcessThread& module_process_thread)
: ViEFrameProviderBase(capture_id, engine_id),
capture_cs_(CriticalSectionWrapper::CreateCriticalSection()),
deliver_cs_(CriticalSectionWrapper::CreateCriticalSection()),
capture_module_(NULL),
external_capture_module_(NULL),
module_process_thread_(module_process_thread),
capture_id_(capture_id),
capture_thread_(*ThreadWrapper::CreateThread(ViECaptureThreadFunction,
this, kHighPriority,
"ViECaptureThread")),
capture_event_(*EventWrapper::Create()),
deliver_event_(*EventWrapper::Create()),
effect_filter_(NULL),
image_proc_module_(NULL),
image_proc_module_ref_counter_(0),
deflicker_frame_stats_(NULL),
brightness_frame_stats_(NULL),
current_brightness_level_(Normal),
reported_brightness_level_(Normal),
denoising_enabled_(false),
observer_cs_(CriticalSectionWrapper::CreateCriticalSection()),
observer_(NULL),
encoding_cs_(CriticalSectionWrapper::CreateCriticalSection()),
capture_encoder_(NULL),
encode_complete_callback_(NULL),
vie_encoder_(NULL),
vcm_(NULL),
decoder_initialized_(false) {
WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, capture_id),
"ViECapturer::ViECapturer(capture_id: %d, engine_id: %d)",
capture_id, engine_id);
unsigned int t_id = 0;
if (capture_thread_.Start(t_id)) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id, capture_id),
"%s: thread started: %u", __FUNCTION__, t_id);
} else {
assert(false);
}
}
ViECapturer::~ViECapturer() {
WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_, capture_id_),
"ViECapturer::~ViECapturer() - capture_id: %d, engine_id: %d",
capture_id_, engine_id_);
// Stop the thread.
deliver_cs_->Enter();
capture_cs_->Enter();
capture_thread_.SetNotAlive();
capture_event_.Set();
capture_cs_->Leave();
deliver_cs_->Leave();
provider_cs_->Enter();
if (vie_encoder_) {
vie_encoder_->DeRegisterExternalEncoder(codec_.plType);
}
provider_cs_->Leave();
// Stop the camera input.
if (capture_module_) {
module_process_thread_.DeRegisterModule(capture_module_);
capture_module_->DeRegisterCaptureDataCallback();
capture_module_->Release();
capture_module_ = NULL;
}
if (capture_thread_.Stop()) {
// Thread stopped.
delete &capture_thread_;
delete &capture_event_;
delete &deliver_event_;
} else {
assert(false);
WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer,
ViEId(engine_id_, capture_id_),
"%s: Not able to stop capture thread for device %d, leaking",
__FUNCTION__, capture_id_);
}
if (image_proc_module_) {
VideoProcessingModule::Destroy(image_proc_module_);
}
if (deflicker_frame_stats_) {
delete deflicker_frame_stats_;
deflicker_frame_stats_ = NULL;
}
delete brightness_frame_stats_;
if (vcm_) {
delete vcm_;
}
}
ViECapturer* ViECapturer::CreateViECapture(
int capture_id,
int engine_id,
VideoCaptureModule& capture_module,
ProcessThread& module_process_thread) {
ViECapturer* capture = new ViECapturer(capture_id, engine_id,
module_process_thread);
if (!capture || capture->Init(capture_module) != 0) {
delete capture;
capture = NULL;
}
return capture;
}
WebRtc_Word32 ViECapturer::Init(VideoCaptureModule& capture_module) {
assert(capture_module_ == NULL);
capture_module_ = &capture_module;
capture_module_->RegisterCaptureDataCallback(*this);
capture_module_->AddRef();
if (module_process_thread_.RegisterModule(capture_module_) != 0) {
return -1;
}
return 0;
}
ViECapturer* ViECapturer::CreateViECapture(
int capture_id,
int engine_id,
const WebRtc_UWord8* device_unique_idUTF8,
const WebRtc_UWord32 device_unique_idUTF8Length,
ProcessThread& module_process_thread) {
ViECapturer* capture = new ViECapturer(capture_id, engine_id,
module_process_thread);
if (!capture ||
capture->Init(device_unique_idUTF8, device_unique_idUTF8Length) != 0) {
delete capture;
capture = NULL;
}
return capture;
}
WebRtc_Word32 ViECapturer::Init(
const WebRtc_UWord8* device_unique_idUTF8,
const WebRtc_UWord32 device_unique_idUTF8Length) {
assert(capture_module_ == NULL);
if (device_unique_idUTF8 == NULL) {
capture_module_ = VideoCaptureFactory::Create(
ViEModuleId(engine_id_, capture_id_), external_capture_module_);
} else {
capture_module_ = VideoCaptureFactory::Create(
ViEModuleId(engine_id_, capture_id_), device_unique_idUTF8);
}
if (!capture_module_) {
return -1;
}
capture_module_->AddRef();
capture_module_->RegisterCaptureDataCallback(*this);
if (module_process_thread_.RegisterModule(capture_module_) != 0) {
return -1;
}
return 0;
}
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 best_width;
int best_height;
int best_frame_rate;
VideoCaptureCapability capture_settings;
capture_module_->CaptureSettings(capture_settings);
GetBestFormat(best_width, best_height, best_frame_rate);
if (best_width != 0 && best_height != 0 && best_frame_rate != 0) {
if (best_width != capture_settings.width ||
best_height != capture_settings.height ||
best_frame_rate != capture_settings.maxFPS ||
capture_settings.codecType != kVideoCodecUnknown) {
Stop();
Start(requested_capability_);
}
}
}
return 0;
}
WebRtc_Word32 ViECapturer::Start(const CaptureCapability& capture_capability) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s",
__FUNCTION__);
int width;
int height;
int frame_rate;
VideoCaptureCapability capability;
requested_capability_ = capture_capability;
if (EncoderActive()) {
CriticalSectionScoped cs(encoding_cs_.get());
capability.width = codec_.width;
capability.height = codec_.height;
capability.maxFPS = codec_.maxFramerate;
capability.codecType = codec_.codecType;
capability.rawType = kVideoI420;
} else if (!CaptureCapabilityFixed()) {
// Ask the observers for best size.
GetBestFormat(width, height, frame_rate);
if (width == 0) {
width = kViECaptureDefaultWidth;
}
if (height == 0) {
height = kViECaptureDefaultHeight;
}
if (frame_rate == 0) {
frame_rate = kViECaptureDefaultFramerate;
}
capability.height = height;
capability.width = width;
capability.maxFPS = frame_rate;
capability.rawType = kVideoI420;
capability.codecType = kVideoCodecUnknown;
} else {
// Width, height and type specified with call to Start, not set by
// observers.
capability.width = requested_capability_.width;
capability.height = requested_capability_.height;
capability.maxFPS = requested_capability_.maxFPS;
capability.rawType = requested_capability_.rawType;
capability.interlaced = requested_capability_.interlaced;
}
return capture_module_->StartCapture(capability);
}
WebRtc_Word32 ViECapturer::Stop() {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s",
__FUNCTION__);
requested_capability_ = CaptureCapability();
return capture_module_->StopCapture();
}
bool ViECapturer::Started() {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s",
__FUNCTION__);
return capture_module_->CaptureStarted();
}
const WebRtc_UWord8* ViECapturer::CurrentDeviceName() const {
return capture_module_->CurrentDeviceName();
}
WebRtc_Word32 ViECapturer::SetCaptureDelay(WebRtc_Word32 delay_ms) {
return capture_module_->SetCaptureDelay(delay_ms);
}
WebRtc_Word32 ViECapturer::SetRotateCapturedFrames(
const RotateCapturedFrame rotation) {
VideoCaptureRotation converted_rotation = kCameraRotate0;
switch (rotation) {
case RotateCapturedFrame_0:
converted_rotation = kCameraRotate0;
break;
case RotateCapturedFrame_90:
converted_rotation = kCameraRotate90;
break;
case RotateCapturedFrame_180:
converted_rotation = kCameraRotate180;
break;
case RotateCapturedFrame_270:
converted_rotation = kCameraRotate270;
break;
}
return capture_module_->SetCaptureRotation(converted_rotation);
}
int ViECapturer::IncomingFrame(unsigned char* video_frame,
unsigned int video_frame_length,
unsigned short width,
unsigned short height,
RawVideoType video_type,
unsigned long long capture_time) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"ExternalCapture::IncomingFrame width %d, height %d, "
"capture_time %u", width, height, capture_time);
if (!external_capture_module_) {
return -1;
}
VideoCaptureCapability capability;
capability.width = width;
capability.height = height;
capability.rawType = video_type;
return external_capture_module_->IncomingFrame(video_frame,
video_frame_length,
capability, capture_time);
}
int ViECapturer::IncomingFrameI420(const ViEVideoFrameI420& video_frame,
unsigned long long capture_time) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"ExternalCapture::IncomingFrame width %d, height %d, "
" capture_time %u", video_frame.width, video_frame.height,
capture_time);
if (!external_capture_module_) {
return -1;
}
VideoFrameI420 frame;
frame.width = video_frame.width;
frame.height = video_frame.height;
frame.y_plane = video_frame.y_plane;
frame.u_plane = video_frame.u_plane;
frame.v_plane = video_frame.v_plane;
frame.y_pitch = video_frame.y_pitch;
frame.u_pitch = video_frame.u_pitch;
frame.v_pitch = video_frame.v_pitch;
return external_capture_module_->IncomingFrameI420(frame, capture_time);
}
void ViECapturer::OnIncomingCapturedFrame(const WebRtc_Word32 capture_id,
VideoFrame& video_frame,
VideoCodecType codec_type) {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_id: %d)", __FUNCTION__, capture_id);
CriticalSectionScoped cs(capture_cs_.get());
if (codec_type != kVideoCodecUnknown) {
if (encoded_frame_.Length() != 0) {
// The last encoded frame has not been sent yet. Need to wait.
deliver_event_.Reset();
WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_id: %d) Last encoded frame not yet delivered.",
__FUNCTION__, capture_id);
capture_cs_->Leave();
// Wait for the coded frame to be sent before unblocking this.
deliver_event_.Wait(kMaxDeliverWaitTime);
assert(encoded_frame_.Length() == 0);
capture_cs_->Enter();
}
encoded_frame_.SwapFrame(video_frame);
} else {
captured_frame_.SwapFrame(video_frame);
}
capture_event_.Set();
return;
}
void ViECapturer::OnCaptureDelayChanged(const WebRtc_Word32 id,
const WebRtc_Word32 delay) {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_id: %d) delay %d", __FUNCTION__, capture_id_,
delay);
// Deliver the network delay to all registered callbacks.
ViEFrameProviderBase::SetFrameDelay(delay);
CriticalSectionScoped cs(encoding_cs_.get());
if (vie_encoder_) {
vie_encoder_->DelayChanged(id, delay);
}
}
WebRtc_Word32 ViECapturer::RegisterEffectFilter(
ViEEffectFilter* effect_filter) {
CriticalSectionScoped cs(deliver_cs_.get());
if (!effect_filter) {
if (!effect_filter_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: no effect filter added for capture device %d",
__FUNCTION__, capture_id_);
return -1;
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: deregister effect filter for device %d", __FUNCTION__,
capture_id_);
} else {
if (effect_filter_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: effect filter already added for capture device %d",
__FUNCTION__, capture_id_);
return -1;
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: register effect filter for device %d", __FUNCTION__,
capture_id_);
}
effect_filter_ = effect_filter;
return 0;
}
WebRtc_Word32 ViECapturer::IncImageProcRefCount() {
if (!image_proc_module_) {
assert(image_proc_module_ref_counter_ == 0);
image_proc_module_ = VideoProcessingModule::Create(
ViEModuleId(engine_id_, capture_id_));
if (!image_proc_module_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: could not create video processing module",
__FUNCTION__);
return -1;
}
}
image_proc_module_ref_counter_++;
return 0;
}
WebRtc_Word32 ViECapturer::DecImageProcRefCount() {
image_proc_module_ref_counter_--;
if (image_proc_module_ref_counter_ == 0) {
// Destroy module.
VideoProcessingModule::Destroy(image_proc_module_);
image_proc_module_ = NULL;
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableDenoising(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d, enable: %d)", __FUNCTION__,
capture_id_, enable);
CriticalSectionScoped cs(deliver_cs_.get());
if (enable) {
if (denoising_enabled_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: denoising already enabled", __FUNCTION__);
return -1;
}
denoising_enabled_ = true;
if (IncImageProcRefCount() != 0) {
return -1;
}
} else {
if (denoising_enabled_ == false) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: denoising not enabled", __FUNCTION__);
return -1;
}
denoising_enabled_ = false;
DecImageProcRefCount();
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableDeflickering(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d, enable: %d)", __FUNCTION__,
capture_id_, enable);
CriticalSectionScoped cs(deliver_cs_.get());
if (enable) {
if (deflicker_frame_stats_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: deflickering already enabled", __FUNCTION__);
return -1;
}
if (IncImageProcRefCount() != 0) {
return -1;
}
deflicker_frame_stats_ = new VideoProcessingModule::FrameStats();
} else {
if (deflicker_frame_stats_ == NULL) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: deflickering not enabled", __FUNCTION__);
return -1;
}
DecImageProcRefCount();
delete deflicker_frame_stats_;
deflicker_frame_stats_ = NULL;
}
return 0;
}
WebRtc_Word32 ViECapturer::EnableBrightnessAlarm(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d, enable: %d)", __FUNCTION__,
capture_id_, enable);
CriticalSectionScoped cs(deliver_cs_.get());
if (enable) {
if (brightness_frame_stats_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: BrightnessAlarm already enabled", __FUNCTION__);
return -1;
}
if (IncImageProcRefCount() != 0) {
return -1;
}
brightness_frame_stats_ = new VideoProcessingModule::FrameStats();
} else {
DecImageProcRefCount();
if (brightness_frame_stats_ == NULL) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: deflickering not enabled", __FUNCTION__);
return -1;
}
delete brightness_frame_stats_;
brightness_frame_stats_ = NULL;
}
return 0;
}
bool ViECapturer::ViECaptureThreadFunction(void* obj) {
return static_cast<ViECapturer*>(obj)->ViECaptureProcess();
}
bool ViECapturer::ViECaptureProcess() {
if (capture_event_.Wait(kThreadWaitTimeMs) == kEventSignaled) {
deliver_cs_->Enter();
if (captured_frame_.Length() > 0) {
// New I420 frame.
capture_cs_->Enter();
deliver_frame_.SwapFrame(captured_frame_);
captured_frame_.SetLength(0);
capture_cs_->Leave();
DeliverI420Frame(deliver_frame_);
}
if (encoded_frame_.Length() > 0) {
capture_cs_->Enter();
deliver_frame_.SwapFrame(encoded_frame_);
encoded_frame_.SetLength(0);
deliver_event_.Set();
capture_cs_->Leave();
DeliverCodedFrame(deliver_frame_);
}
deliver_cs_->Leave();
if (current_brightness_level_ != reported_brightness_level_) {
CriticalSectionScoped cs(observer_cs_.get());
if (observer_) {
observer_->BrightnessAlarm(id_, current_brightness_level_);
reported_brightness_level_ = current_brightness_level_;
}
}
}
// We're done!
return true;
}
void ViECapturer::DeliverI420Frame(VideoFrame& video_frame) {
// Apply image enhancement and effect filter.
if (deflicker_frame_stats_) {
if (image_proc_module_->GetFrameStats(*deflicker_frame_stats_,
video_frame) == 0) {
image_proc_module_->Deflickering(video_frame, *deflicker_frame_stats_);
} else {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: could not get frame stats for captured frame",
__FUNCTION__);
}
}
if (denoising_enabled_) {
image_proc_module_->Denoising(video_frame);
}
if (brightness_frame_stats_) {
if (image_proc_module_->GetFrameStats(*brightness_frame_stats_,
video_frame) == 0) {
WebRtc_Word32 brightness = image_proc_module_->BrightnessDetection(
video_frame, *brightness_frame_stats_);
switch (brightness) {
case VideoProcessingModule::kNoWarning:
current_brightness_level_ = Normal;
break;
case VideoProcessingModule::kDarkWarning:
current_brightness_level_ = Dark;
break;
case VideoProcessingModule::kBrightWarning:
current_brightness_level_ = Bright;
break;
default:
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s: Brightness detection failed", __FUNCTION__);
}
}
}
if (effect_filter_) {
effect_filter_->Transform(video_frame.Length(), video_frame.Buffer(),
video_frame.TimeStamp(), video_frame.Width(),
video_frame.Height());
}
// Deliver the captured frame to all observers (channels, renderer or file).
ViEFrameProviderBase::DeliverFrame(video_frame);
}
void ViECapturer::DeliverCodedFrame(VideoFrame& video_frame) {
if (encode_complete_callback_) {
EncodedImage encoded_image(video_frame.Buffer(), video_frame.Length(),
video_frame.Size());
encoded_image._timeStamp = 90 * (WebRtc_UWord32) video_frame.RenderTimeMs();
encode_complete_callback_->Encoded(encoded_image);
}
if (NumberOfRegisteredFrameCallbacks() > 0 && decoder_initialized_) {
video_frame.Swap(decode_buffer_.payloadData, decode_buffer_.bufferSize,
decode_buffer_.payloadSize);
decode_buffer_.encodedHeight = video_frame.Height();
decode_buffer_.encodedWidth = video_frame.Width();
decode_buffer_.renderTimeMs = video_frame.RenderTimeMs();
decode_buffer_.timeStamp = 90 * (WebRtc_UWord32) video_frame.RenderTimeMs();
decode_buffer_.payloadType = codec_.plType;
vcm_->DecodeFromStorage(decode_buffer_);
}
}
int ViECapturer::DeregisterFrameCallback(
const ViEFrameCallback* callbackObject) {
provider_cs_->Enter();
if (callbackObject == vie_encoder_) {
// Don't use this camera as encoder anymore. Need to tell the ViEEncoder.
ViEEncoder* vie_encoder = NULL;
vie_encoder = vie_encoder_;
vie_encoder_ = NULL;
provider_cs_->Leave();
// Need to take this here in order to avoid deadlock with VCM. The reason is
// that VCM will call ::Release and a deadlock can occur.
deliver_cs_->Enter();
vie_encoder->DeRegisterExternalEncoder(codec_.plType);
deliver_cs_->Leave();
return 0;
}
provider_cs_->Leave();
return ViEFrameProviderBase::DeregisterFrameCallback(callbackObject);
}
bool ViECapturer::IsFrameCallbackRegistered(
const ViEFrameCallback* callbackObject) {
CriticalSectionScoped cs(provider_cs_.get());
if (callbackObject == vie_encoder_) {
return true;
}
return ViEFrameProviderBase::IsFrameCallbackRegistered(callbackObject);
}
WebRtc_Word32 ViECapturer::PreEncodeToViEEncoder(const VideoCodec& codec,
ViEEncoder& vie_encoder,
WebRtc_Word32 vie_encoder_id) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
if (vie_encoder_ && &vie_encoder != vie_encoder_) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d Capture device already encoding)",
__FUNCTION__, capture_id_);
return -1;
}
CriticalSectionScoped cs(encoding_cs_.get());
VideoCaptureModule::VideoCaptureEncodeInterface* capture_encoder =
capture_module_->GetEncodeInterface(codec);
if (!capture_encoder) {
// Encoding not supported?
return -1;
}
capture_encoder_ = capture_encoder;
// Create VCM module used for decoding frames if needed.
if (!vcm_) {
vcm_ = VideoCodingModule::Create(capture_id_);
}
if (vie_encoder.RegisterExternalEncoder(this, codec.plType) != 0) {
return -1;
}
if (vie_encoder.SetEncoder(codec) != 0) {
vie_encoder.DeRegisterExternalEncoder(codec.plType);
return -1;
}
// Make sure the encoder is not an I420 observer.
ViEFrameProviderBase::DeregisterFrameCallback(&vie_encoder);
// Store the vie_encoder using this capture device.
vie_encoder_ = &vie_encoder;
vie_encoder_id_ = vie_encoder_id;
memcpy(&codec_, &codec, sizeof(VideoCodec));
return 0;
}
bool ViECapturer::EncoderActive() {
return vie_encoder_ != NULL;
}
bool ViECapturer::CaptureCapabilityFixed() {
return requested_capability_.width != 0 &&
requested_capability_.height != 0 &&
requested_capability_.maxFPS != 0;
}
WebRtc_Word32 ViECapturer::Version(WebRtc_Word8* version,
WebRtc_Word32 length) const {
return 0;
}
WebRtc_Word32 ViECapturer::InitEncode(const VideoCodec* codec_settings,
WebRtc_Word32 number_of_cores,
WebRtc_UWord32 max_payload_size) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
CriticalSectionScoped cs(encoding_cs_.get());
if (!capture_encoder_ || !codec_settings) {
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(codec_settings, number_of_cores,
false) == 0) {
decoder_initialized_ = true;
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d) VCM Decoder initialized",
__FUNCTION__, capture_id_);
}
}
}
}
return capture_encoder_->ConfigureEncoder(*codec_settings, max_payload_size);
}
WebRtc_Word32 ViECapturer::Encode(const RawImage& input_image,
const CodecSpecificInfo* codec_specific_info,
const VideoFrameType* frame_types) {
CriticalSectionScoped cs(encoding_cs_.get());
if (!capture_encoder_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
if (*frame_types == kKeyFrame) {
return capture_encoder_->EncodeFrameType(kVideoFrameKey);
}
if (*frame_types == kSkipFrame) {
return capture_encoder_->EncodeFrameType(kFrameEmpty);
}
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
WebRtc_Word32 ViECapturer::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
CriticalSectionScoped cs(deliver_cs_.get());
if (!capture_encoder_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
encode_complete_callback_ = callback;
return 0;
}
WebRtc_Word32 ViECapturer::Release() {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
{
CriticalSectionScoped cs(deliver_cs_.get());
encode_complete_callback_ = NULL;
}
{
CriticalSectionScoped cs(encoding_cs_.get());
decoder_initialized_ = false;
codec_.codecType = kVideoCodecUnknown;
// Reset the camera to output I420.
capture_encoder_->ConfigureEncoder(codec_, 0);
if (vie_encoder_) {
// Need to add the encoder as an observer of I420.
ViEFrameProviderBase::RegisterFrameCallback(vie_encoder_id_,
vie_encoder_);
}
vie_encoder_ = NULL;
}
return 0;
}
// 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(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
return 0;
}
WebRtc_Word32 ViECapturer::SetChannelParameters(WebRtc_UWord32 packet_loss,
int rtt) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
CriticalSectionScoped cs(encoding_cs_.get());
if (!capture_encoder_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
return capture_encoder_->SetChannelParameters(packet_loss, rtt);
}
WebRtc_Word32 ViECapturer::SetRates(WebRtc_UWord32 new_bit_rate,
WebRtc_UWord32 frame_rate) {
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s(capture_device_id: %d)", __FUNCTION__, capture_id_);
CriticalSectionScoped cs(encoding_cs_.get());
if (!capture_encoder_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
return capture_encoder_->SetRates(new_bit_rate, frame_rate);
}
WebRtc_Word32 ViECapturer::FrameToRender(VideoFrame& video_frame) {
deliver_cs_->Enter();
DeliverI420Frame(video_frame);
deliver_cs_->Leave();
return 0;
}
WebRtc_Word32 ViECapturer::RegisterObserver(ViECaptureObserver& observer) {
if (observer_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s Observer already registered", __FUNCTION__, capture_id_);
return -1;
}
if (capture_module_->RegisterCaptureCallback(*this) != 0) {
return -1;
}
capture_module_->EnableFrameRateCallback(true);
capture_module_->EnableNoPictureAlarm(true);
observer_ = &observer;
return 0;
}
WebRtc_Word32 ViECapturer::DeRegisterObserver() {
CriticalSectionScoped cs(observer_cs_.get());
if (!observer_) {
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_),
"%s No observer registered", __FUNCTION__, capture_id_);
return -1;
}
capture_module_->EnableFrameRateCallback(false);
capture_module_->EnableNoPictureAlarm(false);
capture_module_->DeRegisterCaptureCallback();
observer_ = NULL;
return 0;
}
bool ViECapturer::IsObserverRegistered() {
CriticalSectionScoped cs(observer_cs_.get());
return observer_ != NULL;
}
void ViECapturer::OnCaptureFrameRate(const WebRtc_Word32 id,
const WebRtc_UWord32 frame_rate) {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
"OnCaptureFrameRate %d", frame_rate);
CriticalSectionScoped cs(observer_cs_.get());
observer_->CapturedFrameRate(id_, (WebRtc_UWord8) frame_rate);
}
void ViECapturer::OnNoPictureAlarm(const WebRtc_Word32 id,
const VideoCaptureAlarm alarm) {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
"OnNoPictureAlarm %d", alarm);
CriticalSectionScoped cs(observer_cs_.get());
CaptureAlarm vie_alarm = (alarm == Raised) ? AlarmRaised : AlarmCleared;
observer_->NoPictureAlarm(id, vie_alarm);
}
WebRtc_Word32 ViECapturer::SetCaptureDeviceImage(
const VideoFrame& capture_device_image) {
return capture_module_->StartSendImage(capture_device_image, 10);
}
} // namespace webrtc