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

FFmpeg HEVC编码YUV数据图像异常,求问题排查方案

HEVC编码YUV图像显示异常的原因分析

我使用FFmpeg对YUV数据进行HEVC编码后,图像显示异常(画面错乱、色彩失真),但通过以下命令行可正常编码出符合预期的图像:

ffmpeg -f rawvideo -s 480x256 -pix_fmt yuv420p -i origin.yuv -c:v hevc -f hevc -x265-params keyint=1:crf=18 out.h265

以下是我的编码代码:

void H265ImageCodec::InitCPUEncoder() {
  avcodec_register_all();
  AVCodec* encoder = avcodec_find_encoder(AV_CODEC_ID_H265);
  CHECK(encoder) << "Can not find encoder with h265.";
  // context
  encode_context_ = avcodec_alloc_context3(encoder);
  CHECK(encode_context_) << "Could not allocate video codec context.";
  encode_context_->codec_id = AV_CODEC_ID_H265;
  encode_context_->profile = FF_PROFILE_HEVC_MAIN;
  encode_context_->codec_type = AVMEDIA_TYPE_VIDEO;
  encode_context_->width = width_; // it's 480
  encode_context_->height = height_; // it's 256
  encode_context_->bit_rate = 384 * 1024;
  encode_context_->pix_fmt = AVPixelFormat::AV_PIX_FMT_YUV420P;
  encode_context_->time_base = (AVRational){1, 25};
  encode_context_->framerate = (AVRational){25, 1};
  AVDictionary* options = NULL;
  av_dict_set(&options, "preset", "ultrafast", 0);
  av_dict_set(&options, "tune", "zero-latency", 0);
  av_opt_set(encode_context_->priv_data, "x265-params", "keyint=1:crf=18",
             0);  // crf: Quality-controlled variable bitrate
  avcodec_open2(encode_context_, encoder, &options);

  encode_frame_ = av_frame_alloc();
  encode_frame_->format = encode_context_->pix_fmt;
  encode_frame_->width = encode_context_->width;
  encode_frame_->height = encode_context_->height;
  av_frame_get_buffer(encode_frame_, 0);
  // packet init
  encode_packet_ = av_packet_alloc();
}

std::string H265ImageCodec::EncodeImage(std::string_view raw_image) {
  av_packet_unref(encode_packet_);
  av_frame_make_writable(encode_frame_);
  const int64 y_size = width_ * height_;
  int64 offset = 0;
  memcpy(encode_frame_->data[0], raw_image.data() + offset, y_size);
  offset += y_size;
  memcpy(encode_frame_->data[1], raw_image.data() + offset, y_size / 4);
  offset += y_size / 4;
  memcpy(encode_frame_->data[2], raw_image.data() + offset, y_size / 4);
  avcodec_send_frame(encode_context_, encode_frame_);
  int ret = avcodec_receive_packet(encode_context_, encode_packet_);
  CHECK_EQ(ret, 0) << "receive encode packet ret: " << ret;
  std::string h265_frame(reinterpret_cast<char*>(encode_packet_->data),
                         encode_packet_->size);
  return h265_frame;
}

可能的原因及修正方案

1. YUV数据拷贝未处理行对齐问题

FFmpeg的AVFrame中,每个颜色平面的linesize(行跨度)会因内存对齐要求,可能大于图像实际宽度。直接用memcpy按平面总大小拷贝数据,会导致后续行的数据错位,最终画面错乱。命令行中FFmpeg会自动处理YUV数据的对齐逻辑,所以无异常。

修正方式:逐行拷贝数据,使用linesize作为每行的跨度:

std::string H265ImageCodec::EncodeImage(std::string_view raw_image) {
  av_packet_unref(encode_packet_);
  av_frame_make_writable(encode_frame_);
  const int y_size = width_ * height_;
  const int uv_width = width_ / 2;
  const int uv_height = height_ / 2;

  // 拷贝Y平面
  for (int i = 0; i < height_; ++i) {
      memcpy(encode_frame_->data[0] + i * encode_frame_->linesize[0],
             raw_image.data() + i * width_, width_);
  }
  // 拷贝U平面
  int offset = y_size;
  for (int i = 0; i < uv_height; ++i) {
      memcpy(encode_frame_->data[1] + i * encode_frame_->linesize[1],
             raw_image.data() + offset + i * uv_width, uv_width);
  }
  // 拷贝V平面
  offset += y_size / 4;
  for (int i = 0; i < uv_height; ++i) {
      memcpy(encode_frame_->data[2] + i * encode_frame_->linesize[2],
             raw_image.data() + offset + i * uv_width, uv_width);
  }

  // 后续编码逻辑不变
  avcodec_send_frame(encode_context_, encode_frame_);
  int ret = avcodec_receive_packet(encode_context_, encode_packet_);
  CHECK_EQ(ret, 0) << "receive encode packet ret: " << ret;
  std::string h265_frame(reinterpret_cast<char*>(encode_packet_->data),
                         encode_packet_->size);
  return h265_frame;
}

2. 未设置AVFrame的显示时间戳(pts)

编码器依赖pts参数做帧类型判断和时序管理,即使单帧编码,缺少pts也可能导致编码器行为异常,尤其是在keyint=1要求每帧为I帧的配置下。

修正方式:在编码前为encode_frame_设置递增的pts:

std::string H265ImageCodec::EncodeImage(std::string_view raw_image) {
  // ... 前面的拷贝逻辑 ...

  // 设置pts,维护帧计数器
  static int64_t frame_idx = 0;
  encode_frame_->pts = frame_idx++;
  // 转换为编码器time_base对应的时间戳
  encode_frame_->pts = av_rescale_q(encode_frame_->pts, (AVRational){1,25}, encode_context_->time_base);

  avcodec_send_frame(encode_context_, encode_frame_);
  // ... 后续逻辑 ...
}

3. 参数设置方式不一致

代码中混合使用av_opt_set和AVDictionary传递编码器参数,可能导致参数传递异常。建议统一用AVDictionary传递所有参数。

修正方式:将x265-params加入options字典:

AVDictionary* options = NULL;
av_dict_set(&options, "preset", "ultrafast", 0);
av_dict_set(&options, "tune", "zero-latency", 0);
av_dict_set(&options, "x265-params", "keyint=1:crf=18", 0); // 移到这里
avcodec_open2(encode_context_, encoder, &options);

同时删除原有的av_opt_set调用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 21:20:26