WebRTC在视频编码过程中会进行QP检测,目的是让视频质量维持在可接受范围的前提下,调节整体视频表现,如分辨率、帧率。这里要注意的是,QP检测机制只是利用QP分析结果对分辨率、帧率进行调节,并不对编码QP做直接调整。说句题外话,通常编码器也不会对外提供QP设置接口,QP主要是由帧率、码率、复杂度等因素计算得到,这部分内容将会另写文章分享。
QP检测的主体代码在quality_scaler.cc的QualityScaler类中,后者作为observer注册到VideoStreamEncoder中,VideoStreamEncoder内完成了相关流程的控制。
1.初始化与配置
与CPU使用度检测类似,初始化过程发生在编码器重新创建的时候(流初始化,或者编码格式变化):
void VideoStreamEncoder::ReconfigureEncoder() { // ... ConfigureQualityScaler(info); } void VideoStreamEncoder::ConfigureQualityScaler( const VideoEncoder::EncoderInfo& encoder_info) { RTC_DCHECK_RUN_ON(&encoder_queue_); const auto scaling_settings = encoder_info.scaling_settings; // 允许进行QP检测的主要条件是,degradation_preference允许调整分辨率 // 换句话说,摄像头流允许,而共享屏幕不允许。 const bool quality_scaling_allowed = IsResolutionScalingEnabled(degradation_preference_) && scaling_settings.thresholds; if (quality_scaling_allowed) { if (quality_scaler_ == nullptr) { //... AdaptationObserverInterface* observer = this; quality_scaler_ = std::make_unique<QualityScaler>( &encoder_queue_, observer, experimental_thresholds ? *experimental_thresholds : *(scaling_settings.thresholds)); has_seen_first_significant_bwe_change_ = false; initial_framedrop_ = 0; } } else { quality_scaler_.reset(nullptr); initial_framedrop_ = kMaxInitialFramedrop; } // ... }
经过初始化后,QP阈值的下限、上限默认分别为:24、37。
2.检测的开启与终止
在QualityScaler构造时开启检测,在所创建的线程中默认每2秒检测一次:
QualityScaler::QualityScaler(rtc::TaskQueue* task_queue, AdaptationObserverInterface* observer, VideoEncoder::QpThresholds thresholds, int64_t sampling_period_ms) { // ... check_qp_task_ = RepeatingTaskHandle::DelayedStart( task_queue->Get(), TimeDelta::ms(GetSamplingPeriodMs()), [this]() { CheckQp(); return TimeDelta::ms(GetSamplingPeriodMs()); }); // ... }
与此相应,在QualityScaler析构时终止检测:
QualityScaler::~QualityScaler() { RTC_DCHECK_RUN_ON(&task_checker_); check_qp_task_.Stop(); }
3.样本数据采集
每帧编码结束后,都会记录QP。这里的QP指的是帧级QP,准确地说是指last_slice_qp,当为单Slice编码时,二者相同。
// 传入回调参数EncodedImage中携带的QP VideoStreamEncoder::RunPostEncode(EncodedImage encoded_image, int64_t time_sent_us, ...) { // ... if (quality_scaler_ && encoded_image.qp_ >= 0) quality_scaler_->ReportQp(encoded_image.qp_, time_sent_us); // ... } void QualityScaler::ReportQp(int qp, int64_t time_sent_us) { // ... average_qp_.AddSample(qp); if (qp_smoother_high_) qp_smoother_high_->Add(qp, time_sent_us); if (qp_smoother_low_) qp_smoother_low_->Add(qp, time_sent_us); }
4.计算过程
由上可知,QP检测到后,分别被添加到三个数据集 average_qp、qp_smoother_high、qp_smoother_low_ 中,在样本数据添加后立即完成滤波计算。其中average_qp使用滑窗滤波rtc::MovingAverage,qp_smoother_high和qp_smoother_low_ 使用指数滤波rtc::ExpFilter,均起到平滑数据的作用。
5.反馈调节
在定时检测中完成反馈:
void QualityScaler::CheckQp() { // ... // Check if we should scale up or down based on QP. const absl::optional<int> avg_qp_high = qp_smoother_high_ ? qp_smoother_high_->GetAvg() : average_qp_.GetAverageRoundedDown(); const absl::optional<int> avg_qp_low = qp_smoother_low_ ? qp_smoother_low_->GetAvg() : average_qp_.GetAverageRoundedDown(); if (avg_qp_high && avg_qp_low) { if (*avg_qp_high > thresholds_.high) { ReportQpHigh(); return; } if (*avg_qp_low <= thresholds_.low) { // QP has been low. We want to try a higher resolution. ReportQpLow(); return; } } } // 当QP过低时,通过AdaptUp对编码进行升级 void QualityScaler::ReportQpLow() { // ... observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality); // ... } // 当QP过高时,通过AdaptDown对编码进行降级 void QualityScaler::ReportQpHigh() { // ... if (observer_->AdaptDown( AdaptationObserverInterface::AdaptReason::kQuality)) { ClearSamples(); } else { adapt_failed_ = true; } // ... }
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓