1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/video/vie_encoder.h" 12 13 #include <assert.h> 14 15 #include <algorithm> 16 17 #include "webrtc/base/checks.h" 18 #include "webrtc/base/logging.h" 19 #include "webrtc/base/trace_event.h" 20 #include "webrtc/call/bitrate_allocator.h" 21 #include "webrtc/common_video/include/video_image.h" 22 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 23 #include "webrtc/frame_callback.h" 24 #include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" 25 #include "webrtc/modules/pacing/paced_sender.h" 26 #include "webrtc/modules/utility/include/process_thread.h" 27 #include "webrtc/modules/video_coding/include/video_codec_interface.h" 28 #include "webrtc/modules/video_coding/include/video_coding.h" 29 #include "webrtc/modules/video_coding/include/video_coding_defines.h" 30 #include "webrtc/modules/video_coding/encoded_frame.h" 31 #include "webrtc/system_wrappers/include/clock.h" 32 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" 33 #include "webrtc/system_wrappers/include/metrics.h" 34 #include "webrtc/system_wrappers/include/tick_util.h" 35 #include "webrtc/video/payload_router.h" 36 #include "webrtc/video/send_statistics_proxy.h" 37 38 namespace webrtc { 39 40 // Margin on when we pause the encoder when the pacing buffer overflows relative 41 // to the configured buffer delay. 42 static const float kEncoderPausePacerMargin = 2.0f; 43 44 // Don't stop the encoder unless the delay is above this configured value. 45 static const int kMinPacingDelayMs = 200; 46 47 static const float kStopPaddingThresholdMs = 2000; 48 49 static const int kMinKeyFrameRequestIntervalMs = 300; 50 51 std::vector<uint32_t> AllocateStreamBitrates( 52 uint32_t total_bitrate, 53 const SimulcastStream* stream_configs, 54 size_t number_of_streams) { 55 if (number_of_streams == 0) { 56 std::vector<uint32_t> stream_bitrates(1, 0); 57 stream_bitrates[0] = total_bitrate; 58 return stream_bitrates; 59 } 60 std::vector<uint32_t> stream_bitrates(number_of_streams, 0); 61 uint32_t bitrate_remainder = total_bitrate; 62 for (size_t i = 0; i < stream_bitrates.size() && bitrate_remainder > 0; ++i) { 63 if (stream_configs[i].maxBitrate * 1000 > bitrate_remainder) { 64 stream_bitrates[i] = bitrate_remainder; 65 } else { 66 stream_bitrates[i] = stream_configs[i].maxBitrate * 1000; 67 } 68 bitrate_remainder -= stream_bitrates[i]; 69 } 70 return stream_bitrates; 71 } 72 73 class QMVideoSettingsCallback : public VCMQMSettingsCallback { 74 public: 75 explicit QMVideoSettingsCallback(VideoProcessing* vpm); 76 77 ~QMVideoSettingsCallback(); 78 79 // Update VPM with QM (quality modes: frame size & frame rate) settings. 80 int32_t SetVideoQMSettings(const uint32_t frame_rate, 81 const uint32_t width, 82 const uint32_t height); 83 84 // Update target frame rate. 85 void SetTargetFramerate(int frame_rate); 86 87 private: 88 VideoProcessing* vp_; 89 }; 90 91 class ViEBitrateObserver : public BitrateObserver { 92 public: 93 explicit ViEBitrateObserver(ViEEncoder* owner) 94 : owner_(owner) { 95 } 96 virtual ~ViEBitrateObserver() {} 97 // Implements BitrateObserver. 98 virtual void OnNetworkChanged(uint32_t bitrate_bps, 99 uint8_t fraction_lost, 100 int64_t rtt) { 101 owner_->OnNetworkChanged(bitrate_bps, fraction_lost, rtt); 102 } 103 private: 104 ViEEncoder* owner_; 105 }; 106 107 ViEEncoder::ViEEncoder(uint32_t number_of_cores, 108 ProcessThread* module_process_thread, 109 SendStatisticsProxy* stats_proxy, 110 I420FrameCallback* pre_encode_callback, 111 PacedSender* pacer, 112 BitrateAllocator* bitrate_allocator) 113 : number_of_cores_(number_of_cores), 114 vp_(VideoProcessing::Create()), 115 qm_callback_(new QMVideoSettingsCallback(vp_.get())), 116 vcm_(VideoCodingModule::Create(Clock::GetRealTimeClock(), 117 this, 118 qm_callback_.get())), 119 send_payload_router_(NULL), 120 data_cs_(CriticalSectionWrapper::CreateCriticalSection()), 121 stats_proxy_(stats_proxy), 122 pre_encode_callback_(pre_encode_callback), 123 pacer_(pacer), 124 bitrate_allocator_(bitrate_allocator), 125 time_of_last_frame_activity_ms_(0), 126 encoder_config_(), 127 min_transmit_bitrate_kbps_(0), 128 last_observed_bitrate_bps_(0), 129 target_delay_ms_(0), 130 network_is_transmitting_(true), 131 encoder_paused_(false), 132 encoder_paused_and_dropped_frame_(false), 133 module_process_thread_(module_process_thread), 134 has_received_sli_(false), 135 picture_id_sli_(0), 136 has_received_rpsi_(false), 137 picture_id_rpsi_(0), 138 video_suspended_(false) { 139 bitrate_observer_.reset(new ViEBitrateObserver(this)); 140 } 141 142 bool ViEEncoder::Init() { 143 vp_->EnableTemporalDecimation(true); 144 145 // Enable/disable content analysis: off by default for now. 146 vp_->EnableContentAnalysis(false); 147 148 if (vcm_->RegisterTransportCallback(this) != 0) { 149 return false; 150 } 151 if (vcm_->RegisterSendStatisticsCallback(this) != 0) { 152 return false; 153 } 154 return true; 155 } 156 157 void ViEEncoder::StartThreadsAndSetSharedMembers( 158 rtc::scoped_refptr<PayloadRouter> send_payload_router, 159 VCMProtectionCallback* vcm_protection_callback) { 160 RTC_DCHECK(send_payload_router_ == NULL); 161 162 send_payload_router_ = send_payload_router; 163 vcm_->RegisterProtectionCallback(vcm_protection_callback); 164 module_process_thread_->RegisterModule(vcm_.get()); 165 } 166 167 void ViEEncoder::StopThreadsAndRemoveSharedMembers() { 168 if (bitrate_allocator_) 169 bitrate_allocator_->RemoveBitrateObserver(bitrate_observer_.get()); 170 module_process_thread_->DeRegisterModule(vcm_.get()); 171 } 172 173 ViEEncoder::~ViEEncoder() { 174 } 175 176 void ViEEncoder::SetNetworkTransmissionState(bool is_transmitting) { 177 { 178 CriticalSectionScoped cs(data_cs_.get()); 179 network_is_transmitting_ = is_transmitting; 180 } 181 } 182 183 void ViEEncoder::Pause() { 184 CriticalSectionScoped cs(data_cs_.get()); 185 encoder_paused_ = true; 186 } 187 188 void ViEEncoder::Restart() { 189 CriticalSectionScoped cs(data_cs_.get()); 190 encoder_paused_ = false; 191 } 192 193 int32_t ViEEncoder::RegisterExternalEncoder(webrtc::VideoEncoder* encoder, 194 uint8_t pl_type, 195 bool internal_source) { 196 if (vcm_->RegisterExternalEncoder(encoder, pl_type, internal_source) != 197 VCM_OK) { 198 return -1; 199 } 200 return 0; 201 } 202 203 int32_t ViEEncoder::DeRegisterExternalEncoder(uint8_t pl_type) { 204 if (vcm_->RegisterExternalEncoder(NULL, pl_type) != VCM_OK) { 205 return -1; 206 } 207 return 0; 208 } 209 210 int32_t ViEEncoder::SetEncoder(const webrtc::VideoCodec& video_codec) { 211 RTC_DCHECK(send_payload_router_ != NULL); 212 // Setting target width and height for VPM. 213 if (vp_->SetTargetResolution(video_codec.width, video_codec.height, 214 video_codec.maxFramerate) != VPM_OK) { 215 return -1; 216 } 217 218 // Cache codec before calling AddBitrateObserver (which calls OnNetworkChanged 219 // that makes use of the number of simulcast streams configured). 220 { 221 CriticalSectionScoped cs(data_cs_.get()); 222 encoder_config_ = video_codec; 223 } 224 225 // Add a bitrate observer to the allocator and update the start, max and 226 // min bitrates of the bitrate controller as needed. 227 int allocated_bitrate_bps = bitrate_allocator_->AddBitrateObserver( 228 bitrate_observer_.get(), video_codec.minBitrate * 1000, 229 video_codec.maxBitrate * 1000); 230 231 webrtc::VideoCodec modified_video_codec = video_codec; 232 modified_video_codec.startBitrate = allocated_bitrate_bps / 1000; 233 234 size_t max_data_payload_length = send_payload_router_->MaxPayloadLength(); 235 if (vcm_->RegisterSendCodec(&modified_video_codec, number_of_cores_, 236 static_cast<uint32_t>(max_data_payload_length)) != 237 VCM_OK) { 238 return -1; 239 } 240 return 0; 241 } 242 243 int ViEEncoder::GetPaddingNeededBps() const { 244 int64_t time_of_last_frame_activity_ms; 245 int min_transmit_bitrate_bps; 246 int bitrate_bps; 247 VideoCodec send_codec; 248 { 249 CriticalSectionScoped cs(data_cs_.get()); 250 bool send_padding = encoder_config_.numberOfSimulcastStreams > 1 || 251 video_suspended_ || min_transmit_bitrate_kbps_ > 0; 252 if (!send_padding) 253 return 0; 254 time_of_last_frame_activity_ms = time_of_last_frame_activity_ms_; 255 min_transmit_bitrate_bps = 1000 * min_transmit_bitrate_kbps_; 256 bitrate_bps = last_observed_bitrate_bps_; 257 send_codec = encoder_config_; 258 } 259 260 bool video_is_suspended = vcm_->VideoSuspended(); 261 262 // Find the max amount of padding we can allow ourselves to send at this 263 // point, based on which streams are currently active and what our current 264 // available bandwidth is. 265 int pad_up_to_bitrate_bps = 0; 266 if (send_codec.numberOfSimulcastStreams == 0) { 267 pad_up_to_bitrate_bps = send_codec.minBitrate * 1000; 268 } else { 269 SimulcastStream* stream_configs = send_codec.simulcastStream; 270 pad_up_to_bitrate_bps = 271 stream_configs[send_codec.numberOfSimulcastStreams - 1].minBitrate * 272 1000; 273 for (int i = 0; i < send_codec.numberOfSimulcastStreams - 1; ++i) { 274 pad_up_to_bitrate_bps += stream_configs[i].targetBitrate * 1000; 275 } 276 } 277 278 // Disable padding if only sending one stream and video isn't suspended and 279 // min-transmit bitrate isn't used (applied later). 280 if (!video_is_suspended && send_codec.numberOfSimulcastStreams <= 1) 281 pad_up_to_bitrate_bps = 0; 282 283 // The amount of padding should decay to zero if no frames are being 284 // captured/encoded unless a min-transmit bitrate is used. 285 int64_t now_ms = TickTime::MillisecondTimestamp(); 286 if (now_ms - time_of_last_frame_activity_ms > kStopPaddingThresholdMs) 287 pad_up_to_bitrate_bps = 0; 288 289 // Pad up to min bitrate. 290 if (pad_up_to_bitrate_bps < min_transmit_bitrate_bps) 291 pad_up_to_bitrate_bps = min_transmit_bitrate_bps; 292 293 // Padding may never exceed bitrate estimate. 294 if (pad_up_to_bitrate_bps > bitrate_bps) 295 pad_up_to_bitrate_bps = bitrate_bps; 296 297 return pad_up_to_bitrate_bps; 298 } 299 300 bool ViEEncoder::EncoderPaused() const { 301 // Pause video if paused by caller or as long as the network is down or the 302 // pacer queue has grown too large in buffered mode. 303 if (encoder_paused_) { 304 return true; 305 } 306 if (target_delay_ms_ > 0) { 307 // Buffered mode. 308 // TODO(pwestin): Workaround until nack is configured as a time and not 309 // number of packets. 310 return pacer_->QueueInMs() >= 311 std::max( 312 static_cast<int>(target_delay_ms_ * kEncoderPausePacerMargin), 313 kMinPacingDelayMs); 314 } 315 if (pacer_->ExpectedQueueTimeMs() > PacedSender::kMaxQueueLengthMs) { 316 // Too much data in pacer queue, drop frame. 317 return true; 318 } 319 return !network_is_transmitting_; 320 } 321 322 void ViEEncoder::TraceFrameDropStart() { 323 // Start trace event only on the first frame after encoder is paused. 324 if (!encoder_paused_and_dropped_frame_) { 325 TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this); 326 } 327 encoder_paused_and_dropped_frame_ = true; 328 return; 329 } 330 331 void ViEEncoder::TraceFrameDropEnd() { 332 // End trace event on first frame after encoder resumes, if frame was dropped. 333 if (encoder_paused_and_dropped_frame_) { 334 TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this); 335 } 336 encoder_paused_and_dropped_frame_ = false; 337 } 338 339 void ViEEncoder::DeliverFrame(VideoFrame video_frame) { 340 RTC_DCHECK(send_payload_router_ != NULL); 341 if (!send_payload_router_->active()) { 342 // We've paused or we have no channels attached, don't waste resources on 343 // encoding. 344 return; 345 } 346 VideoCodecType codec_type; 347 { 348 CriticalSectionScoped cs(data_cs_.get()); 349 time_of_last_frame_activity_ms_ = TickTime::MillisecondTimestamp(); 350 if (EncoderPaused()) { 351 TraceFrameDropStart(); 352 return; 353 } 354 TraceFrameDropEnd(); 355 codec_type = encoder_config_.codecType; 356 } 357 358 TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame.render_time_ms(), 359 "Encode"); 360 const VideoFrame* frame_to_send = &video_frame; 361 // TODO(wuchengli): support texture frames. 362 if (video_frame.native_handle() == NULL) { 363 // Pass frame via preprocessor. 364 frame_to_send = vp_->PreprocessFrame(video_frame); 365 if (!frame_to_send) { 366 // Drop this frame, or there was an error processing it. 367 return; 368 } 369 } 370 371 // If we haven't resampled the frame and we have a FrameCallback, we need to 372 // make a deep copy of |video_frame|. 373 VideoFrame copied_frame; 374 if (pre_encode_callback_) { 375 copied_frame.CopyFrame(*frame_to_send); 376 pre_encode_callback_->FrameCallback(&copied_frame); 377 frame_to_send = &copied_frame; 378 } 379 380 if (codec_type == webrtc::kVideoCodecVP8) { 381 webrtc::CodecSpecificInfo codec_specific_info; 382 codec_specific_info.codecType = webrtc::kVideoCodecVP8; 383 { 384 CriticalSectionScoped cs(data_cs_.get()); 385 codec_specific_info.codecSpecific.VP8.hasReceivedRPSI = 386 has_received_rpsi_; 387 codec_specific_info.codecSpecific.VP8.hasReceivedSLI = 388 has_received_sli_; 389 codec_specific_info.codecSpecific.VP8.pictureIdRPSI = 390 picture_id_rpsi_; 391 codec_specific_info.codecSpecific.VP8.pictureIdSLI = 392 picture_id_sli_; 393 has_received_sli_ = false; 394 has_received_rpsi_ = false; 395 } 396 397 vcm_->AddVideoFrame(*frame_to_send, vp_->GetContentMetrics(), 398 &codec_specific_info); 399 return; 400 } 401 vcm_->AddVideoFrame(*frame_to_send); 402 } 403 404 int ViEEncoder::SendKeyFrame() { 405 return vcm_->IntraFrameRequest(0); 406 } 407 408 uint32_t ViEEncoder::LastObservedBitrateBps() const { 409 CriticalSectionScoped cs(data_cs_.get()); 410 return last_observed_bitrate_bps_; 411 } 412 413 int ViEEncoder::CodecTargetBitrate(uint32_t* bitrate) const { 414 if (vcm_->Bitrate(bitrate) != 0) 415 return -1; 416 return 0; 417 } 418 419 void ViEEncoder::SetProtectionMethod(bool nack, bool fec) { 420 // Set Video Protection for VCM. 421 VCMVideoProtection protection_mode; 422 if (fec) { 423 protection_mode = 424 nack ? webrtc::kProtectionNackFEC : kProtectionFEC; 425 } else { 426 protection_mode = nack ? kProtectionNack : kProtectionNone; 427 } 428 vcm_->SetVideoProtection(protection_mode, true); 429 } 430 431 void ViEEncoder::SetSenderBufferingMode(int target_delay_ms) { 432 { 433 CriticalSectionScoped cs(data_cs_.get()); 434 target_delay_ms_ = target_delay_ms; 435 } 436 if (target_delay_ms > 0) { 437 // Disable external frame-droppers. 438 vcm_->EnableFrameDropper(false); 439 vp_->EnableTemporalDecimation(false); 440 } else { 441 // Real-time mode - enable frame droppers. 442 vp_->EnableTemporalDecimation(true); 443 vcm_->EnableFrameDropper(true); 444 } 445 } 446 447 void ViEEncoder::OnSetRates(uint32_t bitrate_bps, int framerate) { 448 if (stats_proxy_) 449 stats_proxy_->OnSetRates(bitrate_bps, framerate); 450 } 451 452 int32_t ViEEncoder::SendData( 453 const uint8_t payload_type, 454 const EncodedImage& encoded_image, 455 const webrtc::RTPFragmentationHeader& fragmentation_header, 456 const RTPVideoHeader* rtp_video_hdr) { 457 RTC_DCHECK(send_payload_router_ != NULL); 458 459 { 460 CriticalSectionScoped cs(data_cs_.get()); 461 time_of_last_frame_activity_ms_ = TickTime::MillisecondTimestamp(); 462 } 463 464 if (stats_proxy_ != NULL) 465 stats_proxy_->OnSendEncodedImage(encoded_image, rtp_video_hdr); 466 467 return send_payload_router_->RoutePayload( 468 encoded_image._frameType, payload_type, encoded_image._timeStamp, 469 encoded_image.capture_time_ms_, encoded_image._buffer, 470 encoded_image._length, &fragmentation_header, rtp_video_hdr) 471 ? 0 472 : -1; 473 } 474 475 void ViEEncoder::OnEncoderImplementationName( 476 const char* implementation_name) { 477 if (stats_proxy_) 478 stats_proxy_->OnEncoderImplementationName(implementation_name); 479 } 480 481 int32_t ViEEncoder::SendStatistics(const uint32_t bit_rate, 482 const uint32_t frame_rate) { 483 if (stats_proxy_) 484 stats_proxy_->OnOutgoingRate(frame_rate, bit_rate); 485 return 0; 486 } 487 488 void ViEEncoder::OnReceivedSLI(uint32_t /*ssrc*/, 489 uint8_t picture_id) { 490 CriticalSectionScoped cs(data_cs_.get()); 491 picture_id_sli_ = picture_id; 492 has_received_sli_ = true; 493 } 494 495 void ViEEncoder::OnReceivedRPSI(uint32_t /*ssrc*/, 496 uint64_t picture_id) { 497 CriticalSectionScoped cs(data_cs_.get()); 498 picture_id_rpsi_ = picture_id; 499 has_received_rpsi_ = true; 500 } 501 502 void ViEEncoder::OnReceivedIntraFrameRequest(uint32_t ssrc) { 503 // Key frame request from remote side, signal to VCM. 504 TRACE_EVENT0("webrtc", "OnKeyFrameRequest"); 505 506 int idx = 0; 507 { 508 CriticalSectionScoped cs(data_cs_.get()); 509 auto stream_it = ssrc_streams_.find(ssrc); 510 if (stream_it == ssrc_streams_.end()) { 511 LOG_F(LS_WARNING) << "ssrc not found: " << ssrc << ", map size " 512 << ssrc_streams_.size(); 513 return; 514 } 515 std::map<unsigned int, int64_t>::iterator time_it = 516 time_last_intra_request_ms_.find(ssrc); 517 if (time_it == time_last_intra_request_ms_.end()) { 518 time_last_intra_request_ms_[ssrc] = 0; 519 } 520 521 int64_t now = TickTime::MillisecondTimestamp(); 522 if (time_last_intra_request_ms_[ssrc] + kMinKeyFrameRequestIntervalMs 523 > now) { 524 return; 525 } 526 time_last_intra_request_ms_[ssrc] = now; 527 idx = stream_it->second; 528 } 529 // Release the critsect before triggering key frame. 530 vcm_->IntraFrameRequest(idx); 531 } 532 533 void ViEEncoder::OnLocalSsrcChanged(uint32_t old_ssrc, uint32_t new_ssrc) { 534 CriticalSectionScoped cs(data_cs_.get()); 535 std::map<unsigned int, int>::iterator it = ssrc_streams_.find(old_ssrc); 536 if (it == ssrc_streams_.end()) { 537 return; 538 } 539 540 ssrc_streams_[new_ssrc] = it->second; 541 ssrc_streams_.erase(it); 542 543 std::map<unsigned int, int64_t>::iterator time_it = 544 time_last_intra_request_ms_.find(old_ssrc); 545 int64_t last_intra_request_ms = 0; 546 if (time_it != time_last_intra_request_ms_.end()) { 547 last_intra_request_ms = time_it->second; 548 time_last_intra_request_ms_.erase(time_it); 549 } 550 time_last_intra_request_ms_[new_ssrc] = last_intra_request_ms; 551 } 552 553 void ViEEncoder::SetSsrcs(const std::vector<uint32_t>& ssrcs) { 554 CriticalSectionScoped cs(data_cs_.get()); 555 ssrc_streams_.clear(); 556 time_last_intra_request_ms_.clear(); 557 int idx = 0; 558 for (uint32_t ssrc : ssrcs) { 559 ssrc_streams_[ssrc] = idx++; 560 } 561 } 562 563 void ViEEncoder::SetMinTransmitBitrate(int min_transmit_bitrate_kbps) { 564 assert(min_transmit_bitrate_kbps >= 0); 565 CriticalSectionScoped crit(data_cs_.get()); 566 min_transmit_bitrate_kbps_ = min_transmit_bitrate_kbps; 567 } 568 569 // Called from ViEBitrateObserver. 570 void ViEEncoder::OnNetworkChanged(uint32_t bitrate_bps, 571 uint8_t fraction_lost, 572 int64_t round_trip_time_ms) { 573 LOG(LS_VERBOSE) << "OnNetworkChanged, bitrate" << bitrate_bps 574 << " packet loss " << static_cast<int>(fraction_lost) 575 << " rtt " << round_trip_time_ms; 576 RTC_DCHECK(send_payload_router_ != NULL); 577 vcm_->SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms); 578 bool video_is_suspended = vcm_->VideoSuspended(); 579 bool video_suspension_changed; 580 VideoCodec send_codec; 581 uint32_t first_ssrc; 582 { 583 CriticalSectionScoped cs(data_cs_.get()); 584 last_observed_bitrate_bps_ = bitrate_bps; 585 video_suspension_changed = video_suspended_ != video_is_suspended; 586 video_suspended_ = video_is_suspended; 587 send_codec = encoder_config_; 588 first_ssrc = ssrc_streams_.begin()->first; 589 } 590 591 SimulcastStream* stream_configs = send_codec.simulcastStream; 592 // Allocate the bandwidth between the streams. 593 std::vector<uint32_t> stream_bitrates = AllocateStreamBitrates( 594 bitrate_bps, stream_configs, send_codec.numberOfSimulcastStreams); 595 send_payload_router_->SetTargetSendBitrates(stream_bitrates); 596 597 if (!video_suspension_changed) 598 return; 599 // Video suspend-state changed, inform codec observer. 600 LOG(LS_INFO) << "Video suspend state changed " << video_is_suspended 601 << " for ssrc " << first_ssrc; 602 if (stats_proxy_) 603 stats_proxy_->OnSuspendChange(video_is_suspended); 604 } 605 606 void ViEEncoder::SuspendBelowMinBitrate() { 607 vcm_->SuspendBelowMinBitrate(); 608 bitrate_allocator_->EnforceMinBitrate(false); 609 } 610 611 void ViEEncoder::RegisterPostEncodeImageCallback( 612 EncodedImageCallback* post_encode_callback) { 613 vcm_->RegisterPostEncodeImageCallback(post_encode_callback); 614 } 615 616 QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessing* vpm) 617 : vp_(vpm) { 618 } 619 620 QMVideoSettingsCallback::~QMVideoSettingsCallback() { 621 } 622 623 int32_t QMVideoSettingsCallback::SetVideoQMSettings( 624 const uint32_t frame_rate, 625 const uint32_t width, 626 const uint32_t height) { 627 return vp_->SetTargetResolution(width, height, frame_rate); 628 } 629 630 void QMVideoSettingsCallback::SetTargetFramerate(int frame_rate) { 631 vp_->SetTargetFramerate(frame_rate); 632 } 633 634 } // namespace webrtc 635