webrtc 视频编码之 h264 自动调节分辨率一

本文详细介绍了Webrtc中的H264编码实现及其动态分辨率调整机制。主要内容包括:如何通过SetRates接口调整编码器的码率和帧率;编码器参数的设置方法;以及QualityScaler模块如何根据编码过程中的反馈调整分辨率。

webrtc 内部支持 vp8,vp9,h264 视频编码,由于业务需要和出于通用性考虑,我选择了 h264 编码,webrtc集成了openh264,ffmpeg用于h264的编解码。当然在移动平台也集成了硬件编解码,但是测试发现在ios上硬件编码还算可以,android上表现不稳定,差异很大,主要问题出在码率控制,视频质量控制上。动态调整码率可是保证视频流畅的重要技术,但android的mediacodec在编码过程中调整码率,会出现花屏,视频质量下降严重,并且编码的延时也比较大。在windows,android,mac上采用软编码,在ios上采用硬编码。

今天主要看看openh264 是如何动态调整分辨率的

webrtc 的调整流程

openh264 的编码调用位置

src\webrtc\modules\video_coding\codecs\h264\h264_encoder_impl.cc

首先看看几个重要接口、参数

码率调整接口


int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) {
  if (bitrate <= 0 || framerate <= 0) {
    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
  }
  codec_settings_.targetBitrate = bitrate;
  codec_settings_.maxFramerate = framerate;
  quality_scaler_.ReportFramerate(framerate);

  SBitrateInfo target_bitrate;
  memset(&target_bitrate, 0, sizeof(SBitrateInfo));
  target_bitrate.iLayer = SPATIAL_LAYER_ALL,
  target_bitrate.iBitrate = codec_settings_.targetBitrate * 1000;
  openh264_encoder_->SetOption(ENCODER_OPTION_BITRATE,
                               &target_bitrate);
  float max_framerate = static_cast<float>(codec_settings_.maxFramerate);
  openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE,
                               &max_framerate);
  return WEBRTC_VIDEO_CODEC_OK;
}

这个函数继承自 H264Encoder, 码率调整后调用此接口通知编码器去调整 输出码率。openh264encoder->SetOption调用

openh264 的几个重要参数

SEncParamExt H264EncoderImpl::CreateEncoderParams() const {
  ...
  //宽
  encoder_params.iPicWidth = codec_settings_.width;
  //高
  encoder_params.iPicHeight = codec_settings_.height;
  // |encoder_params| uses bit/s, |codec_settings_| uses kbit/s.
  //目标码率
  encoder_params.iTargetBitrate = codec_settings_.targetBitrate * 1000;
  //最大码率
  encoder_params.iMaxBitrate = codec_settings_.maxBitrate * 1000;
  // Rate Control mode
  encoder_params.iRCMode = RC_BITRATE_MODE;
  //最大帧率
  encoder_params.fMaxFrameRate =
      static_cast<float>(codec_settings_.maxFramerate);

  // The following parameters are extension parameters (they're in SEncParamExt,
  // not in SEncParamBase).
  //当码率不足时,丢弃当前帧
  encoder_params.bEnableFrameSkip =
      codec_settings_.codecSpecific.H264.frameDroppingOn;
  // |uiIntraPeriod|    - multiple of GOP size
  // |keyFrameInterval| - number of frames
  //关键帧间隔
  encoder_params.uiIntraPeriod =
      codec_settings_.codecSpecific.H264.keyFrameInterval;
 ...
  return encoder_params;
}

重点参数是 bEnableFrameSkip, 当画面剧烈运动时,编码需要的带宽也会增大,但是最大码率限制了输出带宽,当增大qp仍无法控制码率在最大码率范围内时,编码器无法正常编码,此时允许丢弃掉编码帧,稍后会说丢帧会引起调整分辨率。

有了码率调整和编码器参数调整,这两者时怎么关联起来的呢?

通过 QualityScaler quality_scaler_;

看看调用

在初始化里调用了init 设置了码率,宽,高,帧率

  quality_scaler_.Init(codec_settings_.codecType, codec_settings_.startBitrate,
                       codec_settings_.width, codec_settings_.height,
                       codec_settings_.maxFramerate);
                       

在SetRates 里上报目标帧率


quality_scaler_.ReportFramerate(framerate);

编码完成后会上报qp,或者丢弃后上报droped


 if (encoded_image_._length > 0) {
    // Deliver encoded image.
    CodecSpecificInfo codec_specific;
    codec_specific.codecType = kVideoCodecH264;
    encoded_image_callback_->Encoded(encoded_image_, &codec_specific,
                                     &frag_header);

    // Parse and report QP.
    h264_bitstream_parser_.ParseBitstream(encoded_image_._buffer,
                                          encoded_image_._length);
    int qp = -1;
    if (h264_bitstream_parser_.GetLastSliceQp(&qp))
      quality_scaler_.ReportQP(qp);
  } else {
    quality_scaler_.ReportDroppedFrame();
  }

在编码的时候获取调整后的分辨率

  quality_scaler_.OnEncodeFrame(input_frame.width(), input_frame.height());
  rtc::scoped_refptr<const VideoFrameBuffer> frame_buffer =
      quality_scaler_.GetScaledBuffer(input_frame.video_frame_buffer());
  if (frame_buffer->width() != codec_settings_.width ||
      frame_buffer->height() != codec_settings_.height) {
    LOG(LS_INFO) << "Encoder reinitialized from " << codec_settings_.width
                 << "x" << codec_settings_.height << " to "
                 << frame_buffer->width() << "x" << frame_buffer->height();
    codec_settings_.width = frame_buffer->width();
    codec_settings_.height = frame_buffer->height();
    SEncParamExt encoder_params = CreateEncoderParams();
    openh264_encoder_->SetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT,
                                 &encoder_params);
  }

quality_scaler_.OnEncodeFrame 上报当前采集帧的大小 quality_scaler_.GetScaledBuffer 根据前面上报的qp droped等计算出当前应该使用的分辨率,并做了缩放处理,返回的frame是经过缩放后的帧。 if 判断分辨率做出了调整,就出重新 CreateEncoderParams(),重置编码器参数,完成调整分辨率

openh264 有个好处就是支持编码过程中调整分辨率

后面会继续分析 quality_scaler_ 是如何调整分辨率的

### 解决多个 WebRTC 窗口并发运行时的卡顿问题 当处理多个 WebRTC 流量并行传输时,可能会遇到显著的延迟和性能下降。为了优化多窗口 WebRTC 应用程序的表现,可以采取系列措施来提高效率。 #### 资源分配与管理 合理配置 CPU 和内存资源对于维持流畅体验至关重要。通过动态调整每个会话所占用的计算能力,能够有效减少瓶颈现象的发生[^1]。例如,在服务器端实施负载均衡策略;客户端则依据设备状态自动调节视频分辨率或帧率。 #### 数据压缩技术的应用 采用高效的编码解码算法有助于降低带宽消耗以及减轻网络拥塞带来的影响。H.264/VP8 编解码器因其良好的兼容性和高压缩比而被广泛应用于实时通信领域。此外,启用 FEC (Forward Error Correction) 功能可以在定程度上弥补丢包造成的画质损失。 ```javascript // 设置 H.264 编码参数 const sdp = await peerConnection.createOffer(); sdp.sdp = sdp.sdp.replace( 'a=fmtp:96', 'a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f' ); await peerConnection.setLocalDescription(sdp); ``` #### 利用硬件加速功能 现代浏览器通常支持 GPU 加速渲染图像及视频流,这不仅加快了图形处理速度还降低了功耗。确保应用程序充分利用这些特性,特别是在移动平台上更为重要[^2]。 #### 实施 QoS(Quality of Service) 政策 针对不同类型的流量设定优先级,保障关键业务不受干扰的同时也提高了整体服务质量。比如将语音通话置于较高层次从而获得更稳定的连接质量。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值