You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

libavcodec/H.264编码首帧无法即时解码的问题分析与解决

使用libavcodec+H.264实现低延迟视频流的问题排查

我在一端用libavcodec库搭配H.264编解码器生成视频流,将编码后的帧传输到另一台PC解码。遇到的问题是:接收并向解码器输入第一个编码帧的数据包后,无法解码该帧;只有收到下一帧时,前一帧才能解码,但当前帧又无法解码,最终解码器端始终存在一帧延迟。

我试过不同的preset(重点测试了'ultrafast')、'zerolatency'调优参数,以及AVCodecContext的多种bit_rate值,都未解决问题。还尝试在注入第一帧数据后用空数据包刷新解码器,排查是否因内部缓冲区优化导致,但帧仍无法解码。测试mpeg4等其他编解码器时,首帧可解码前的延迟帧数更多。

想问:这种情况是编解码内部机制导致的正常、不可避免现象吗?否则如何实现真正的零延迟?


补充配置信息

  • max_b_frames设为0(值越高延迟越大)
  • pix_fmt设为AV_PIX_FMT_YUV420P

编辑:补充细节回复评论

(1) 解码器(或播放系统)是什么?

使用libavcodec编写的自定义解码器,解码后的帧通过OpenGL显示在屏幕上。

解码器初始化代码

parser_ = av_parser_init(AV_CODEC_ID_H264);
codec_ = avcodec_find_decoder(AV_CODEC_ID_H264);
context_ = avcodec_alloc_context3(codec_);
context_->width = 1024;
context_->height = 768;
context_->thread_count = 1;
if ((codec_->capabilities & AV_CODEC_CAP_TRUNCATED) == 0)
{
  context_->flags |= AV_CODEC_FLAG_TRUNCATED;
}
if (avcodec_open2(context_, codec_, nullptr) < 0)
{
  throw std::runtime_error{"avcodec_open2 failed"};
}
avcodec_flush_buffers(context_);

播放器解码逻辑(定期调用)

auto result = avcodec_receive_frame(context_, frame_);
if (!buffer_.empty())
{  // 上传新数据包用于解码
  int used;
  if (upload_package(buffer_.data(), buffer_.size(), &used))
  {
    buffer_.erase(buffer_.begin(), buffer_.begin() + used);
  }
}
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF || result < 0)
{
  return false;
}
yuv_to_rgb();
return true;

布尔返回值表示解码是否成功,每次调用都会检查存储输入数据包的缓冲区并上传至libavcodec解码器。

数据包上传方法

bool upload_package(const void* data, const std::size_t size, int* used)
{
  auto result = av_parser_parse2(parser_, context_, &packet_->data, &packet_->size, reinterpret_cast<const std::uint8_t*>(data), size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
  if (result < 0)
  {
    return false;
  }
  *used = result;
  if (packet_->size != 0)
  {
    result = avcodec_send_packet(context_, packet_);
    if (result < 0)
    {
      return false;
    }
  }
  return true;
}

(2) 若可能,将每个帧保存为.bin文件并分享测试链接。

我会尝试处理这个问题...

(3) 展示H264编码器设置的C++示例代码...

编码器初始化代码

codec_ = avcodec_find_encoder(AV_CODEC_ID_H264);
context_ = avcodec_alloc_context3(codec_);

context_->bit_rate = 1048576; // 1xMbit;
context_->width = 1024;
context_->height = 768;
context_->time_base = {1, 30}; // 30 fps
context_->pix_fmt = AV_PIX_FMT_YUV420P;
context_->thread_count = 1;

av_opt_set(context_->priv_data, "preset", "ultrafast", 0);
av_opt_set(context_->priv_data, "tune", "zerolatency", 0);
avcodec_open2(context_, codec_, nullptr);

frame_->format = AV_PIX_FMT_YUV420P;
frame_->width = 1024;
frame_->height = 768;
av_image_alloc(frame_->data, frame_->linesize, 1024, 768, AV_PIX_FMT_YUV420P, 32);

帧编码代码

rgb_to_yuv();
frame_->pts = frame_num_++;
auto result = avcodec_send_frame(context_, frame_);
while (result >= 0)
{
  result = avcodec_receive_packet(context_, packet_);
  if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
  {
    return;
  }
  else if (result < 0)
  {
    throw std::runtime_error{"avcodec_receive_packet failed"};
  }
  // 此处将数据包发送到解码器,完整数据包存储在上述buffer_中并通过avcodec_send_packet上传
  // 补充说明:完整缓冲区/数据包会一次性上传
  stream_video_data(packet_->data, packet_->size); 
}
av_packet_unref(packet_);
}

编辑2:问题定位

我已经找到问题所在:

对于每个输入数据包(编码帧),我先调用av_parser_parse2,再通过avcodec_send_packet发送数据,但未在buffer_为空时重新调用该流程。导致第一帧数据通过avcodec_send_packet上传后,av_parser_parse2从未被调用;接收第二帧时才触发该方法,第一帧得以正常解码,但第二帧的parse2同样未被调用,以此类推,最终形成了始终延迟一帧的现象。

本质是处理编码数据时av_parser_parse2与avcodec_send_packet的调用逻辑顺序错误。


内容的提问来源于stack exchange,提问作者Marek Kijo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 05:25:05