|  | /* | 
|  | *  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 "webrtc/video_engine/vie_codec_impl.h" | 
|  |  | 
|  | #include <list> | 
|  |  | 
|  | #include "webrtc/engine_configurations.h" | 
|  | #include "webrtc/modules/video_coding/main/interface/video_coding.h" | 
|  | #include "webrtc/system_wrappers/interface/logging.h" | 
|  | #include "webrtc/system_wrappers/interface/trace.h" | 
|  | #include "webrtc/video_engine/include/vie_errors.h" | 
|  | #include "webrtc/video_engine/vie_capturer.h" | 
|  | #include "webrtc/video_engine/vie_channel.h" | 
|  | #include "webrtc/video_engine/vie_channel_manager.h" | 
|  | #include "webrtc/video_engine/vie_defines.h" | 
|  | #include "webrtc/video_engine/vie_encoder.h" | 
|  | #include "webrtc/video_engine/vie_impl.h" | 
|  | #include "webrtc/video_engine/vie_input_manager.h" | 
|  | #include "webrtc/video_engine/vie_shared_data.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | ViECodec* ViECodec::GetInterface(VideoEngine* video_engine) { | 
|  | #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API | 
|  | if (!video_engine) { | 
|  | return NULL; | 
|  | } | 
|  | VideoEngineImpl* vie_impl = static_cast<VideoEngineImpl*>(video_engine); | 
|  | ViECodecImpl* vie_codec_impl = vie_impl; | 
|  | // Increase ref count. | 
|  | (*vie_codec_impl)++; | 
|  | return vie_codec_impl; | 
|  | #else | 
|  | return NULL; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::Release() { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), | 
|  | "ViECodecImpl::Release()"); | 
|  | // Decrease ref count. | 
|  | (*this)--; | 
|  |  | 
|  | int32_t ref_count = GetCount(); | 
|  | if (ref_count < 0) { | 
|  | WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), | 
|  | "ViECodec released too many times"); | 
|  | shared_data_->SetLastError(kViEAPIDoesNotExist); | 
|  | return -1; | 
|  | } | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), | 
|  | "ViECodec reference count: %d", ref_count); | 
|  | return ref_count; | 
|  | } | 
|  |  | 
|  | ViECodecImpl::ViECodecImpl(ViESharedData* shared_data) | 
|  | : shared_data_(shared_data) { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), | 
|  | "ViECodecImpl::ViECodecImpl() Ctor"); | 
|  | } | 
|  |  | 
|  | ViECodecImpl::~ViECodecImpl() { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), | 
|  | "ViECodecImpl::~ViECodecImpl() Dtor"); | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::NumberOfCodecs() const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s", __FUNCTION__); | 
|  | // +2 because of FEC(RED and ULPFEC) | 
|  | return static_cast<int>((VideoCodingModule::NumberOfCodecs() + 2)); | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetCodec(const unsigned char list_number, | 
|  | VideoCodec& video_codec) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s(list_number: %d)", __FUNCTION__, list_number); | 
|  | if (list_number == VideoCodingModule::NumberOfCodecs()) { | 
|  | memset(&video_codec, 0, sizeof(VideoCodec)); | 
|  | strcpy(video_codec.plName, "red"); | 
|  | video_codec.codecType = kVideoCodecRED; | 
|  | video_codec.plType = VCM_RED_PAYLOAD_TYPE; | 
|  | } else if (list_number == VideoCodingModule::NumberOfCodecs() + 1) { | 
|  | memset(&video_codec, 0, sizeof(VideoCodec)); | 
|  | strcpy(video_codec.plName, "ulpfec"); | 
|  | video_codec.codecType = kVideoCodecULPFEC; | 
|  | video_codec.plType = VCM_ULPFEC_PAYLOAD_TYPE; | 
|  | } else if (VideoCodingModule::Codec(list_number, &video_codec) != VCM_OK) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s: Could not get codec for list_number: %u", __FUNCTION__, | 
|  | list_number); | 
|  | shared_data_->SetLastError(kViECodecInvalidArgument); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SetSendCodec(const int video_channel, | 
|  | const VideoCodec& video_codec) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, | 
|  | video_channel, video_codec.codecType); | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: codec: %d, pl_type: %d, width: %d, height: %d, bitrate: %d" | 
|  | "maxBr: %d, min_br: %d, frame_rate: %d, qpMax: %u," | 
|  | "numberOfSimulcastStreams: %u )", __FUNCTION__, | 
|  | video_codec.codecType, video_codec.plType, video_codec.width, | 
|  | video_codec.height, video_codec.startBitrate, | 
|  | video_codec.maxBitrate, video_codec.minBitrate, | 
|  | video_codec.maxFramerate, video_codec.qpMax, | 
|  | video_codec.numberOfSimulcastStreams); | 
|  | if (video_codec.codecType == kVideoCodecVP8) { | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "pictureLossIndicationOn: %d, feedbackModeOn: %d, " | 
|  | "complexity: %d, resilience: %d, numberOfTemporalLayers: %u" | 
|  | "keyFrameInterval %d", | 
|  | video_codec.codecSpecific.VP8.pictureLossIndicationOn, | 
|  | video_codec.codecSpecific.VP8.feedbackModeOn, | 
|  | video_codec.codecSpecific.VP8.complexity, | 
|  | video_codec.codecSpecific.VP8.resilience, | 
|  | video_codec.codecSpecific.VP8.numberOfTemporalLayers, | 
|  | video_codec.codecSpecific.VP8.keyFrameInterval); | 
|  | } | 
|  | if (!CodecValid(video_codec)) { | 
|  | // Error logged. | 
|  | shared_data_->SetLastError(kViECodecInvalidCodec); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | assert(vie_encoder); | 
|  | if (vie_encoder->Owner() != video_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Receive only channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecReceiveOnlyChannel); | 
|  | return -1; | 
|  | } | 
|  | // Set a max_bitrate if the user hasn't set one. | 
|  | VideoCodec video_codec_internal; | 
|  | memcpy(&video_codec_internal, &video_codec, sizeof(VideoCodec)); | 
|  | if (video_codec_internal.maxBitrate == 0) { | 
|  | // Max is one bit per pixel. | 
|  | video_codec_internal.maxBitrate = (video_codec_internal.width * | 
|  | video_codec_internal.height * | 
|  | video_codec_internal.maxFramerate) | 
|  | / 1000; | 
|  | if (video_codec_internal.startBitrate > video_codec_internal.maxBitrate) { | 
|  | // Don't limit the set start bitrate. | 
|  | video_codec_internal.maxBitrate = video_codec_internal.startBitrate; | 
|  | } | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: New max bitrate set to %d kbps", __FUNCTION__, | 
|  | video_codec_internal.maxBitrate); | 
|  | } | 
|  |  | 
|  | VideoCodec encoder; | 
|  | vie_encoder->GetEncoder(&encoder); | 
|  |  | 
|  | // Make sure to generate a new SSRC if the codec type and/or resolution has | 
|  | // changed. This won't have any effect if the user has set an SSRC. | 
|  | bool new_rtp_stream = false; | 
|  | if (encoder.codecType != video_codec_internal.codecType) { | 
|  | new_rtp_stream = true; | 
|  | } | 
|  |  | 
|  | ViEInputManagerScoped is(*(shared_data_->input_manager())); | 
|  |  | 
|  | // Stop the media flow while reconfiguring. | 
|  | vie_encoder->Pause(); | 
|  |  | 
|  | if (vie_encoder->SetEncoder(video_codec_internal) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not change encoder for channel %d", __FUNCTION__, | 
|  | video_channel); | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Give the channel(s) the new information. | 
|  | ChannelList channels; | 
|  | cs.ChannelsUsingViEEncoder(video_channel, &channels); | 
|  | for (ChannelList::iterator it = channels.begin(); it != channels.end(); | 
|  | ++it) { | 
|  | bool ret = true; | 
|  | if ((*it)->SetSendCodec(video_codec_internal, new_rtp_stream) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not set send codec for channel %d", __FUNCTION__, | 
|  | video_channel); | 
|  | ret = false; | 
|  | } | 
|  | if (!ret) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(mflodman) Break out this part in GetLocalSsrcList(). | 
|  | // Update all SSRCs to ViEEncoder. | 
|  | std::list<unsigned int> ssrcs; | 
|  | if (video_codec_internal.numberOfSimulcastStreams == 0) { | 
|  | unsigned int ssrc = 0; | 
|  | if (vie_channel->GetLocalSSRC(0, &ssrc) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not get ssrc", __FUNCTION__); | 
|  | } | 
|  | ssrcs.push_back(ssrc); | 
|  | } else { | 
|  | for (int idx = 0; idx < video_codec_internal.numberOfSimulcastStreams; | 
|  | ++idx) { | 
|  | unsigned int ssrc = 0; | 
|  | if (vie_channel->GetLocalSSRC(idx, &ssrc) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not get ssrc for idx %d", __FUNCTION__, idx); | 
|  | } | 
|  | ssrcs.push_back(ssrc); | 
|  | } | 
|  | } | 
|  | vie_encoder->SetSsrcs(ssrcs); | 
|  | shared_data_->channel_manager()->UpdateSsrcs(video_channel, ssrcs); | 
|  |  | 
|  | // Update the protection mode, we might be switching NACK/FEC. | 
|  | vie_encoder->UpdateProtectionMethod(vie_encoder->nack_enabled()); | 
|  |  | 
|  | // Get new best format for frame provider. | 
|  | ViEFrameProviderBase* frame_provider = is.FrameProvider(vie_encoder); | 
|  | if (frame_provider) { | 
|  | frame_provider->FrameCallbackChanged(); | 
|  | } | 
|  | // Restart the media flow | 
|  | if (new_rtp_stream) { | 
|  | // Stream settings changed, make sure we get a key frame. | 
|  | vie_encoder->SendKeyFrame(); | 
|  | } | 
|  | vie_encoder->Restart(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetSendCodec(const int video_channel, | 
|  | VideoCodec& video_codec) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d)", __FUNCTION__, video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder for channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | return vie_encoder->GetEncoder(&video_codec); | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SetReceiveCodec(const int video_channel, | 
|  | const VideoCodec& video_codec) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, | 
|  | video_channel, video_codec.codecType); | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: codec: %d, pl_type: %d, width: %d, height: %d, bitrate: %d," | 
|  | "maxBr: %d, min_br: %d, frame_rate: %d", __FUNCTION__, | 
|  | video_codec.codecType, video_codec.plType, video_codec.width, | 
|  | video_codec.height, video_codec.startBitrate, | 
|  | video_codec.maxBitrate, video_codec.minBitrate, | 
|  | video_codec.maxFramerate); | 
|  |  | 
|  | if (CodecValid(video_codec) == false) { | 
|  | // Error logged. | 
|  | shared_data_->SetLastError(kViECodecInvalidCodec); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (vie_channel->SetReceiveCodec(video_codec) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not set receive codec for channel %d", | 
|  | __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetReceiveCodec(const int video_channel, | 
|  | VideoCodec& video_codec) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, | 
|  | video_channel, video_codec.codecType); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (vie_channel->GetReceiveCodec(&video_codec) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetCodecConfigParameters( | 
|  | const int video_channel, | 
|  | unsigned char config_parameters[kConfigParameterSize], | 
|  | unsigned char& config_parameters_size) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d)", __FUNCTION__, video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder for channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (vie_encoder->GetCodecConfigParameters(config_parameters, | 
|  | config_parameters_size) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SetImageScaleStatus(const int video_channel, | 
|  | const bool enable) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, enable: %d)", __FUNCTION__, video_channel, | 
|  | enable); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (vie_encoder->ScaleInputImage(enable) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetSendCodecStastistics(const int video_channel, | 
|  | unsigned int& key_frames, | 
|  | unsigned int& delta_frames) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel %d)", __FUNCTION__, video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No send codec for channel %d", __FUNCTION__, | 
|  | video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (vie_encoder->SendCodecStatistics(&key_frames, &delta_frames) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetReceiveCodecStastistics(const int video_channel, | 
|  | unsigned int& key_frames, | 
|  | unsigned int& delta_frames) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d)", __FUNCTION__, | 
|  | video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->ReceiveCodecStatistics(&key_frames, &delta_frames) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::GetReceiveSideDelay(const int video_channel, | 
|  | int* delay_ms) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d)", __FUNCTION__, video_channel); | 
|  | if (delay_ms == NULL) { | 
|  | LOG_F(LS_ERROR) << "NULL pointer argument."; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | *delay_ms = vie_channel->ReceiveDelay(); | 
|  | if (*delay_ms < 0) { | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int ViECodecImpl::GetCodecTargetBitrate(const int video_channel, | 
|  | unsigned int* bitrate) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, | 
|  | video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No send codec for channel %d", __FUNCTION__, | 
|  | video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | return vie_encoder->CodecTargetBitrate(static_cast<uint32_t*>(bitrate)); | 
|  | } | 
|  |  | 
|  | unsigned int ViECodecImpl::GetDiscardedPackets(const int video_channel) const { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, | 
|  | video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | return vie_channel->DiscardedPackets(); | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SetKeyFrameRequestCallbackStatus(const int video_channel, | 
|  | const bool enable) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d)", __FUNCTION__, video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->EnableKeyFrameRequestCallback(enable) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SetSignalKeyPacketLossStatus(const int video_channel, | 
|  | const bool enable, | 
|  | const bool only_key_frames) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s(video_channel: %d, enable: %d, only_key_frames: %d)", | 
|  | __FUNCTION__, video_channel, enable); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->SetSignalPacketLossStatus(enable, only_key_frames) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::RegisterEncoderObserver(const int video_channel, | 
|  | ViEEncoderObserver& observer) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s", __FUNCTION__); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder for channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_encoder->RegisterCodecObserver(&observer) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not register codec observer at channel", | 
|  | __FUNCTION__); | 
|  | shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::DeregisterEncoderObserver(const int video_channel) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s", __FUNCTION__); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder for channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_encoder->RegisterCodecObserver(NULL) != 0) { | 
|  | shared_data_->SetLastError(kViECodecObserverNotRegistered); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::RegisterDecoderObserver(const int video_channel, | 
|  | ViEDecoderObserver& observer) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s", __FUNCTION__); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->RegisterCodecObserver(&observer) != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: Could not register codec observer at channel", | 
|  | __FUNCTION__); | 
|  | shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::DeregisterDecoderObserver(const int video_channel) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id()), "%s", | 
|  | __FUNCTION__); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->RegisterCodecObserver(NULL) != 0) { | 
|  | shared_data_->SetLastError(kViECodecObserverNotRegistered); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::SendKeyFrame(const int video_channel) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), | 
|  | "%s(video_channel: %d)", __FUNCTION__, video_channel); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_encoder->SendKeyFrame() != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::WaitForFirstKeyFrame(const int video_channel, | 
|  | const bool wait) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id()), | 
|  | "%s(video_channel: %d, wait: %d)", __FUNCTION__, video_channel, | 
|  | wait); | 
|  |  | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return -1; | 
|  | } | 
|  | if (vie_channel->WaitForKeyFrame(wait) != 0) { | 
|  | shared_data_->SetLastError(kViECodecUnknownError); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::StartDebugRecording(int video_channel, | 
|  | const char* file_name_utf8) { | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder %d", __FUNCTION__, video_channel); | 
|  | return -1; | 
|  | } | 
|  | return vie_encoder->StartDebugRecording(file_name_utf8); | 
|  | } | 
|  |  | 
|  | int ViECodecImpl::StopDebugRecording(int video_channel) { | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder %d", __FUNCTION__, video_channel); | 
|  | return -1; | 
|  | } | 
|  | return vie_encoder->StopDebugRecording(); | 
|  | } | 
|  |  | 
|  | void ViECodecImpl::SuspendBelowMinBitrate(int video_channel) { | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEEncoder* vie_encoder = cs.Encoder(video_channel); | 
|  | if (!vie_encoder) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No encoder %d", __FUNCTION__, video_channel); | 
|  | return; | 
|  | } | 
|  | vie_encoder->SuspendBelowMinBitrate(); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | return; | 
|  | } | 
|  | // Must enable pacing when enabling SuspendBelowMinBitrate. Otherwise, no | 
|  | // padding will be sent when the video is suspended so the video will be | 
|  | // unable to recover. | 
|  | vie_channel->SetTransmissionSmoothingStatus(true); | 
|  | } | 
|  |  | 
|  | bool ViECodecImpl::GetSendSideDelay(int video_channel, int* avg_delay_ms, | 
|  | int* max_delay_ms) const { | 
|  | ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); | 
|  | ViEChannel* vie_channel = cs.Channel(video_channel); | 
|  | if (!vie_channel) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, | 
|  | ViEId(shared_data_->instance_id(), video_channel), | 
|  | "%s: No channel %d", __FUNCTION__, video_channel); | 
|  | shared_data_->SetLastError(kViECodecInvalidChannelId); | 
|  | return false; | 
|  | } | 
|  | return vie_channel->GetSendSideDelay(avg_delay_ms, max_delay_ms); | 
|  | } | 
|  |  | 
|  | bool ViECodecImpl::CodecValid(const VideoCodec& video_codec) { | 
|  | // Check pl_name matches codec_type. | 
|  | if (video_codec.codecType == kVideoCodecRED) { | 
|  | #if defined(WIN32) | 
|  | if (_strnicmp(video_codec.plName, "red", 3) == 0) { | 
|  | #else | 
|  | if (strncasecmp(video_codec.plName, "red", 3) == 0) { | 
|  | #endif | 
|  | // We only care about the type and name for red. | 
|  | return true; | 
|  | } | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, | 
|  | "Codec type doesn't match pl_name", video_codec.plType); | 
|  | return false; | 
|  | } else if (video_codec.codecType == kVideoCodecULPFEC) { | 
|  | #if defined(WIN32) | 
|  | if (_strnicmp(video_codec.plName, "ULPFEC", 6) == 0) { | 
|  | #else | 
|  | if (strncasecmp(video_codec.plName, "ULPFEC", 6) == 0) { | 
|  | #endif | 
|  | // We only care about the type and name for ULPFEC. | 
|  | return true; | 
|  | } | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, | 
|  | "Codec type doesn't match pl_name", video_codec.plType); | 
|  | return false; | 
|  | } else if ((video_codec.codecType == kVideoCodecVP8 && | 
|  | strncmp(video_codec.plName, "VP8", 4) == 0) || | 
|  | (video_codec.codecType == kVideoCodecI420 && | 
|  | strncmp(video_codec.plName, "I420", 4) == 0)) { | 
|  | // OK. | 
|  | } else if (video_codec.codecType != kVideoCodecGeneric) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, | 
|  | "Codec type doesn't match pl_name", video_codec.plType); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (video_codec.plType == 0 || video_codec.plType > 127) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, | 
|  | "Invalid codec payload type: %d", video_codec.plType); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (video_codec.width > kViEMaxCodecWidth || | 
|  | video_codec.height > kViEMaxCodecHeight) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid codec size: %u x %u", | 
|  | video_codec.width, video_codec.height); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (video_codec.startBitrate < kViEMinCodecBitrate) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid start_bitrate: %u", | 
|  | video_codec.startBitrate); | 
|  | return false; | 
|  | } | 
|  | if (video_codec.minBitrate < kViEMinCodecBitrate) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid min_bitrate: %u", | 
|  | video_codec.minBitrate); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |