webrtc qm_select 简略注释
来源:互联网 发布:非凡软件下载 编辑:程序博客网 时间:2024/06/01 23:01
/* * 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. */#ifndef WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_#define WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_#include "webrtc/common_types.h"#include "webrtc/typedefs.h"/******************************************************//* Quality Modes: Resolution and Robustness settings *//******************************************************/namespace webrtc {struct VideoContentMetrics;struct VCMResolutionScale { VCMResolutionScale() : codec_width(640), codec_height(480), frame_rate(30.0f), spatial_width_fact(1.0f), spatial_height_fact(1.0f), temporal_fact(1.0f), change_resolution_spatial(false), change_resolution_temporal(false) {} uint16_t codec_width; uint16_t codec_height; float frame_rate; float spatial_width_fact; float spatial_height_fact; float temporal_fact; bool change_resolution_spatial; bool change_resolution_temporal;};enum ImageType { kQCIF = 0, // 176x144 kHCIF, // 264x216 = half(~3/4x3/4) CIF. kQVGA, // 320x240 = quarter VGA. kCIF, // 352x288 kHVGA, // 480x360 = half(~3/4x3/4) VGA. kVGA, // 640x480 kQFULLHD, // 960x540 = quarter FULLHD, and half(~3/4x3/4) WHD. kWHD, // 1280x720 kFULLHD, // 1920x1080 kNumImageTypes};const uint32_t kSizeOfImageType[kNumImageTypes] = { 25344, 57024, 76800, 101376, 172800, 307200, 518400, 921600, 2073600};enum FrameRateLevelClass { kFrameRateLow, kFrameRateMiddle1, kFrameRateMiddle2, kFrameRateHigh};enum ContentLevelClass { kLow, kHigh, kDefault };struct VCMContFeature { VCMContFeature() : value(0.0f), level(kDefault) {} void Reset() { value = 0.0f; level = kDefault; } float value; ContentLevelClass level;};enum UpDownAction { kUpResolution, kDownResolution };enum SpatialAction { kNoChangeSpatial, kOneHalfSpatialUniform, // 3/4 x 3/4: 9/6 ~1/2 pixel reduction. kOneQuarterSpatialUniform, // 1/2 x 1/2: 1/4 pixel reduction. kNumModesSpatial};enum TemporalAction { kNoChangeTemporal, kTwoThirdsTemporal, // 2/3 frame rate reduction kOneHalfTemporal, // 1/2 frame rate reduction kNumModesTemporal};struct ResolutionAction { ResolutionAction() : spatial(kNoChangeSpatial), temporal(kNoChangeTemporal) {} SpatialAction spatial; TemporalAction temporal;};// Down-sampling factors for spatial (width and height), and temporal.const float kFactorWidthSpatial[kNumModesSpatial] = {1.0f, 4.0f / 3.0f, 2.0f};const float kFactorHeightSpatial[kNumModesSpatial] = {1.0f, 4.0f / 3.0f, 2.0f};const float kFactorTemporal[kNumModesTemporal] = {1.0f, 1.5f, 2.0f};enum EncoderState { kStableEncoding, // Low rate mis-match, stable buffer levels. kStressedEncoding, // Significant over-shooting of target rate, // Buffer under-flow, etc. kEasyEncoding // Significant under-shooting of target rate.};// QmMethod class: main class for resolution and robustness settingsclass VCMQmMethod { public: VCMQmMethod(); virtual ~VCMQmMethod(); // Reset values void ResetQM(); virtual void Reset() = 0; // Compute content class. //获取内容分析等级(1~27) uint8_t ComputeContentClass(); // Update with the content metrics. //设置content_metrics void UpdateContent(const VideoContentMetrics* content_metrics); // Compute spatial texture magnitude and level. // Spatial texture is a spatial prediction error measure. //计算分辨率 void ComputeSpatial(); // Compute motion magnitude and level for NFD metric. // NFD is normalized frame difference (normalized by spatial variance). void ComputeMotionNFD(); // Get the imageType (CIF, VGA, HD, etc) for the system width/height. //根据图片字节获取图片类型(高清->低清) ImageType GetImageType(uint16_t width, uint16_t height); // Return the closest image type. //根据图片字节获取图片类型(高清->低清) ImageType FindClosestImageType(uint16_t width, uint16_t height); // Get the frame rate level. //获取帧速率等级(低中高) FrameRateLevelClass FrameRateLevel(float frame_rate); protected: // Content Data. const VideoContentMetrics* content_metrics_; // Encoder frame sizes and native frame sizes. uint16_t width_; uint16_t height_; float user_frame_rate_; uint16_t native_width_; uint16_t native_height_; float native_frame_rate_; float aspect_ratio_; // Image type and frame rate leve, for the current encoder resolution. ImageType image_type_; FrameRateLevelClass framerate_level_; // Content class data. VCMContFeature motion_; VCMContFeature spatial_; uint8_t content_class_; bool init_;};// Resolution settings classclass VCMQmResolution : public VCMQmMethod { public: VCMQmResolution(); virtual ~VCMQmResolution(); // Reset all quantities. //重置所有 virtual void Reset(); // Reset rate quantities and counters after every SelectResolution() call. //重置帧率 void ResetRates(); // Reset down-sampling state. //重置采样装菜 void ResetDownSamplingState(); // Get the encoder state. //获取编码状态(稳定/强/简单) EncoderState GetEncoderState(); // Initialize after SetEncodingData in media_opt. //初始化编码器参数 int Initialize(float bitrate, float user_framerate, uint16_t width, uint16_t height, int num_layers); // Update the encoder frame size. //更新编解码参数 void UpdateCodecParameters(float frame_rate, uint16_t width, uint16_t height); // Update with actual bit rate (size of the latest encoded frame) // and frame type, after every encoded frame. //更新编码帧一段时间内的总大小 void UpdateEncodedSize(size_t encoded_size); // Update with new target bitrate, actual encoder sent rate, frame_rate, // loss rate: every ~1 sec from SetTargetRates in media_opt. //更新帧率 void UpdateRates(float target_bitrate, float encoder_sent_rate, float incoming_framerate, uint8_t packet_loss); // Extract ST (spatio-temporal) resolution action. // Inputs: qm: Reference to the quality modes pointer. // Output: the spatial and/or temporal scale change. //选择分辨率 int SelectResolution(VCMResolutionScale** qm); private: // Set the default resolution action. //设置当前状态至默认数据 void SetDefaultAction(); // Compute rates for the selection of down-sampling action. //降低分辨率或者帧率 void ComputeRatesForSelection(); // Compute the encoder state. //更改编码器状态 void ComputeEncoderState(); // Return true if the action is to go back up in resolution. //提升分辨率或者帧率 bool GoingUpResolution(); // Return true if the action is to go down in resolution. //降低分辨率或者帧率 bool GoingDownResolution(); // Check the condition for going up in resolution by the scale factors: // |facWidth|, |facHeight|, |facTemp|. // |scaleFac| is a scale factor for the transition rate. //检查是否需要做提升操作 bool ConditionForGoingUp(float fac_width, float fac_height, float fac_temp, float scale_fac); // Get the bitrate threshold for the resolution action. // The case |facWidth|=|facHeight|=|facTemp|==1 is for down-sampling action. // |scaleFac| is a scale factor for the transition rate. //获取流量阈值 float GetTransitionRate(float fac_width, float fac_height, float fac_temp, float scale_fac); // Update the down-sampling state. //更改VCMResolutionScale系数 void UpdateDownsamplingState(UpDownAction up_down); // Update the codec frame size and frame rate. //更新分辨率数据 void UpdateCodecResolution(); // Return a state based on average target rate relative transition rate. //获取rateClass级数 uint8_t RateClass(float transition_rate); // Adjust the action selected from the table. //调整动作参数 void AdjustAction(); // Covert 2 stages of 3/4 (=9/16) spatial decimation to 1/2. //切换当前状态(换挡) void ConvertSpatialFractionalToWhole(); // Returns true if the new frame sizes, under the selected spatial action, // are of even size. //检查分辨率是否是偶数系 bool EvenFrameSize(); // Insert latest down-sampling action into the history list. //将当前Action插入列表中 void InsertLatestDownAction(); // Remove the last (first element) down-sampling action from the list. //移除最后一次插入的Action void RemoveLastDownAction(); // Check constraints on the amount of down-sampling allowed. //约束当前状态更改,避免超标 void ConstrainAmountOfDownSampling(); // For going up in resolution: pick spatial or temporal action, // if both actions were separately selected. //选择分辨率或者帧率操作 void PickSpatialOrTemporal(); // Select the directional (1x2 or 2x1) spatial down-sampling action. void SelectSpatialDirectionMode(float transition_rate); enum { kDownActionHistorySize = 10 }; VCMResolutionScale* qm_; // Encoder rate control parameters. float target_bitrate_;//对端流量 float incoming_framerate_;//当前使用的帧率 float per_frame_bandwidth_;//对端前一次期望帧的流量 float buffer_level_;//缓冲区等级 // Data accumulated every ~1sec from MediaOpt. float sum_target_rate_;//总的对端流量 float sum_incoming_framerate_;//总的帧率 float sum_rate_MM_;//流量差异比例总量 float sum_rate_MM_sgn_;//双端流量差异状态(<0 对端流量过低 >0 本端流量过低) (用以决定编码强度) float sum_packet_loss_;//总的丢包数 // Counters. uint32_t frame_cnt_;//编码帧数 uint32_t update_rate_cnt_;//更新帧率次数 uint32_t low_buffer_cnt_;//低流量次数 // Resolution state parameters. float state_dec_factor_spatial_;//空间削减系数(越小 削减的越多) float state_dec_factor_temporal_;//帧率削减系数(越小 削减的越多) // Quantities used for selection. float avg_target_rate_;//平均的对端流量 float avg_incoming_framerate_;//平均帧率 float avg_ratio_buffer_low_;//平均低流量次数 float avg_rate_mismatch_;//失配率 float avg_rate_mismatch_sgn_;//失配率标志(本端还是对端)(<0 对端流量过低 >0 本端流量过低) (用以决定编码强度) float avg_packet_loss_;//丢包平均数 EncoderState encoder_state_;//编码器状态(强中弱) ResolutionAction action_;//分辨率操作 // Short history of the down-sampling actions from the Initialize() state. // This is needed for going up in resolution. Since the total amount of // down-sampling actions are constrained, the length of the list need not be // large: i.e., (4/3) ^{kDownActionHistorySize} <= kMaxDownSample. ResolutionAction down_action_history_[kDownActionHistorySize];//将分辨率帧率记录,用于后续回归提升 int num_layers_;};} // namespace webrtc#endif // WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
/* * 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/modules/video_coding/qm_select.h"#include <math.h>#include "webrtc/modules/include/module_common_types.h"#include "webrtc/modules/video_coding/include/video_coding_defines.h"#include "webrtc/modules/video_coding/internal_defines.h"#include "webrtc/modules/video_coding/qm_select_data.h"#include "webrtc/system_wrappers/include/trace.h"namespace webrtc {// QM-METHOD classVCMQmMethod::VCMQmMethod() : content_metrics_(NULL), width_(0), height_(0), user_frame_rate_(0.0f), native_width_(0), native_height_(0), native_frame_rate_(0.0f), image_type_(kVGA), framerate_level_(kFrameRateHigh), init_(false) { ResetQM();}VCMQmMethod::~VCMQmMethod() {}void VCMQmMethod::ResetQM() { aspect_ratio_ = 1.0f; motion_.Reset(); spatial_.Reset(); content_class_ = 0;}uint8_t VCMQmMethod::ComputeContentClass() { ComputeMotionNFD(); ComputeSpatial(); return content_class_ = 3 * motion_.level + spatial_.level;}void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) { content_metrics_ = contentMetrics;}void VCMQmMethod::ComputeMotionNFD() { if (content_metrics_) { motion_.value = content_metrics_->motion_magnitude; } // Determine motion level. if (motion_.value < kLowMotionNfd) { motion_.level = kLow; } else if (motion_.value > kHighMotionNfd) { motion_.level = kHigh; } else { motion_.level = kDefault; }}void VCMQmMethod::ComputeSpatial() { float spatial_err = 0.0; float spatial_err_h = 0.0; float spatial_err_v = 0.0; if (content_metrics_) { spatial_err = content_metrics_->spatial_pred_err; spatial_err_h = content_metrics_->spatial_pred_err_h; spatial_err_v = content_metrics_->spatial_pred_err_v; } // Spatial measure: take average of 3 prediction errors. spatial_.value = (spatial_err + spatial_err_h + spatial_err_v) / 3.0f; // Reduce thresholds for large scenes/higher pixel correlation. float scale2 = image_type_ > kVGA ? kScaleTexture : 1.0; if (spatial_.value > scale2 * kHighTexture) { spatial_.level = kHigh; } else if (spatial_.value < scale2 * kLowTexture) { spatial_.level = kLow; } else { spatial_.level = kDefault; }}ImageType VCMQmMethod::GetImageType(uint16_t width, uint16_t height) { // Get the image type for the encoder frame size. uint32_t image_size = width * height; if (image_size == kSizeOfImageType[kQCIF]) { return kQCIF; } else if (image_size == kSizeOfImageType[kHCIF]) { return kHCIF; } else if (image_size == kSizeOfImageType[kQVGA]) { return kQVGA; } else if (image_size == kSizeOfImageType[kCIF]) { return kCIF; } else if (image_size == kSizeOfImageType[kHVGA]) { return kHVGA; } else if (image_size == kSizeOfImageType[kVGA]) { return kVGA; } else if (image_size == kSizeOfImageType[kQFULLHD]) { return kQFULLHD; } else if (image_size == kSizeOfImageType[kWHD]) { return kWHD; } else if (image_size == kSizeOfImageType[kFULLHD]) { return kFULLHD; } else { // No exact match, find closet one. return FindClosestImageType(width, height); }}ImageType VCMQmMethod::FindClosestImageType(uint16_t width, uint16_t height) { float size = static_cast<float>(width * height); float min = size; int isel = 0; for (int i = 0; i < kNumImageTypes; ++i) { float dist = fabs(size - kSizeOfImageType[i]); if (dist < min) { min = dist; isel = i; } } return static_cast<ImageType>(isel);}FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { if (avg_framerate <= kLowFrameRate) { return kFrameRateLow; } else if (avg_framerate <= kMiddleFrameRate) { return kFrameRateMiddle1; } else if (avg_framerate <= kHighFrameRate) { return kFrameRateMiddle2; } else { return kFrameRateHigh; }}// RESOLUTION CLASSVCMQmResolution::VCMQmResolution() : qm_(new VCMResolutionScale()) { Reset();}VCMQmResolution::~VCMQmResolution() { delete qm_;}void VCMQmResolution::ResetRates() { sum_target_rate_ = 0.0f; sum_incoming_framerate_ = 0.0f; sum_rate_MM_ = 0.0f; sum_rate_MM_sgn_ = 0.0f; sum_packet_loss_ = 0.0f; buffer_level_ = kInitBufferLevel * target_bitrate_; frame_cnt_ = 0; frame_cnt_delta_ = 0; low_buffer_cnt_ = 0; update_rate_cnt_ = 0;}void VCMQmResolution::ResetDownSamplingState() { state_dec_factor_spatial_ = 1.0; state_dec_factor_temporal_ = 1.0; for (int i = 0; i < kDownActionHistorySize; i++) { down_action_history_[i].spatial = kNoChangeSpatial; down_action_history_[i].temporal = kNoChangeTemporal; }}void VCMQmResolution::Reset() { target_bitrate_ = 0.0f; incoming_framerate_ = 0.0f; buffer_level_ = 0.0f; per_frame_bandwidth_ = 0.0f; avg_target_rate_ = 0.0f; avg_incoming_framerate_ = 0.0f; avg_ratio_buffer_low_ = 0.0f; avg_rate_mismatch_ = 0.0f; avg_rate_mismatch_sgn_ = 0.0f; avg_packet_loss_ = 0.0f; encoder_state_ = kStableEncoding; num_layers_ = 1; ResetRates(); ResetDownSamplingState(); ResetQM();}EncoderState VCMQmResolution::GetEncoderState() { return encoder_state_;}// Initialize state after re-initializing the encoder,// i.e., after SetEncodingData() in mediaOpt.int VCMQmResolution::Initialize(float bitrate, float user_framerate, uint16_t width, uint16_t height, int num_layers) { if (user_framerate == 0.0f || width == 0 || height == 0) { return VCM_PARAMETER_ERROR; } Reset(); target_bitrate_ = bitrate; incoming_framerate_ = user_framerate; UpdateCodecParameters(user_framerate, width, height); native_width_ = width; native_height_ = height; native_frame_rate_ = user_framerate; num_layers_ = num_layers; // Initial buffer level. buffer_level_ = kInitBufferLevel * target_bitrate_; // Per-frame bandwidth. per_frame_bandwidth_ = target_bitrate_ / user_framerate; init_ = true; return VCM_OK;}void VCMQmResolution::UpdateCodecParameters(float frame_rate, uint16_t width, uint16_t height) { width_ = width; height_ = height; // |user_frame_rate| is the target frame rate for VPM frame dropper. user_frame_rate_ = frame_rate; image_type_ = GetImageType(width, height);}// Update rate data after every encoded frame.void VCMQmResolution::UpdateEncodedSize(size_t encoded_size) { frame_cnt_++; // Convert to Kbps. float encoded_size_kbits = 8.0f * static_cast<float>(encoded_size) / 1000.0f; // Update the buffer level: // Note this is not the actual encoder buffer level. // |buffer_level_| is reset to an initial value after SelectResolution is // called, and does not account for frame dropping by encoder or VCM. buffer_level_ += per_frame_bandwidth_ - encoded_size_kbits; // Counter for occurrences of low buffer level: // low/negative values means encoder is likely dropping frames. if (buffer_level_ <= kPercBufferThr * kInitBufferLevel * target_bitrate_) { low_buffer_cnt_++; }}// Update various quantities after SetTargetRates in MediaOpt.void VCMQmResolution::UpdateRates(float target_bitrate, float encoder_sent_rate, float incoming_framerate, uint8_t packet_loss) { // Sum the target bitrate: this is the encoder rate from previous update // (~1sec), i.e, before the update for next ~1sec. sum_target_rate_ += target_bitrate_; update_rate_cnt_++; // Sum the received (from RTCP reports) packet loss rates. sum_packet_loss_ += static_cast<float>(packet_loss / 255.0); // Sum the sequence rate mismatch: // Mismatch here is based on the difference between the target rate // used (in previous ~1sec) and the average actual encoding rate measured // at previous ~1sec. float diff = target_bitrate_ - encoder_sent_rate; if (target_bitrate_ > 0.0) sum_rate_MM_ += fabs(diff) / target_bitrate_; int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0); // To check for consistent under(+)/over_shooting(-) of target rate. sum_rate_MM_sgn_ += sgnDiff; // Update with the current new target and frame rate: // these values are ones the encoder will use for the current/next ~1sec. target_bitrate_ = target_bitrate; incoming_framerate_ = incoming_framerate; sum_incoming_framerate_ += incoming_framerate_; // Update the per_frame_bandwidth: // this is the per_frame_bw for the current/next ~1sec. per_frame_bandwidth_ = 0.0f; if (incoming_framerate_ > 0.0f) { per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_; }}// Select the resolution factors: frame size and frame rate change (qm scales).// Selection is for going down in resolution, or for going back up// (if a previous down-sampling action was taken).// In the current version the following constraints are imposed:// 1) We only allow for one action, either down or up, at a given time.// 2) The possible down-sampling actions are: spatial by 1/2x1/2, 3/4x3/4;// temporal/frame rate reduction by 1/2 and 2/3.// 3) The action for going back up is the reverse of last (spatial or temporal)// down-sampling action. The list of down-sampling actions from the// Initialize() state are kept in |down_action_history_|.// 4) The total amount of down-sampling (spatial and/or temporal) from the// Initialize() state (native resolution) is limited by various factors.int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { if (!init_) { return VCM_UNINITIALIZED; } if (content_metrics_ == NULL) { Reset(); *qm = qm_; return VCM_OK; } // Check conditions on down-sampling state. assert(state_dec_factor_spatial_ >= 1.0f); assert(state_dec_factor_temporal_ >= 1.0f); assert(state_dec_factor_spatial_ <= kMaxSpatialDown); assert(state_dec_factor_temporal_ <= kMaxTempDown); assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <= kMaxTotalDown); // Compute content class for selection. content_class_ = ComputeContentClass(); // Compute various rate quantities for selection. ComputeRatesForSelection(); // Get the encoder state. ComputeEncoderState(); // Default settings: no action. SetDefaultAction(); *qm = qm_; // Check for going back up in resolution, if we have had some down-sampling // relative to native state in Initialize(). if (down_action_history_[0].spatial != kNoChangeSpatial || down_action_history_[0].temporal != kNoChangeTemporal) { if (GoingUpResolution()) { *qm = qm_; return VCM_OK; } } // Check for going down in resolution. if (GoingDownResolution()) { *qm = qm_; return VCM_OK; } return VCM_OK;}void VCMQmResolution::SetDefaultAction() { qm_->codec_width = width_; qm_->codec_height = height_; qm_->frame_rate = user_frame_rate_; qm_->change_resolution_spatial = false; qm_->change_resolution_temporal = false; qm_->spatial_width_fact = 1.0f; qm_->spatial_height_fact = 1.0f; qm_->temporal_fact = 1.0f; action_.spatial = kNoChangeSpatial; action_.temporal = kNoChangeTemporal;}void VCMQmResolution::ComputeRatesForSelection() { avg_target_rate_ = 0.0f; avg_incoming_framerate_ = 0.0f; avg_ratio_buffer_low_ = 0.0f; avg_rate_mismatch_ = 0.0f; avg_rate_mismatch_sgn_ = 0.0f; avg_packet_loss_ = 0.0f; if (frame_cnt_ > 0) { avg_ratio_buffer_low_ = static_cast<float>(low_buffer_cnt_) / static_cast<float>(frame_cnt_); } if (update_rate_cnt_ > 0) { avg_rate_mismatch_ = static_cast<float>(sum_rate_MM_) / static_cast<float>(update_rate_cnt_); avg_rate_mismatch_sgn_ = static_cast<float>(sum_rate_MM_sgn_) / static_cast<float>(update_rate_cnt_); avg_target_rate_ = static_cast<float>(sum_target_rate_) / static_cast<float>(update_rate_cnt_); avg_incoming_framerate_ = static_cast<float>(sum_incoming_framerate_) / static_cast<float>(update_rate_cnt_); avg_packet_loss_ = static_cast<float>(sum_packet_loss_) / static_cast<float>(update_rate_cnt_); } // For selection we may want to weight some quantities more heavily // with the current (i.e., next ~1sec) rate values. avg_target_rate_ = kWeightRate * avg_target_rate_ + (1.0 - kWeightRate) * target_bitrate_; avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ + (1.0 - kWeightRate) * incoming_framerate_; // Use base layer frame rate for temporal layers: this will favor spatial. assert(num_layers_ > 0); framerate_level_ = FrameRateLevel(avg_incoming_framerate_ / static_cast<float>(1 << (num_layers_ - 1)));}void VCMQmResolution::ComputeEncoderState() { // Default. encoder_state_ = kStableEncoding; // Assign stressed state if: // 1) occurrences of low buffer levels is high, or // 2) rate mis-match is high, and consistent over-shooting by encoder. //30%处于低功率模式则编码器处于稳定状态 //编码码率高于对端接收码率50%则处于稳定状态 if ((avg_ratio_buffer_low_ > kMaxBufferLow) || ((avg_rate_mismatch_ > kMaxRateMisMatch) && (avg_rate_mismatch_sgn_ < -kRateOverShoot))) { encoder_state_ = kStressedEncoding; } // Assign easy state if: // 1) rate mis-match is high, and // 2) consistent under-shooting by encoder. //编码码率低于对端接收码率50%则处理低功率状态 if ((avg_rate_mismatch_ > kMaxRateMisMatch) && (avg_rate_mismatch_sgn_ > kRateUnderShoot)) { encoder_state_ = kEasyEncoding; }}bool VCMQmResolution::GoingUpResolution() { // For going up, we check for undoing the previous down-sampling action. float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial]; float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial]; float fac_temp = kFactorTemporal[down_action_history_[0].temporal]; // For going up spatially, we allow for going up by 3/4x3/4 at each stage. // So if the last spatial action was 1/2x1/2 it would be undone in 2 stages. // Modify the fac_width/height for this case. if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) { fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] / kFactorWidthSpatial[kOneHalfSpatialUniform]; fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] / kFactorHeightSpatial[kOneHalfSpatialUniform]; } // Check if we should go up both spatially and temporally. if (down_action_history_[0].spatial != kNoChangeSpatial && down_action_history_[0].temporal != kNoChangeTemporal) { if (ConditionForGoingUp(fac_width, fac_height, fac_temp, kTransRateScaleUpSpatialTemp)) { action_.spatial = down_action_history_[0].spatial; action_.temporal = down_action_history_[0].temporal; UpdateDownsamplingState(kUpResolution); return true; } } // Check if we should go up either spatially or temporally. bool selected_up_spatial = false; bool selected_up_temporal = false; if (down_action_history_[0].spatial != kNoChangeSpatial) { selected_up_spatial = ConditionForGoingUp(fac_width, fac_height, 1.0f, kTransRateScaleUpSpatial); } if (down_action_history_[0].temporal != kNoChangeTemporal) { selected_up_temporal = ConditionForGoingUp(1.0f, 1.0f, fac_temp, kTransRateScaleUpTemp); } if (selected_up_spatial && !selected_up_temporal) { action_.spatial = down_action_history_[0].spatial; action_.temporal = kNoChangeTemporal; UpdateDownsamplingState(kUpResolution); return true; } else if (!selected_up_spatial && selected_up_temporal) { action_.spatial = kNoChangeSpatial; action_.temporal = down_action_history_[0].temporal; UpdateDownsamplingState(kUpResolution); return true; } else if (selected_up_spatial && selected_up_temporal) { PickSpatialOrTemporal(); UpdateDownsamplingState(kUpResolution); return true; } return false;}bool VCMQmResolution::ConditionForGoingUp(float fac_width, float fac_height, float fac_temp, float scale_fac) { float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height, fac_temp, scale_fac); // Go back up if: // 1) target rate is above threshold and current encoder state is stable, or // 2) encoder state is easy (encoder is significantly under-shooting target). //对端流量超过阈值同时编码器处理强功率 //编码器为低功率模式 if (((avg_target_rate_ > estimated_transition_rate_up) && (encoder_state_ == kStableEncoding)) || (encoder_state_ == kEasyEncoding)) { return true; } else { return false; }}bool VCMQmResolution::GoingDownResolution() { float estimated_transition_rate_down = GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f); float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_]; // Resolution reduction if: // (1) target rate is below transition rate, or // (2) encoder is in stressed state and target rate below a max threshold. if ((avg_target_rate_ < estimated_transition_rate_down) || (encoder_state_ == kStressedEncoding && avg_target_rate_ < max_rate)) { // Get the down-sampling action: based on content class, and how low // average target rate is relative to transition rate.//获取预设预估权值用于开启降操作 uint8_t spatial_fact = kSpatialAction[content_class_ + 9 * RateClass(estimated_transition_rate_down)]; uint8_t temp_fact = kTemporalAction[content_class_ + 9 * RateClass(estimated_transition_rate_down)]; switch (spatial_fact) { case 4: { action_.spatial = kOneQuarterSpatialUniform; break; } case 2: { action_.spatial = kOneHalfSpatialUniform; break; } case 1: { action_.spatial = kNoChangeSpatial; break; } default: { assert(false); } } switch (temp_fact) { case 3: { action_.temporal = kTwoThirdsTemporal; break; } case 2: { action_.temporal = kOneHalfTemporal; break; } case 1: { action_.temporal = kNoChangeTemporal; break; } default: { assert(false); } } // Only allow for one action (spatial or temporal) at a given time. assert(action_.temporal == kNoChangeTemporal || action_.spatial == kNoChangeSpatial); // Adjust cases not captured in tables, mainly based on frame rate, and // also check for odd frame sizes.//调整降低动作数值 AdjustAction(); // Update down-sampling state. if (action_.spatial != kNoChangeSpatial || action_.temporal != kNoChangeTemporal) { UpdateDownsamplingState(kDownResolution); return true; } } return false;}float VCMQmResolution::GetTransitionRate(float fac_width, float fac_height, float fac_temp, float scale_fac) { //通过图片尺寸获取图片类型(0~8) ImageType image_type = GetImageType(static_cast<uint16_t>(fac_width * width_), static_cast<uint16_t>(fac_height * height_)); //获取帧率等级(0~4) (10/15/20/) FrameRateLevelClass framerate_level = FrameRateLevel(fac_temp * avg_incoming_framerate_); // If we are checking for going up temporally, and this is the last // temporal action, then use native frame rate. //如果这是最后一次更改同时需要提升帧率 if (down_action_history_[1].temporal == kNoChangeTemporal && fac_temp > 1.0f) { framerate_level = FrameRateLevel(native_frame_rate_); } // The maximum allowed rate below which down-sampling is allowed: // Nominal values based on image format (frame size and frame rate). //按照帧率等级一定比率取阈值流量 float max_rate = kFrameRateFac[framerate_level] * kMaxRateQm[image_type]; //按照kVGA分成两块参数进行匹配 uint8_t image_class = image_type > kVGA ? 1 : 0; //具体的通过content_class_确定缩放比率 uint8_t table_index = image_class * 9 + content_class_; // Scale factor for down-sampling transition threshold: // factor based on the content class and the image size. float scaleTransRate = kScaleTransRateQm[table_index]; // Threshold bitrate for resolution action. return static_cast<float>(scale_fac * scaleTransRate * max_rate);}void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {//如果为提升状态 if (up_down == kUpResolution) { //更改分辨率系数 qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; // If last spatial action was 1/2x1/2, we undo it in two steps, so the // spatial scale factor in this first step is modified as (4.0/3.0 / 2.0).//如果当前动作为2档,重新修整系数 if (action_.spatial == kOneQuarterSpatialUniform) { qm_->spatial_width_fact = 1.0f * kFactorWidthSpatial[kOneHalfSpatialUniform] / kFactorWidthSpatial[kOneQuarterSpatialUniform]; qm_->spatial_height_fact = 1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] / kFactorHeightSpatial[kOneQuarterSpatialUniform]; } qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal];//移除最后一次添加的动作 RemoveLastDownAction(); } else if (up_down == kDownResolution) { //检查约束条件 ConstrainAmountOfDownSampling();//换挡 ConvertSpatialFractionalToWhole();//更改系数 qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial]; qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial]; qm_->temporal_fact = kFactorTemporal[action_.temporal];//添加最后一次动作 InsertLatestDownAction(); } else { // This function should only be called if either the Up or Down action // has been selected. assert(false); } //更改分辨率 UpdateCodecResolution(); //更改削减因子 state_dec_factor_spatial_ = state_dec_factor_spatial_ * qm_->spatial_width_fact * qm_->spatial_height_fact; state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact;}void VCMQmResolution::UpdateCodecResolution() { if (action_.spatial != kNoChangeSpatial) { qm_->change_resolution_spatial = true; qm_->codec_width = static_cast<uint16_t>(width_ / qm_->spatial_width_fact + 0.5f); qm_->codec_height = static_cast<uint16_t>(height_ / qm_->spatial_height_fact + 0.5f); // Size should not exceed native sizes. assert(qm_->codec_width <= native_width_); assert(qm_->codec_height <= native_height_); // New sizes should be multiple of 2, otherwise spatial should not have // been selected. assert(qm_->codec_width % 2 == 0); assert(qm_->codec_height % 2 == 0); } if (action_.temporal != kNoChangeTemporal) { qm_->change_resolution_temporal = true; // Update the frame rate based on the average incoming frame rate. qm_->frame_rate = avg_incoming_framerate_ / qm_->temporal_fact + 0.5f; if (down_action_history_[0].temporal == 0) { // When we undo the last temporal-down action, make sure we go back up // to the native frame rate. Since the incoming frame rate may // fluctuate over time, |avg_incoming_framerate_| scaled back up may // be smaller than |native_frame rate_|. qm_->frame_rate = native_frame_rate_; } }}uint8_t VCMQmResolution::RateClass(float transition_rate) {//对端流量低于预估流量的一半则为0,对端流量大于预估流量则为2,其他为1 return avg_target_rate_ < (kFacLowRate * transition_rate) ? 0 : (avg_target_rate_ >= transition_rate ? 2 : 1);}// TODO(marpan): Would be better to capture these frame rate adjustments by// extending the table data (qm_select_data.h).void VCMQmResolution::AdjustAction() { // If the spatial level is default state (neither low or high), motion level // is not high, and spatial action was selected, switch to 2/3 frame rate // reduction if the average incoming frame rate is high. if (spatial_.level == kDefault && motion_.level != kHigh && action_.spatial != kNoChangeSpatial && framerate_level_ == kFrameRateHigh) { action_.spatial = kNoChangeSpatial; action_.temporal = kTwoThirdsTemporal; } // If both motion and spatial level are low, and temporal down action was // selected, switch to spatial 3/4x3/4 if the frame rate is not above the // lower middle level (|kFrameRateMiddle1|). //如果图像分析后等级为低等级同时帧率低于2档,帧率需切换 if (motion_.level == kLow && spatial_.level == kLow && framerate_level_ <= kFrameRateMiddle1 && action_.temporal != kNoChangeTemporal) { action_.spatial = kOneHalfSpatialUniform; action_.temporal = kNoChangeTemporal; } // If spatial action is selected, and there has been too much spatial // reduction already (i.e., 1/4), then switch to temporal action if the // average frame rate is not low. //如果分辨率已经更改到最高级别,同时帧率还在低级别就需要提升修改帧率等级 if (action_.spatial != kNoChangeSpatial && down_action_history_[0].spatial == kOneQuarterSpatialUniform && framerate_level_ != kFrameRateLow) { action_.spatial = kNoChangeSpatial; action_.temporal = kTwoThirdsTemporal; } //当图层大于2个的时候是不能更改帧率,只能改分辨率 // Never use temporal action if number of temporal layers is above 2. if (num_layers_ > 2) { if (action_.temporal != kNoChangeTemporal) { action_.spatial = kOneHalfSpatialUniform; } action_.temporal = kNoChangeTemporal; } // If spatial action was selected, we need to make sure the frame sizes // are multiples of two. Otherwise switch to 2/3 temporal. //如果分辨率不符合更改条件,则更改帧率 if (action_.spatial != kNoChangeSpatial && !EvenFrameSize()) { action_.spatial = kNoChangeSpatial; // Only one action (spatial or temporal) is allowed at a given time, so need // to check whether temporal action is currently selected. action_.temporal = kTwoThirdsTemporal; }}void VCMQmResolution::ConvertSpatialFractionalToWhole() { // If 3/4 spatial is selected, check if there has been another 3/4, // and if so, combine them into 1/2. 1/2 scaling is more efficient than 9/16. // Note we define 3/4x3/4 spatial as kOneHalfSpatialUniform. //如果为1档 if (action_.spatial == kOneHalfSpatialUniform) {//如果历史记录中还有一档记录 bool found = false; int isel = kDownActionHistorySize; for (int i = 0; i < kDownActionHistorySize; ++i) { if (down_action_history_[i].spatial == kOneHalfSpatialUniform) { isel = i; found = true; break; } } if (found) {//切换为2档 action_.spatial = kOneQuarterSpatialUniform; state_dec_factor_spatial_ = state_dec_factor_spatial_ / (kFactorWidthSpatial[kOneHalfSpatialUniform] * kFactorHeightSpatial[kOneHalfSpatialUniform]); // Check if switching to 1/2x1/2 (=1/4) spatial is allowed. //检查约束条件是否合适 ConstrainAmountOfDownSampling(); //约束检查不通过 if (action_.spatial == kNoChangeSpatial) { //切回去 // Not allowed. Go back to 3/4x3/4 spatial. action_.spatial = kOneHalfSpatialUniform; state_dec_factor_spatial_ = state_dec_factor_spatial_ * kFactorWidthSpatial[kOneHalfSpatialUniform] * kFactorHeightSpatial[kOneHalfSpatialUniform]; } else { //在历史记录中删除找到的一档记录 // Switching is allowed. Remove 3/4x3/4 from the history, and update // the frame size. for (int i = isel; i < kDownActionHistorySize - 1; ++i) { down_action_history_[i].spatial = down_action_history_[i + 1].spatial; }//更改宽高 width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; } } }}// Returns false if the new frame sizes, under the current spatial action,// are not multiples of two.bool VCMQmResolution::EvenFrameSize() { if (action_.spatial == kOneHalfSpatialUniform) { if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) { return false; } } else if (action_.spatial == kOneQuarterSpatialUniform) { if ((width_ * 1 / 2) % 2 != 0 || (height_ * 1 / 2) % 2 != 0) { return false; } } return true;}void VCMQmResolution::InsertLatestDownAction() { if (action_.spatial != kNoChangeSpatial) { for (int i = kDownActionHistorySize - 1; i > 0; --i) { down_action_history_[i].spatial = down_action_history_[i - 1].spatial; } down_action_history_[0].spatial = action_.spatial; } if (action_.temporal != kNoChangeTemporal) { for (int i = kDownActionHistorySize - 1; i > 0; --i) { down_action_history_[i].temporal = down_action_history_[i - 1].temporal; } down_action_history_[0].temporal = action_.temporal; }}void VCMQmResolution::RemoveLastDownAction() { if (action_.spatial != kNoChangeSpatial) { // If the last spatial action was 1/2x1/2 we replace it with 3/4x3/4. if (action_.spatial == kOneQuarterSpatialUniform) { down_action_history_[0].spatial = kOneHalfSpatialUniform; } else { for (int i = 0; i < kDownActionHistorySize - 1; ++i) { down_action_history_[i].spatial = down_action_history_[i + 1].spatial; } down_action_history_[kDownActionHistorySize - 1].spatial = kNoChangeSpatial; } } if (action_.temporal != kNoChangeTemporal) { for (int i = 0; i < kDownActionHistorySize - 1; ++i) { down_action_history_[i].temporal = down_action_history_[i + 1].temporal; } down_action_history_[kDownActionHistorySize - 1].temporal = kNoChangeTemporal; }}void VCMQmResolution::ConstrainAmountOfDownSampling() { // Sanity checks on down-sampling selection: // override the settings for too small image size and/or frame rate. // Also check the limit on current down-sampling states. float spatial_width_fact = kFactorWidthSpatial[action_.spatial]; float spatial_height_fact = kFactorHeightSpatial[action_.spatial]; float temporal_fact = kFactorTemporal[action_.temporal]; float new_dec_factor_spatial = state_dec_factor_spatial_ * spatial_width_fact * spatial_height_fact; float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact; // No spatial sampling if current frame size is too small, or if the // amount of spatial down-sampling is above maximum spatial down-action. if ((width_ * height_) <= kMinImageSize || new_dec_factor_spatial > kMaxSpatialDown) { action_.spatial = kNoChangeSpatial; new_dec_factor_spatial = state_dec_factor_spatial_; } // No frame rate reduction if average frame rate is below some point, or if // the amount of temporal down-sampling is above maximum temporal down-action. if (avg_incoming_framerate_ <= kMinFrameRate || new_dec_factor_temp > kMaxTempDown) { action_.temporal = kNoChangeTemporal; new_dec_factor_temp = state_dec_factor_temporal_; } // Check if the total (spatial-temporal) down-action is above maximum allowed, // if so, disallow the current selected down-action. if (new_dec_factor_spatial * new_dec_factor_temp > kMaxTotalDown) { if (action_.spatial != kNoChangeSpatial) { action_.spatial = kNoChangeSpatial; } else if (action_.temporal != kNoChangeTemporal) { action_.temporal = kNoChangeTemporal; } else { // We only allow for one action (spatial or temporal) at a given time, so // either spatial or temporal action is selected when this function is // called. If the selected action is disallowed from one of the above // 2 prior conditions (on spatial & temporal max down-action), then this // condition "total down-action > |kMaxTotalDown|" would not be entered. assert(false); } }}void VCMQmResolution::PickSpatialOrTemporal() { // Pick the one that has had the most down-sampling thus far. //如果宽高削减的多则开始提升宽高,反之则提升帧率 if (state_dec_factor_spatial_ > state_dec_factor_temporal_) { action_.spatial = down_action_history_[0].spatial; action_.temporal = kNoChangeTemporal; } else { action_.spatial = kNoChangeSpatial; action_.temporal = down_action_history_[0].temporal; }}// TODO(marpan): Update when we allow for directional spatial down-sampling.void VCMQmResolution::SelectSpatialDirectionMode(float transition_rate) { // Default is 4/3x4/3 // For bit rates well below transitional rate, we select 2x2. if (avg_target_rate_ < transition_rate * kRateRedSpatial2X2) { qm_->spatial_width_fact = 2.0f; qm_->spatial_height_fact = 2.0f; } // Otherwise check prediction errors and aspect ratio. float spatial_err = 0.0f; float spatial_err_h = 0.0f; float spatial_err_v = 0.0f; if (content_metrics_) { spatial_err = content_metrics_->spatial_pred_err; spatial_err_h = content_metrics_->spatial_pred_err_h; spatial_err_v = content_metrics_->spatial_pred_err_v; } // Favor 1x2 if aspect_ratio is 16:9. if (aspect_ratio_ >= 16.0f / 9.0f) { // Check if 1x2 has lowest prediction error. if (spatial_err_h < spatial_err && spatial_err_h < spatial_err_v) { qm_->spatial_width_fact = 2.0f; qm_->spatial_height_fact = 1.0f; } } // Check for 4/3x4/3 selection: favor 2x2 over 1x2 and 2x1. if (spatial_err < spatial_err_h * (1.0f + kSpatialErr2x2VsHoriz) && spatial_err < spatial_err_v * (1.0f + kSpatialErr2X2VsVert)) { qm_->spatial_width_fact = 4.0f / 3.0f; qm_->spatial_height_fact = 4.0f / 3.0f; } // Check for 2x1 selection. if (spatial_err_v < spatial_err_h * (1.0f - kSpatialErrVertVsHoriz) && spatial_err_v < spatial_err * (1.0f - kSpatialErr2X2VsVert)) { qm_->spatial_width_fact = 1.0f; qm_->spatial_height_fact = 2.0f; }}} // namespace webrtc
阅读全文
0 0
- webrtc qm_select 简略注释
- webrtc VCMQmResolution分辨率调整模块简略分析
- WebRTC
- WebRTC
- WEBRTC
- webrtc
- WebRTC
- WebRTC
- WebRTC
- WebRTC
- webrtc
- WebRTC
- WebRTC
- WebRTC
- WebRTC
- WebRTC
- webrtc
- webrtc
- 排序
- struct fb_bitfield
- Character 方法字符的操作
- 异常处理:Bean property 'XXX' is not writable or has an invalid setter method.
- 树上记忆化搜索(Crazy Bobo,HDU 5325)
- webrtc qm_select 简略注释
- express框架的理解
- mysql 将竖列的表格数据拼接成横向的数据,查询结果
- 关于欧拉函数的一个性质
- Windows下Nginx的启动、停止等命令
- Kotlin学习之-4.3.3 控制流
- 【重要更新】强大的文档管理工具包Aspose.Total 2017年中更新大合集
- linux常见命令
- Oracle的Webservice版本和客户端版本不同的解决方法