From b015cbede88899f67a53fbbe581b02ce8e327949 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Mon, 22 Oct 2012 18:19:23 +0000 Subject: Move src/ -> webrtc/ TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/915006 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@2963 4adac7df-926f-26a2-2b94-8c16560cd09d --- video_engine/vie_capturer.cc | 901 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 901 insertions(+) create mode 100644 video_engine/vie_capturer.cc (limited to 'video_engine/vie_capturer.cc') diff --git a/video_engine/vie_capturer.cc b/video_engine/vie_capturer.cc new file mode 100644 index 00000000..ad9f39d2 --- /dev/null +++ b/video_engine/vie_capturer.cc @@ -0,0 +1,901 @@ +/* + * 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 char* 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 char* 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 char* 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, + uint16_t width, + uint16_t height, + RawVideoType video_type, + unsigned long long capture_time) { // NOLINT + 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) { // NOLINT + 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()); + // Make sure we render this frame earlier since we know the render time set + // is slightly off since it's being set when the frame has been received from + // the camera, and not when the camera actually captured the frame. + video_frame.SetRenderTime(video_frame.RenderTimeMs() - FrameDelay()); + 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_) { + // Already enabled, nothing need to be done. + return 0; + } + denoising_enabled_ = true; + if (IncImageProcRefCount() != 0) { + return -1; + } + } else { + if (denoising_enabled_ == false) { + // Already disabled, nothing need to be done. + return 0; + } + 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(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 * static_cast(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(); + const int kMsToRtpTimestamp = 90; + decode_buffer_.timeStamp = kMsToRtpTimestamp * + static_cast(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(char* 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 VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) { + CriticalSectionScoped cs(encoding_cs_.get()); + if (!capture_encoder_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (frame_types == NULL) { + return capture_encoder_->EncodeFrameType(kVideoFrameDelta); + } else if ((*frame_types)[0] == kKeyFrame) { + return capture_encoder_->EncodeFrameType(kVideoFrameKey); + } else if ((*frame_types)[0] == 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) { // NOLINT + 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); +} + +} // namespace webrtc -- cgit v1.2.3