使用VP9编码时如何立即获取首帧编码数据包
libvpx VP9编码首帧高延迟问题解决方案
问题根因
libvpx官方simple_encoder示例默认采用离线转码场景配置,VP9编码器默认开启长度为20+的前瞻帧缓存(对应g_lag_in_frames默认值),会先缓存足够多的输入帧完成帧间参考优化、全局码率分配、替代参考帧计算后才开始输出编码结果,这就是需要连续调用23次vpx_codec_encode才能拿到第一帧数据的核心原因,该默认配置完全不适用于屏幕共享这类低延迟实时编码场景。
可落地方案
针对实时屏幕共享的VP9编码需求,按以下步骤配置即可实现输入一帧立刻输出编码包,首帧延迟降到单帧级别:
- 核心配置调整:开启零延迟编码模式
初始化编码配置时,在加载完默认VP9配置后,强制将前瞻缓存帧数设为0,直接关闭多帧攒帧逻辑。配套调整实时场景适配参数,参考代码如下:vpx_codec_enc_cfg_t enc_cfg; // 加载VP9默认编码配置 if (vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &enc_cfg, 0) != VPX_CODEC_OK) { // 按业务需求实现错误处理 } // *核心开关*:零延迟模式配置,关闭前瞻攒帧逻辑 enc_cfg.g_lag_in_frames = 0; // 屏幕共享场景基础参数配置 enc_cfg.g_w = 捕获的屏幕宽度; enc_cfg.g_h = 捕获的屏幕高度; enc_cfg.g_timebase.num = 1; enc_cfg.g_timebase.den = 目标编码帧率; // 如15/30帧对应填15/30 enc_cfg.rc_end_usage = VPX_CBR; // 恒定码率模式适配实时推流 enc_cfg.rc_target_bitrate = 目标码率; // 单位kbps,1080p屏幕共享建议设2000-4000 enc_cfg.g_error_resilient = 1; // 开启错误弹性,弱网下降低花屏概率 enc_cfg.g_threads = 编码线程数; // 建议设为CPU物理核心数的1/2,不增加延迟同时提升编码速度 enc_cfg.kf_mode = VPX_KF_DISABLED; // 关闭编码器自动关键帧逻辑,由业务按需强制插入关键帧 - 编码调用逻辑适配
必须使用实时编码模式,每输入一帧后立刻遍历取出所有编码输出包,不要攒多次输入再取包,参考代码:// 送入一帧原始YUV数据,首帧强制标记为关键帧 int encode_flags = is_first_frame ? VPX_EFLAG_FORCE_KF : 0; vpx_codec_err_t ret = vpx_codec_encode( &codec_handle, &raw_yuv_img, current_pts, 1, // 单帧时长,按配置的时间基计算即可 encode_flags, VPX_DL_REALTIME // 必须传实时模式标记,禁止使用GOOD/BEST_QUALITY这类离线模式标记 ); if (ret != VPX_CODEC_OK) { // 按业务需求实现错误处理 } // 立刻迭代取出所有编码完成的数据包 vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt = NULL; while ((pkt = vpx_codec_get_cx_data(&codec_handle, &iter)) != NULL) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { // pkt->data.frame 为编码后的帧数据,可直接用于推流/存储 // 该配置下第一次调用encode后就会立刻返回首帧数据,无需等待后续输入 } } - 场景避坑注意事项
- 禁止使用2-pass编码模式,2-pass需要采集全片帧信息做码率优化,天生不适合实时场景,固定使用1-pass实时编码即可
- 零延迟场景下
g_lag_in_frames必须设为0,哪怕设为1都会引入1帧的额外延迟 - 屏幕共享静态画面较多时,可通过
enc_cfg.rc_min_quantizer = 20; enc_cfg.rc_max_quantizer = 40;控制量化参数范围,在不增加延迟的前提下平衡码率和画质
效果验证
按上述配置修改后,第一次调用vpx_codec_encode完成后即可拿到首帧可解码的关键帧数据,后续每输入一帧都会立刻输出对应编码包,完全满足屏幕共享的低延迟要求。
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

