| /* | 
 |  *  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_encoder.h" | 
 |  | 
 | #include <assert.h> | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "webrtc/common_video/interface/video_image.h" | 
 | #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" | 
 | #include "webrtc/modules/pacing/include/paced_sender.h" | 
 | #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" | 
 | #include "webrtc/modules/utility/interface/process_thread.h" | 
 | #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" | 
 | #include "webrtc/modules/video_coding/main/interface/video_coding.h" | 
 | #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" | 
 | #include "webrtc/modules/video_coding/main/source/encoded_frame.h" | 
 | #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | 
 | #include "webrtc/system_wrappers/interface/logging.h" | 
 | #include "webrtc/system_wrappers/interface/tick_util.h" | 
 | #include "webrtc/system_wrappers/interface/trace.h" | 
 | #include "webrtc/system_wrappers/interface/trace_event.h" | 
 | #include "webrtc/video_engine/include/vie_codec.h" | 
 | #include "webrtc/video_engine/include/vie_image_process.h" | 
 | #include "webrtc/frame_callback.h" | 
 | #include "webrtc/video_engine/vie_defines.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | // Pace in kbits/s until we receive first estimate. | 
 | static const int kInitialPace = 2000; | 
 |  | 
 | // Pacing-rate relative to our target send rate. | 
 | // Multiplicative factor that is applied to the target bitrate to calculate the | 
 | // number of bytes that can be transmitted per interval. | 
 | // Increasing this factor will result in lower delays in cases of bitrate | 
 | // overshoots from the encoder. | 
 | static const float kPaceMultiplier = 2.5f; | 
 |  | 
 | // Margin on when we pause the encoder when the pacing buffer overflows relative | 
 | // to the configured buffer delay. | 
 | static const float kEncoderPausePacerMargin = 2.0f; | 
 |  | 
 | // Don't stop the encoder unless the delay is above this configured value. | 
 | static const int kMinPacingDelayMs = 200; | 
 |  | 
 | // Allow packets to be transmitted in up to 2 times max video bitrate if the | 
 | // bandwidth estimate allows it. | 
 | // TODO(holmer): Expose transmission start, min and max bitrates in the | 
 | // VideoEngine API and remove the kTransmissionMaxBitrateMultiplier. | 
 | static const int kTransmissionMaxBitrateMultiplier = 2; | 
 |  | 
 | static const float kStopPaddingThresholdMs = 2000; | 
 |  | 
 | std::vector<uint32_t> AllocateStreamBitrates( | 
 |     uint32_t total_bitrate, | 
 |     const SimulcastStream* stream_configs, | 
 |     size_t number_of_streams) { | 
 |   if (number_of_streams == 0) { | 
 |     std::vector<uint32_t> stream_bitrates(1, 0); | 
 |     stream_bitrates[0] = total_bitrate; | 
 |     return stream_bitrates; | 
 |   } | 
 |   std::vector<uint32_t> stream_bitrates(number_of_streams, 0); | 
 |   uint32_t bitrate_remainder = total_bitrate; | 
 |   for (size_t i = 0; i < stream_bitrates.size() && bitrate_remainder > 0; ++i) { | 
 |     if (stream_configs[i].maxBitrate * 1000 > bitrate_remainder) { | 
 |       stream_bitrates[i] = bitrate_remainder; | 
 |     } else { | 
 |       stream_bitrates[i] = stream_configs[i].maxBitrate * 1000; | 
 |     } | 
 |     bitrate_remainder -= stream_bitrates[i]; | 
 |   } | 
 |   return stream_bitrates; | 
 | } | 
 |  | 
 | class QMVideoSettingsCallback : public VCMQMSettingsCallback { | 
 |  public: | 
 |   explicit QMVideoSettingsCallback(VideoProcessingModule* vpm); | 
 |  | 
 |   ~QMVideoSettingsCallback(); | 
 |  | 
 |   // Update VPM with QM (quality modes: frame size & frame rate) settings. | 
 |   int32_t SetVideoQMSettings(const uint32_t frame_rate, | 
 |                              const uint32_t width, | 
 |                              const uint32_t height); | 
 |  | 
 |  private: | 
 |   VideoProcessingModule* vpm_; | 
 | }; | 
 |  | 
 | class ViEBitrateObserver : public BitrateObserver { | 
 |  public: | 
 |   explicit ViEBitrateObserver(ViEEncoder* owner) | 
 |       : owner_(owner) { | 
 |   } | 
 |   virtual ~ViEBitrateObserver() {} | 
 |   // Implements BitrateObserver. | 
 |   virtual void OnNetworkChanged(const uint32_t bitrate_bps, | 
 |                                 const uint8_t fraction_lost, | 
 |                                 const uint32_t rtt) { | 
 |     owner_->OnNetworkChanged(bitrate_bps, fraction_lost, rtt); | 
 |   } | 
 |  private: | 
 |   ViEEncoder* owner_; | 
 | }; | 
 |  | 
 | class ViEPacedSenderCallback : public PacedSender::Callback { | 
 |  public: | 
 |   explicit ViEPacedSenderCallback(ViEEncoder* owner) | 
 |       : owner_(owner) { | 
 |   } | 
 |   virtual ~ViEPacedSenderCallback() {} | 
 |   virtual bool TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number, | 
 |                                 int64_t capture_time_ms, bool retransmission) { | 
 |     return owner_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms, | 
 |                                     retransmission); | 
 |   } | 
 |   virtual int TimeToSendPadding(int bytes) { | 
 |     return owner_->TimeToSendPadding(bytes); | 
 |   } | 
 |  private: | 
 |   ViEEncoder* owner_; | 
 | }; | 
 |  | 
 | ViEEncoder::ViEEncoder(int32_t engine_id, | 
 |                        int32_t channel_id, | 
 |                        uint32_t number_of_cores, | 
 |                        const Config& config, | 
 |                        ProcessThread& module_process_thread, | 
 |                        BitrateController* bitrate_controller) | 
 |   : engine_id_(engine_id), | 
 |     channel_id_(channel_id), | 
 |     number_of_cores_(number_of_cores), | 
 |     vcm_(*webrtc::VideoCodingModule::Create(ViEModuleId(engine_id, | 
 |                                                         channel_id))), | 
 |     vpm_(*webrtc::VideoProcessingModule::Create(ViEModuleId(engine_id, | 
 |                                                             channel_id))), | 
 |     callback_cs_(CriticalSectionWrapper::CreateCriticalSection()), | 
 |     data_cs_(CriticalSectionWrapper::CreateCriticalSection()), | 
 |     bitrate_controller_(bitrate_controller), | 
 |     time_of_last_incoming_frame_ms_(0), | 
 |     send_padding_(false), | 
 |     target_delay_ms_(0), | 
 |     network_is_transmitting_(true), | 
 |     encoder_paused_(false), | 
 |     encoder_paused_and_dropped_frame_(false), | 
 |     fec_enabled_(false), | 
 |     nack_enabled_(false), | 
 |     codec_observer_(NULL), | 
 |     effect_filter_(NULL), | 
 |     module_process_thread_(module_process_thread), | 
 |     has_received_sli_(false), | 
 |     picture_id_sli_(0), | 
 |     has_received_rpsi_(false), | 
 |     picture_id_rpsi_(0), | 
 |     qm_callback_(NULL), | 
 |     video_suspended_(false), | 
 |     pre_encode_callback_(NULL) { | 
 |   WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, | 
 |                ViEId(engine_id, channel_id), | 
 |                "%s(engine_id: %d) 0x%p - Constructor", __FUNCTION__, engine_id, | 
 |                this); | 
 |  | 
 |   RtpRtcp::Configuration configuration; | 
 |   configuration.id = ViEModuleId(engine_id_, channel_id_); | 
 |   configuration.audio = false;  // Video. | 
 |  | 
 |   default_rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(configuration)); | 
 |   bitrate_observer_.reset(new ViEBitrateObserver(this)); | 
 |   pacing_callback_.reset(new ViEPacedSenderCallback(this)); | 
 |   paced_sender_.reset( | 
 |       new PacedSender(pacing_callback_.get(), kInitialPace, kPaceMultiplier)); | 
 | } | 
 |  | 
 | bool ViEEncoder::Init() { | 
 |   if (vcm_.InitializeSender() != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s InitializeSender failure", __FUNCTION__); | 
 |     return false; | 
 |   } | 
 |   vpm_.EnableTemporalDecimation(true); | 
 |  | 
 |   // Enable/disable content analysis: off by default for now. | 
 |   vpm_.EnableContentAnalysis(false); | 
 |  | 
 |   if (module_process_thread_.RegisterModule(&vcm_) != 0 || | 
 |       module_process_thread_.RegisterModule(default_rtp_rtcp_.get()) != 0 || | 
 |       module_process_thread_.RegisterModule(paced_sender_.get()) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s RegisterModule failure", __FUNCTION__); | 
 |     return false; | 
 |   } | 
 |   if (qm_callback_) { | 
 |     delete qm_callback_; | 
 |   } | 
 |   qm_callback_ = new QMVideoSettingsCallback(&vpm_); | 
 |  | 
 | #ifdef VIDEOCODEC_VP8 | 
 |   VideoCodec video_codec; | 
 |   if (vcm_.Codec(webrtc::kVideoCodecVP8, &video_codec) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s Codec failure", __FUNCTION__); | 
 |     return false; | 
 |   } | 
 |   send_padding_ = video_codec.numberOfSimulcastStreams > 1; | 
 |   if (vcm_.RegisterSendCodec(&video_codec, number_of_cores_, | 
 |                              default_rtp_rtcp_->MaxDataPayloadLength()) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s RegisterSendCodec failure", __FUNCTION__); | 
 |     return false; | 
 |   } | 
 |   if (default_rtp_rtcp_->RegisterSendPayload(video_codec) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s RegisterSendPayload failure", __FUNCTION__); | 
 |     return false; | 
 |   } | 
 | #else | 
 |   VideoCodec video_codec; | 
 |   if (vcm_.Codec(webrtc::kVideoCodecI420, &video_codec) == VCM_OK) { | 
 |     send_padding_ = video_codec.numberOfSimulcastStreams > 1; | 
 |     vcm_.RegisterSendCodec(&video_codec, number_of_cores_, | 
 |                            default_rtp_rtcp_->MaxDataPayloadLength()); | 
 |     default_rtp_rtcp_->RegisterSendPayload(video_codec); | 
 |   } else { | 
 |     return false; | 
 |   } | 
 | #endif | 
 |  | 
 |   if (vcm_.RegisterTransportCallback(this) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "ViEEncoder: VCM::RegisterTransportCallback failure"); | 
 |     return false; | 
 |   } | 
 |   if (vcm_.RegisterSendStatisticsCallback(this) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "ViEEncoder: VCM::RegisterSendStatisticsCallback failure"); | 
 |     return false; | 
 |   } | 
 |   if (vcm_.RegisterVideoQMCallback(qm_callback_) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "VCM::RegisterQMCallback failure"); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | ViEEncoder::~ViEEncoder() { | 
 |   WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "ViEEncoder Destructor 0x%p, engine_id: %d", this, engine_id_); | 
 |   if (bitrate_controller_) { | 
 |     bitrate_controller_->RemoveBitrateObserver(bitrate_observer_.get()); | 
 |   } | 
 |   module_process_thread_.DeRegisterModule(&vcm_); | 
 |   module_process_thread_.DeRegisterModule(&vpm_); | 
 |   module_process_thread_.DeRegisterModule(default_rtp_rtcp_.get()); | 
 |   module_process_thread_.DeRegisterModule(paced_sender_.get()); | 
 |   VideoCodingModule::Destroy(&vcm_); | 
 |   VideoProcessingModule::Destroy(&vpm_); | 
 |   delete qm_callback_; | 
 | } | 
 |  | 
 | int ViEEncoder::Owner() const { | 
 |   return channel_id_; | 
 | } | 
 |  | 
 | void ViEEncoder::SetNetworkTransmissionState(bool is_transmitting) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s(%s)", __FUNCTION__, | 
 |                is_transmitting ? "transmitting" : "not transmitting"); | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     network_is_transmitting_ = is_transmitting; | 
 |   } | 
 |   if (is_transmitting) { | 
 |     paced_sender_->Resume(); | 
 |   } else { | 
 |     paced_sender_->Pause(); | 
 |   } | 
 | } | 
 |  | 
 | void ViEEncoder::Pause() { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s", __FUNCTION__); | 
 |   CriticalSectionScoped cs(data_cs_.get()); | 
 |   encoder_paused_ = true; | 
 | } | 
 |  | 
 | void ViEEncoder::Restart() { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s", __FUNCTION__); | 
 |   CriticalSectionScoped cs(data_cs_.get()); | 
 |   encoder_paused_ = false; | 
 | } | 
 |  | 
 | uint8_t ViEEncoder::NumberOfCodecs() { | 
 |   return vcm_.NumberOfCodecs(); | 
 | } | 
 |  | 
 | int32_t ViEEncoder::GetCodec(uint8_t list_index, VideoCodec* video_codec) { | 
 |   if (vcm_.Codec(list_index, video_codec) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: Could not get codec", | 
 |                  __FUNCTION__); | 
 |     return -1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::RegisterExternalEncoder(webrtc::VideoEncoder* encoder, | 
 |                                             uint8_t pl_type, | 
 |                                             bool internal_source) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s: pltype %u", __FUNCTION__, | 
 |                pl_type); | 
 |  | 
 |   if (encoder == NULL) | 
 |     return -1; | 
 |  | 
 |   if (vcm_.RegisterExternalEncoder(encoder, pl_type, internal_source) != | 
 |           VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not register external encoder"); | 
 |     return -1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::DeRegisterExternalEncoder(uint8_t pl_type) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s: pltype %u", __FUNCTION__, pl_type); | 
 |  | 
 |   webrtc::VideoCodec current_send_codec; | 
 |   if (vcm_.SendCodec(¤t_send_codec) == VCM_OK) { | 
 |     uint32_t current_bitrate_bps = 0; | 
 |     if (vcm_.Bitrate(¤t_bitrate_bps) != 0) { | 
 |       WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "Failed to get the current encoder target bitrate."); | 
 |     } | 
 |     current_send_codec.startBitrate = (current_bitrate_bps + 500) / 1000; | 
 |   } | 
 |  | 
 |   if (vcm_.RegisterExternalEncoder(NULL, pl_type) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not deregister external encoder"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // If the external encoder is the current send codec, use vcm internal | 
 |   // encoder. | 
 |   if (current_send_codec.plType == pl_type) { | 
 |     uint16_t max_data_payload_length = | 
 |         default_rtp_rtcp_->MaxDataPayloadLength(); | 
 |     send_padding_ = current_send_codec.numberOfSimulcastStreams > 1; | 
 |     if (vcm_.RegisterSendCodec(¤t_send_codec, number_of_cores_, | 
 |                                max_data_payload_length) != VCM_OK) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "Could not use internal encoder"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::SetEncoder(const webrtc::VideoCodec& video_codec) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s: CodecType: %d, width: %u, height: %u", __FUNCTION__, | 
 |                video_codec.codecType, video_codec.width, video_codec.height); | 
 |   // Setting target width and height for VPM. | 
 |   if (vpm_.SetTargetResolution(video_codec.width, video_codec.height, | 
 |                                video_codec.maxFramerate) != VPM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not set VPM target dimensions"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (default_rtp_rtcp_->RegisterSendPayload(video_codec) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could register RTP module video payload"); | 
 |     return -1; | 
 |   } | 
 |   // Convert from kbps to bps. | 
 |   std::vector<uint32_t> stream_bitrates = AllocateStreamBitrates( | 
 |       video_codec.startBitrate * 1000, | 
 |       video_codec.simulcastStream, | 
 |       video_codec.numberOfSimulcastStreams); | 
 |   default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates); | 
 |  | 
 |   uint16_t max_data_payload_length = | 
 |       default_rtp_rtcp_->MaxDataPayloadLength(); | 
 |  | 
 |   send_padding_ = video_codec.numberOfSimulcastStreams > 1; | 
 |   if (vcm_.RegisterSendCodec(&video_codec, number_of_cores_, | 
 |                              max_data_payload_length) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not register send codec"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Set this module as sending right away, let the slave module in the channel | 
 |   // start and stop sending. | 
 |   if (default_rtp_rtcp_->Sending() == false) { | 
 |     if (default_rtp_rtcp_->SetSendingStatus(true) != 0) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "Could start RTP module sending"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |   bitrate_controller_->SetBitrateObserver(bitrate_observer_.get(), | 
 |                                           video_codec.startBitrate * 1000, | 
 |                                           video_codec.minBitrate * 1000, | 
 |                                           kTransmissionMaxBitrateMultiplier * | 
 |                                           video_codec.maxBitrate * 1000); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::GetEncoder(VideoCodec* video_codec) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |  | 
 |   if (vcm_.SendCodec(video_codec) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not get VCM send codec"); | 
 |     return -1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::GetCodecConfigParameters( | 
 |     unsigned char config_parameters[kConfigParameterSize], | 
 |     unsigned char& config_parameters_size) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |  | 
 |   int32_t num_parameters = | 
 |       vcm_.CodecConfigParameters(config_parameters, kConfigParameterSize); | 
 |   if (num_parameters <= 0) { | 
 |     config_parameters_size = 0; | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not get config parameters"); | 
 |     return -1; | 
 |   } | 
 |   config_parameters_size = static_cast<unsigned char>(num_parameters); | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::ScaleInputImage(bool enable) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s(enable %d)", __FUNCTION__, | 
 |                enable); | 
 |  | 
 |   VideoFrameResampling resampling_mode = kFastRescaling; | 
 |   if (enable == true) { | 
 |     // kInterpolation is currently not supported. | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s not supported", | 
 |                  __FUNCTION__, enable); | 
 |     return -1; | 
 |   } | 
 |   vpm_.SetInputFrameResampleMode(resampling_mode); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | bool ViEEncoder::TimeToSendPacket(uint32_t ssrc, | 
 |                                   uint16_t sequence_number, | 
 |                                   int64_t capture_time_ms, | 
 |                                   bool retransmission) { | 
 |   return default_rtp_rtcp_->TimeToSendPacket(ssrc, sequence_number, | 
 |                                              capture_time_ms, retransmission); | 
 | } | 
 |  | 
 | int ViEEncoder::TimeToSendPadding(int bytes) { | 
 |   bool send_padding; | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     send_padding = send_padding_ || video_suspended_; | 
 |   } | 
 |   if (send_padding) { | 
 |     return default_rtp_rtcp_->TimeToSendPadding(bytes); | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | bool ViEEncoder::EncoderPaused() const { | 
 |   // Pause video if paused by caller or as long as the network is down or the | 
 |   // pacer queue has grown too large in buffered mode. | 
 |   if (encoder_paused_) { | 
 |     return true; | 
 |   } | 
 |   if (target_delay_ms_ > 0) { | 
 |     // Buffered mode. | 
 |     // TODO(pwestin): Workaround until nack is configured as a time and not | 
 |     // number of packets. | 
 |     return paced_sender_->QueueInMs() >= | 
 |         std::max(static_cast<int>(target_delay_ms_ * kEncoderPausePacerMargin), | 
 |                  kMinPacingDelayMs); | 
 |   } | 
 |   return !network_is_transmitting_; | 
 | } | 
 |  | 
 | RtpRtcp* ViEEncoder::SendRtpRtcpModule() { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |  | 
 |   return default_rtp_rtcp_.get(); | 
 | } | 
 |  | 
 | void ViEEncoder::DeliverFrame(int id, | 
 |                               I420VideoFrame* video_frame, | 
 |                               int num_csrcs, | 
 |                               const uint32_t CSRC[kRtpCsrcSize]) { | 
 |   WEBRTC_TRACE(webrtc::kTraceStream, | 
 |                webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s: %llu", __FUNCTION__, | 
 |                video_frame->timestamp()); | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     time_of_last_incoming_frame_ms_ = TickTime::MillisecondTimestamp(); | 
 |     if (default_rtp_rtcp_->SendingMedia() == false) { | 
 |       // We've paused or we have no channels attached, don't encode. | 
 |       return; | 
 |     } | 
 |     if (EncoderPaused()) { | 
 |       if (!encoder_paused_and_dropped_frame_) { | 
 |         TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this); | 
 |       } | 
 |       encoder_paused_and_dropped_frame_ = true; | 
 |       return; | 
 |     } | 
 |     if (encoder_paused_and_dropped_frame_) { | 
 |       TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this); | 
 |     } | 
 |     encoder_paused_and_dropped_frame_ = false; | 
 |   } | 
 |  | 
 |   // Convert render time, in ms, to RTP timestamp. | 
 |   const int kMsToRtpTimestamp = 90; | 
 |   const uint32_t time_stamp = | 
 |       kMsToRtpTimestamp * | 
 |       static_cast<uint32_t>(video_frame->render_time_ms()); | 
 |  | 
 |   TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame->render_time_ms(), | 
 |                           "Encode"); | 
 |   video_frame->set_timestamp(time_stamp); | 
 |   { | 
 |     CriticalSectionScoped cs(callback_cs_.get()); | 
 |     if (effect_filter_) { | 
 |       unsigned int length = CalcBufferSize(kI420, | 
 |                                            video_frame->width(), | 
 |                                            video_frame->height()); | 
 |       scoped_array<uint8_t> video_buffer(new uint8_t[length]); | 
 |       ExtractBuffer(*video_frame, length, video_buffer.get()); | 
 |       effect_filter_->Transform(length, | 
 |                                 video_buffer.get(), | 
 |                                 video_frame->timestamp(), | 
 |                                 video_frame->width(), | 
 |                                 video_frame->height()); | 
 |     } | 
 |   } | 
 |  | 
 |   // Make sure the CSRC list is correct. | 
 |   if (num_csrcs > 0) { | 
 |     uint32_t tempCSRC[kRtpCsrcSize]; | 
 |     for (int i = 0; i < num_csrcs; i++) { | 
 |       if (CSRC[i] == 1) { | 
 |         tempCSRC[i] = default_rtp_rtcp_->SSRC(); | 
 |       } else { | 
 |         tempCSRC[i] = CSRC[i]; | 
 |       } | 
 |     } | 
 |     default_rtp_rtcp_->SetCSRCs(tempCSRC, (uint8_t) num_csrcs); | 
 |   } | 
 |   // Pass frame via preprocessor. | 
 |   I420VideoFrame* decimated_frame = NULL; | 
 |   const int ret = vpm_.PreprocessFrame(*video_frame, &decimated_frame); | 
 |   if (ret == 1) { | 
 |     // Drop this frame. | 
 |     return; | 
 |   } | 
 |   if (ret != VPM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, | 
 |                  webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s: Error preprocessing frame %u", __FUNCTION__, | 
 |                  video_frame->timestamp()); | 
 |     return; | 
 |   } | 
 |   // Frame was not sampled => use original. | 
 |   if (decimated_frame == NULL)  { | 
 |     decimated_frame = video_frame; | 
 |   } | 
 |  | 
 |   { | 
 |     CriticalSectionScoped cs(callback_cs_.get()); | 
 |     if (pre_encode_callback_) | 
 |       pre_encode_callback_->FrameCallback(decimated_frame); | 
 |   } | 
 |  | 
 | #ifdef VIDEOCODEC_VP8 | 
 |   if (vcm_.SendCodec() == webrtc::kVideoCodecVP8) { | 
 |     webrtc::CodecSpecificInfo codec_specific_info; | 
 |     codec_specific_info.codecType = webrtc::kVideoCodecVP8; | 
 |     codec_specific_info.codecSpecific.VP8.hasReceivedRPSI = | 
 |         has_received_rpsi_; | 
 |     codec_specific_info.codecSpecific.VP8.hasReceivedSLI = | 
 |         has_received_sli_; | 
 |     codec_specific_info.codecSpecific.VP8.pictureIdRPSI = | 
 |         picture_id_rpsi_; | 
 |     codec_specific_info.codecSpecific.VP8.pictureIdSLI  = | 
 |         picture_id_sli_; | 
 |     has_received_sli_ = false; | 
 |     has_received_rpsi_ = false; | 
 |  | 
 |     if (vcm_.AddVideoFrame(*decimated_frame, | 
 |                            vpm_.ContentMetrics(), | 
 |                            &codec_specific_info) != VCM_OK) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, | 
 |                    webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "%s: Error encoding frame %u", __FUNCTION__, | 
 |                    video_frame->timestamp()); | 
 |     } | 
 |     return; | 
 |   } | 
 | #endif | 
 |   if (vcm_.AddVideoFrame(*decimated_frame) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, | 
 |                  webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s: Error encoding frame %u", __FUNCTION__, | 
 |                  video_frame->timestamp()); | 
 |   } | 
 | } | 
 |  | 
 | void ViEEncoder::DelayChanged(int id, int frame_delay) { | 
 |   WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s: %u", __FUNCTION__, | 
 |                frame_delay); | 
 |  | 
 |   default_rtp_rtcp_->SetCameraDelay(frame_delay); | 
 | } | 
 |  | 
 | int ViEEncoder::GetPreferedFrameSettings(int* width, | 
 |                                          int* height, | 
 |                                          int* frame_rate) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |  | 
 |   webrtc::VideoCodec video_codec; | 
 |   memset(&video_codec, 0, sizeof(video_codec)); | 
 |   if (vcm_.SendCodec(&video_codec) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "Could not get VCM send codec"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   *width = video_codec.width; | 
 |   *height = video_codec.height; | 
 |   *frame_rate = video_codec.maxFramerate; | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEEncoder::SendKeyFrame() { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |   return vcm_.IntraFrameRequest(0); | 
 | } | 
 |  | 
 | int32_t ViEEncoder::SendCodecStatistics( | 
 |     uint32_t* num_key_frames, uint32_t* num_delta_frames) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |  | 
 |   webrtc::VCMFrameCount sent_frames; | 
 |   if (vcm_.SentFrameCount(sent_frames) != VCM_OK) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s: Could not get sent frame information", __FUNCTION__); | 
 |     return -1; | 
 |   } | 
 |   *num_key_frames = sent_frames.numKeyFrames; | 
 |   *num_delta_frames = sent_frames.numDeltaFrames; | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::EstimatedSendBandwidth( | 
 |     uint32_t* available_bandwidth) const { | 
 |   WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", | 
 |                __FUNCTION__); | 
 |  | 
 |   if (!bitrate_controller_->AvailableBandwidth(available_bandwidth)) { | 
 |     return -1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEEncoder::CodecTargetBitrate(uint32_t* bitrate) const { | 
 |   WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", | 
 |                __FUNCTION__); | 
 |   if (vcm_.Bitrate(bitrate) != 0) | 
 |     return -1; | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::UpdateProtectionMethod(bool enable_nack) { | 
 |   bool fec_enabled = false; | 
 |   uint8_t dummy_ptype_red = 0; | 
 |   uint8_t dummy_ptypeFEC = 0; | 
 |  | 
 |   // Updated protection method to VCM to get correct packetization sizes. | 
 |   // FEC has larger overhead than NACK -> set FEC if used. | 
 |   int32_t error = default_rtp_rtcp_->GenericFECStatus(fec_enabled, | 
 |                                                       dummy_ptype_red, | 
 |                                                       dummy_ptypeFEC); | 
 |   if (error) { | 
 |     return -1; | 
 |   } | 
 |   if (fec_enabled_ == fec_enabled && nack_enabled_ == enable_nack) { | 
 |     // No change needed, we're already in correct state. | 
 |     return 0; | 
 |   } | 
 |   fec_enabled_ = fec_enabled; | 
 |   nack_enabled_ = enable_nack; | 
 |  | 
 |   // Set Video Protection for VCM. | 
 |   if (fec_enabled && nack_enabled_) { | 
 |     vcm_.SetVideoProtection(webrtc::kProtectionNackFEC, true); | 
 |   } else { | 
 |     vcm_.SetVideoProtection(webrtc::kProtectionFEC, fec_enabled_); | 
 |     vcm_.SetVideoProtection(webrtc::kProtectionNackSender, nack_enabled_); | 
 |     vcm_.SetVideoProtection(webrtc::kProtectionNackFEC, false); | 
 |   } | 
 |  | 
 |   if (fec_enabled_ || nack_enabled_) { | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: FEC status ", | 
 |                  __FUNCTION__, fec_enabled); | 
 |     vcm_.RegisterProtectionCallback(this); | 
 |     // The send codec must be registered to set correct MTU. | 
 |     webrtc::VideoCodec codec; | 
 |     if (vcm_.SendCodec(&codec) == 0) { | 
 |       uint16_t max_pay_load = default_rtp_rtcp_->MaxDataPayloadLength(); | 
 |       uint32_t current_bitrate_bps = 0; | 
 |       if (vcm_.Bitrate(¤t_bitrate_bps) != 0) { | 
 |         WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, | 
 |                      ViEId(engine_id_, channel_id_), | 
 |                      "Failed to get the current encoder target bitrate."); | 
 |       } | 
 |       // Convert to start bitrate in kbps. | 
 |       codec.startBitrate = (current_bitrate_bps + 500) / 1000; | 
 |       if (vcm_.RegisterSendCodec(&codec, number_of_cores_, max_pay_load) != 0) { | 
 |         WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                      ViEId(engine_id_, channel_id_), | 
 |                      "%s: Failed to update Sendcodec when enabling FEC", | 
 |                      __FUNCTION__, fec_enabled); | 
 |         return -1; | 
 |       } | 
 |     } | 
 |     return 0; | 
 |   } else { | 
 |     // FEC and NACK are disabled. | 
 |     vcm_.RegisterProtectionCallback(NULL); | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | void ViEEncoder::SetSenderBufferingMode(int target_delay_ms) { | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     target_delay_ms_ = target_delay_ms; | 
 |   } | 
 |   if (target_delay_ms > 0) { | 
 |     // Disable external frame-droppers. | 
 |     vcm_.EnableFrameDropper(false); | 
 |     vpm_.EnableTemporalDecimation(false); | 
 |     // We don't put any limits on the pacer queue when running in buffered mode | 
 |     // since the encoder will be paused if the queue grow too large. | 
 |     paced_sender_->set_max_queue_length_ms(-1); | 
 |   } else { | 
 |     // Real-time mode - enable frame droppers. | 
 |     vpm_.EnableTemporalDecimation(true); | 
 |     vcm_.EnableFrameDropper(true); | 
 |     paced_sender_->set_max_queue_length_ms( | 
 |         PacedSender::kDefaultMaxQueueLengthMs); | 
 |   } | 
 | } | 
 |  | 
 | int32_t ViEEncoder::SendData( | 
 |     const FrameType frame_type, | 
 |     const uint8_t payload_type, | 
 |     const uint32_t time_stamp, | 
 |     int64_t capture_time_ms, | 
 |     const uint8_t* payload_data, | 
 |     const uint32_t payload_size, | 
 |     const webrtc::RTPFragmentationHeader& fragmentation_header, | 
 |     const RTPVideoHeader* rtp_video_hdr) { | 
 |   // New encoded data, hand over to the rtp module. | 
 |   return default_rtp_rtcp_->SendOutgoingData(frame_type, | 
 |                                              payload_type, | 
 |                                              time_stamp, | 
 |                                              capture_time_ms, | 
 |                                              payload_data, | 
 |                                              payload_size, | 
 |                                              &fragmentation_header, | 
 |                                              rtp_video_hdr); | 
 | } | 
 |  | 
 | int32_t ViEEncoder::ProtectionRequest( | 
 |     const FecProtectionParams* delta_fec_params, | 
 |     const FecProtectionParams* key_fec_params, | 
 |     uint32_t* sent_video_rate_bps, | 
 |     uint32_t* sent_nack_rate_bps, | 
 |     uint32_t* sent_fec_rate_bps) { | 
 |   WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s, deltaFECRate: %u, key_fecrate: %u, " | 
 |                "delta_use_uep_protection: %d, key_use_uep_protection: %d, " | 
 |                "delta_max_fec_frames: %d, key_max_fec_frames: %d, " | 
 |                "delta_mask_type: %d, key_mask_type: %d, ", | 
 |                __FUNCTION__, | 
 |                delta_fec_params->fec_rate, | 
 |                key_fec_params->fec_rate, | 
 |                delta_fec_params->use_uep_protection, | 
 |                key_fec_params->use_uep_protection, | 
 |                delta_fec_params->max_fec_frames, | 
 |                key_fec_params->max_fec_frames, | 
 |                delta_fec_params->fec_mask_type, | 
 |                key_fec_params->fec_mask_type); | 
 |   if (default_rtp_rtcp_->SetFecParameters(delta_fec_params, | 
 |                                          key_fec_params) != 0) { | 
 |     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s: Could not update FEC parameters", __FUNCTION__); | 
 |   } | 
 |   default_rtp_rtcp_->BitrateSent(NULL, | 
 |                                 sent_video_rate_bps, | 
 |                                 sent_fec_rate_bps, | 
 |                                 sent_nack_rate_bps); | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::SendStatistics(const uint32_t bit_rate, | 
 |                                    const uint32_t frame_rate) { | 
 |   CriticalSectionScoped cs(callback_cs_.get()); | 
 |   if (codec_observer_) { | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: bitrate %u, framerate %u", | 
 |                  __FUNCTION__, bit_rate, frame_rate); | 
 |     codec_observer_->OutgoingRate(channel_id_, frame_rate, bit_rate); | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t ViEEncoder::RegisterCodecObserver(ViEEncoderObserver* observer) { | 
 |   CriticalSectionScoped cs(callback_cs_.get()); | 
 |   if (observer) { | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: observer added", | 
 |                  __FUNCTION__); | 
 |     if (codec_observer_) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), "%s: observer already set.", | 
 |                    __FUNCTION__); | 
 |       return -1; | 
 |     } | 
 |     codec_observer_ = observer; | 
 |   } else { | 
 |     if (codec_observer_ == NULL) { | 
 |       WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "%s: observer does not exist.", __FUNCTION__); | 
 |       return -1; | 
 |     } | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: observer removed", | 
 |                  __FUNCTION__); | 
 |     codec_observer_ = NULL; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | void ViEEncoder::OnReceivedSLI(uint32_t /*ssrc*/, | 
 |                                uint8_t picture_id) { | 
 |   picture_id_sli_ = picture_id; | 
 |   has_received_sli_ = true; | 
 | } | 
 |  | 
 | void ViEEncoder::OnReceivedRPSI(uint32_t /*ssrc*/, | 
 |                                 uint64_t picture_id) { | 
 |   picture_id_rpsi_ = picture_id; | 
 |   has_received_rpsi_ = true; | 
 | } | 
 |  | 
 | void ViEEncoder::OnReceivedIntraFrameRequest(uint32_t ssrc) { | 
 |   // Key frame request from remote side, signal to VCM. | 
 |   WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); | 
 |   TRACE_EVENT0("webrtc", "OnKeyFrameRequest"); | 
 |  | 
 |   int idx = 0; | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     std::map<unsigned int, int>::iterator stream_it = ssrc_streams_.find(ssrc); | 
 |     if (stream_it == ssrc_streams_.end()) { | 
 |       LOG_F(LS_WARNING) << "ssrc not found: " << ssrc << ", map size " | 
 |                         << ssrc_streams_.size(); | 
 |       return; | 
 |     } | 
 |     std::map<unsigned int, int64_t>::iterator time_it = | 
 |         time_last_intra_request_ms_.find(ssrc); | 
 |     if (time_it == time_last_intra_request_ms_.end()) { | 
 |       time_last_intra_request_ms_[ssrc] = 0; | 
 |     } | 
 |  | 
 |     int64_t now = TickTime::MillisecondTimestamp(); | 
 |     if (time_last_intra_request_ms_[ssrc] + kViEMinKeyRequestIntervalMs > now) { | 
 |       WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "%s: Not encoding new intra due to timing", __FUNCTION__); | 
 |       return; | 
 |     } | 
 |     time_last_intra_request_ms_[ssrc] = now; | 
 |     idx = stream_it->second; | 
 |   } | 
 |   // Release the critsect before triggering key frame. | 
 |   vcm_.IntraFrameRequest(idx); | 
 | } | 
 |  | 
 | void ViEEncoder::OnLocalSsrcChanged(uint32_t old_ssrc, uint32_t new_ssrc) { | 
 |   CriticalSectionScoped cs(data_cs_.get()); | 
 |   std::map<unsigned int, int>::iterator it = ssrc_streams_.find(old_ssrc); | 
 |   if (it == ssrc_streams_.end()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   ssrc_streams_[new_ssrc] = it->second; | 
 |   ssrc_streams_.erase(it); | 
 |  | 
 |   std::map<unsigned int, int64_t>::iterator time_it = | 
 |       time_last_intra_request_ms_.find(old_ssrc); | 
 |   int64_t last_intra_request_ms = 0; | 
 |   if (time_it != time_last_intra_request_ms_.end()) { | 
 |     last_intra_request_ms = time_it->second; | 
 |     time_last_intra_request_ms_.erase(time_it); | 
 |   } | 
 |   time_last_intra_request_ms_[new_ssrc] = last_intra_request_ms; | 
 | } | 
 |  | 
 | bool ViEEncoder::SetSsrcs(const std::list<unsigned int>& ssrcs) { | 
 |   VideoCodec codec; | 
 |   if (vcm_.SendCodec(&codec) != 0) | 
 |     return false; | 
 |  | 
 |   if (codec.numberOfSimulcastStreams > 0 && | 
 |       ssrcs.size() != codec.numberOfSimulcastStreams) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   CriticalSectionScoped cs(data_cs_.get()); | 
 |   ssrc_streams_.clear(); | 
 |   time_last_intra_request_ms_.clear(); | 
 |   int idx = 0; | 
 |   for (std::list<unsigned int>::const_iterator it = ssrcs.begin(); | 
 |        it != ssrcs.end(); ++it, ++idx) { | 
 |     unsigned int ssrc = *it; | 
 |     ssrc_streams_[ssrc] = idx; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | // Called from ViEBitrateObserver. | 
 | void ViEEncoder::OnNetworkChanged(const uint32_t bitrate_bps, | 
 |                                   const uint8_t fraction_lost, | 
 |                                   const uint32_t round_trip_time_ms) { | 
 |   WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                ViEId(engine_id_, channel_id_), | 
 |                "%s(bitrate_bps: %u, fraction_lost: %u, rtt_ms: %u", | 
 |                __FUNCTION__, bitrate_bps, fraction_lost, round_trip_time_ms); | 
 |  | 
 |   vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms); | 
 |   bool video_is_suspended = vcm_.VideoSuspended(); | 
 |   int bitrate_kbps = bitrate_bps / 1000; | 
 |   VideoCodec send_codec; | 
 |   if (vcm_.SendCodec(&send_codec) != 0) { | 
 |     return; | 
 |   } | 
 |   SimulcastStream* stream_configs = send_codec.simulcastStream; | 
 |   // Allocate the bandwidth between the streams. | 
 |   std::vector<uint32_t> stream_bitrates = AllocateStreamBitrates( | 
 |       bitrate_bps, | 
 |       stream_configs, | 
 |       send_codec.numberOfSimulcastStreams); | 
 |   // Find the max amount of padding we can allow ourselves to send at this | 
 |   // point, based on which streams are currently active and what our current | 
 |   // available bandwidth is. | 
 |   int max_padding_bitrate_kbps = 0; | 
 |   int pad_up_to_bitrate_kbps = 0; | 
 |   if (send_codec.numberOfSimulcastStreams == 0) { | 
 |     max_padding_bitrate_kbps = send_codec.minBitrate; | 
 |     pad_up_to_bitrate_kbps = send_codec.minBitrate; | 
 |   } else { | 
 |     int i = send_codec.numberOfSimulcastStreams - 1; | 
 |     for (std::vector<uint32_t>::reverse_iterator it = stream_bitrates.rbegin(); | 
 |         it != stream_bitrates.rend(); ++it) { | 
 |       if (*it > 0) { | 
 |         max_padding_bitrate_kbps = std::min((*it + 500) / 1000, | 
 |                                             stream_configs[i].minBitrate); | 
 |         break; | 
 |       } | 
 |       --i; | 
 |     } | 
 |     pad_up_to_bitrate_kbps = | 
 |         stream_configs[send_codec.numberOfSimulcastStreams - 1].minBitrate; | 
 |     for (int i = 0; i < send_codec.numberOfSimulcastStreams - 1; ++i) { | 
 |       pad_up_to_bitrate_kbps += stream_configs[i].targetBitrate; | 
 |     } | 
 |   } | 
 |   if (video_is_suspended || send_codec.numberOfSimulcastStreams > 1) { | 
 |     pad_up_to_bitrate_kbps = std::min(bitrate_kbps, pad_up_to_bitrate_kbps); | 
 |   } else { | 
 |     // Disable padding if only sending one stream and video isn't suspended. | 
 |     pad_up_to_bitrate_kbps = 0; | 
 |   } | 
 |  | 
 |   { | 
 |     // The amount of padding should decay to zero if no frames are being | 
 |     // captured. | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     int64_t now_ms = TickTime::MillisecondTimestamp(); | 
 |     if (now_ms - time_of_last_incoming_frame_ms_ > kStopPaddingThresholdMs) | 
 |       max_padding_bitrate_kbps = 0; | 
 |   } | 
 |  | 
 |   paced_sender_->UpdateBitrate(bitrate_kbps, | 
 |                                max_padding_bitrate_kbps, | 
 |                                pad_up_to_bitrate_kbps); | 
 |   default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates); | 
 |   { | 
 |     CriticalSectionScoped cs(data_cs_.get()); | 
 |     if (video_suspended_ == video_is_suspended) | 
 |       return; | 
 |     video_suspended_ = video_is_suspended; | 
 |   } | 
 |   // State changed, inform codec observer. | 
 |   if (codec_observer_) { | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), | 
 |                  "%s: video_suspended_ changed to %i", | 
 |                  __FUNCTION__, video_is_suspended); | 
 |     codec_observer_->SuspendChange(channel_id_, video_is_suspended); | 
 |   } | 
 | } | 
 |  | 
 | PacedSender* ViEEncoder::GetPacedSender() { | 
 |   return paced_sender_.get(); | 
 | } | 
 |  | 
 | int32_t ViEEncoder::RegisterEffectFilter(ViEEffectFilter* effect_filter) { | 
 |   CriticalSectionScoped cs(callback_cs_.get()); | 
 |   if (effect_filter == NULL) { | 
 |     if (effect_filter_ == NULL) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), "%s: no effect filter added", | 
 |                    __FUNCTION__); | 
 |       return -1; | 
 |     } | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: deregister effect filter", | 
 |                  __FUNCTION__); | 
 |   } else { | 
 |     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, | 
 |                  ViEId(engine_id_, channel_id_), "%s: register effect", | 
 |                  __FUNCTION__); | 
 |     if (effect_filter_) { | 
 |       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, | 
 |                    ViEId(engine_id_, channel_id_), | 
 |                    "%s: effect filter already added ", __FUNCTION__); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |   effect_filter_ = effect_filter; | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEEncoder::StartDebugRecording(const char* fileNameUTF8) { | 
 |   return vcm_.StartDebugRecording(fileNameUTF8); | 
 | } | 
 |  | 
 | int ViEEncoder::StopDebugRecording() { | 
 |   return vcm_.StopDebugRecording(); | 
 | } | 
 |  | 
 | void ViEEncoder::SuspendBelowMinBitrate() { | 
 |   vcm_.SuspendBelowMinBitrate(); | 
 |   bitrate_controller_->EnforceMinBitrate(false); | 
 | } | 
 |  | 
 | void ViEEncoder::RegisterPreEncodeCallback( | 
 |     I420FrameCallback* pre_encode_callback) { | 
 |   CriticalSectionScoped cs(callback_cs_.get()); | 
 |   pre_encode_callback_ = pre_encode_callback; | 
 | } | 
 |  | 
 | void ViEEncoder::DeRegisterPreEncodeCallback() { | 
 |   CriticalSectionScoped cs(callback_cs_.get()); | 
 |   pre_encode_callback_ = NULL; | 
 | } | 
 |  | 
 | void ViEEncoder::RegisterPostEncodeImageCallback( | 
 |       EncodedImageCallback* post_encode_callback) { | 
 |   vcm_.RegisterPostEncodeImageCallback(post_encode_callback); | 
 | } | 
 |  | 
 | void ViEEncoder::DeRegisterPostEncodeImageCallback() { | 
 |   vcm_.RegisterPostEncodeImageCallback(NULL); | 
 | } | 
 |  | 
 | QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessingModule* vpm) | 
 |     : vpm_(vpm) { | 
 | } | 
 |  | 
 | QMVideoSettingsCallback::~QMVideoSettingsCallback() { | 
 | } | 
 |  | 
 | int32_t QMVideoSettingsCallback::SetVideoQMSettings( | 
 |     const uint32_t frame_rate, | 
 |     const uint32_t width, | 
 |     const uint32_t height) { | 
 |   return vpm_->SetTargetResolution(width, height, frame_rate); | 
 | } | 
 |  | 
 | }  // namespace webrtc |