/* * 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 #include "webrtc/engine_configurations.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/system_wrappers/interface/logging.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 { static void LogCodec(const VideoCodec& codec) { LOG(LS_INFO) << "CodecType " << codec.codecType << ", pl_type " << static_cast(codec.plType) << ", resolution " << codec.width << " x " << codec.height << ", start br " << codec.startBitrate << ", min br " << codec.minBitrate << ", max br " << codec.maxBitrate << ", max fps " << static_cast(codec.maxFramerate) << ", max qp " << codec.qpMax << ", number of streams " << static_cast(codec.numberOfSimulcastStreams); if (codec.codecType == kVideoCodecVP8) { LOG(LS_INFO) << "VP8 specific settings"; LOG(LS_INFO) << "pictureLossIndicationOn " << codec.codecSpecific.VP8.pictureLossIndicationOn << ", feedbackModeOn " << codec.codecSpecific.VP8.feedbackModeOn << ", complexity " << codec.codecSpecific.VP8.complexity << ", resilience " << codec.codecSpecific.VP8.resilience << ", numberOfTemporalLayers " << static_cast( codec.codecSpecific.VP8.numberOfTemporalLayers) << ", keyFrameinterval " << codec.codecSpecific.VP8.keyFrameInterval; for (int idx = 0; idx < codec.numberOfSimulcastStreams; ++idx) { LOG(LS_INFO) << "Stream " << codec.simulcastStream[idx].width << " x " << codec.simulcastStream[idx].height; LOG(LS_INFO) << "Temporal layers " << static_cast( codec.simulcastStream[idx].numberOfTemporalLayers) << ", min br " << codec.simulcastStream[idx].minBitrate << ", target br " << codec.simulcastStream[idx].targetBitrate << ", max br " << codec.simulcastStream[idx].maxBitrate << ", qp max " << codec.simulcastStream[idx].qpMax; } } else if (codec.codecType == kVideoCodecH264) { LOG(LS_INFO) << "H264 specific settings"; LOG(LS_INFO) << "profile: " << codec.codecSpecific.H264.profile << ", framedropping: " << codec.codecSpecific.H264.frameDroppingOn << ", keyFrameInterval: " << codec.codecSpecific.H264.keyFrameInterval << ", spslen: " << codec.codecSpecific.H264.spsLen << ", ppslen: " << codec.codecSpecific.H264.ppsLen; } } ViECodec* ViECodec::GetInterface(VideoEngine* video_engine) { #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API if (!video_engine) { return NULL; } VideoEngineImpl* vie_impl = static_cast(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() { LOG(LS_INFO) << "ViECodec::Release."; // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { LOG(LS_WARNING) << "ViECodec released too many times."; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } return ref_count; } ViECodecImpl::ViECodecImpl(ViESharedData* shared_data) : shared_data_(shared_data) { } ViECodecImpl::~ViECodecImpl() { } int ViECodecImpl::NumberOfCodecs() const { // +2 because of FEC(RED and ULPFEC) return static_cast((VideoCodingModule::NumberOfCodecs() + 2)); } int ViECodecImpl::GetCodec(const unsigned char list_number, VideoCodec& video_codec) const { 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) { shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } return 0; } int ViECodecImpl::SetSendCodec(const int video_channel, const VideoCodec& video_codec) { LOG(LS_INFO) << "SetSendCodec for channel " << video_channel; LogCodec(video_codec); 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) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } ViEEncoder* vie_encoder = cs.Encoder(video_channel); assert(vie_encoder); if (vie_encoder->Owner() != video_channel) { LOG_F(LS_ERROR) << "Receive only 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; LOG(LS_INFO) << "New max bitrate set " << video_codec_internal.maxBitrate; } if (video_codec_internal.startBitrate < video_codec_internal.minBitrate) { video_codec_internal.startBitrate = video_codec_internal.minBitrate; } if (video_codec_internal.startBitrate > video_codec_internal.maxBitrate) { video_codec_internal.startBitrate = 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) { 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) { 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 ssrcs; if (video_codec_internal.numberOfSimulcastStreams == 0) { unsigned int ssrc = 0; if (vie_channel->GetLocalSSRC(0, &ssrc) != 0) { LOG_F(LS_ERROR) << "Could not get ssrc."; } 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) { LOG_F(LS_ERROR) << "Could not get ssrc for stream " << 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 { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } return vie_encoder->GetEncoder(&video_codec); } int ViECodecImpl::SetReceiveCodec(const int video_channel, const VideoCodec& video_codec) { LOG(LS_INFO) << "SetReceiveCodec for channel " << video_channel; LOG(LS_INFO) << "Codec type " << video_codec.codecType << ", payload type " << video_codec.plType; if (CodecValid(video_codec) == false) { shared_data_->SetLastError(kViECodecInvalidCodec); return -1; } ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_channel->SetReceiveCodec(video_codec) != 0) { shared_data_->SetLastError(kViECodecUnknownError); return -1; } return 0; } int ViECodecImpl::GetReceiveCodec(const int video_channel, VideoCodec& video_codec) const { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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 { LOG(LS_INFO) << "GetCodecConfigParameters " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { 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) { LOG(LS_INFO) << "SetImageScaleStates for channel " << video_channel << ", enable: " << enable; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { 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 { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { 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 { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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 { assert(delay_ms != NULL); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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 { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } return vie_encoder->CodecTargetBitrate(static_cast(bitrate)); } int ViECodecImpl::GetNumDiscardedPackets(int video_channel) const { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } return static_cast(vie_channel->DiscardedPackets()); } int ViECodecImpl::SetKeyFrameRequestCallbackStatus(const int video_channel, const bool enable) { LOG(LS_INFO) << "SetKeyFrameRequestCallbackStatus for " << video_channel << ", enable " << enable; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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) { LOG(LS_INFO) << "SetSignalKeyPacketLossStatus for " << video_channel << "enable, " << enable << ", only key frames " << only_key_frames; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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) { LOG(LS_INFO) << "RegisterEncoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_encoder->RegisterCodecObserver(&observer) != 0) { shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); return -1; } return 0; } int ViECodecImpl::DeregisterEncoderObserver(const int video_channel) { LOG(LS_INFO) << "DeregisterEncoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { 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) { LOG(LS_INFO) << "RegisterDecoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_channel->RegisterCodecObserver(&observer) != 0) { shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); return -1; } return 0; } int ViECodecImpl::DeregisterDecoderObserver(const int video_channel) { LOG(LS_INFO) << "DeregisterDecodeObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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) { LOG(LS_INFO) << "SendKeyFrame on channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { 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) { LOG(LS_INFO) << "WaitForFirstKeyFrame for channel " << video_channel << ", wait " << wait; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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) { LOG(LS_INFO) << "StartDebugRecording for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { return -1; } return vie_encoder->StartDebugRecording(file_name_utf8); } int ViECodecImpl::StopDebugRecording(int video_channel) { LOG(LS_INFO) << "StopDebugRecording for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { return -1; } return vie_encoder->StopDebugRecording(); } void ViECodecImpl::SuspendBelowMinBitrate(int video_channel) { LOG(LS_INFO) << "SuspendBelowMinBitrate for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { return; } vie_encoder->SuspendBelowMinBitrate(); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_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) { 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; } LOG_F(LS_ERROR) << "Invalid RED configuration."; 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; } LOG_F(LS_ERROR) << "Invalid ULPFEC configuration."; 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) || (video_codec.codecType == kVideoCodecH264 && strncmp(video_codec.plName, "H264", 4) == 0)) { // OK. } else if (video_codec.codecType != kVideoCodecGeneric) { LOG(LS_ERROR) << "Codec type and name mismatch."; return false; } if (video_codec.plType == 0 || video_codec.plType > 127) { LOG(LS_ERROR) << "Invalif payload type: " << video_codec.plType; return false; } if (video_codec.width > kViEMaxCodecWidth || video_codec.height > kViEMaxCodecHeight) { LOG(LS_ERROR) << "Invalid codec resolution " << video_codec.width << " x " << video_codec.height; return false; } if (video_codec.startBitrate < kViEMinCodecBitrate) { LOG(LS_ERROR) << "Invalid start bitrate."; return false; } if (video_codec.minBitrate < kViEMinCodecBitrate) { LOG(LS_ERROR) << "Invalid min bitrate."; return false; } return true; } } // namespace webrtc