使用特殊分辨率时FFmpeg生成无效输出的问题排查
问题根源:帧数据的行对齐(Stride/Linesize)不匹配
你的问题出在YUV帧数据的行对齐处理上。当分辨率不是编码器默认对齐边界的整数倍时,AVFrame的linesize(每行字节数)会大于实际图像宽度,而你的代码直接按连续的width*height内存块复制数据,导致帧数据错位,最终播放显示异常。
为什么常见分辨率没问题?
像1920、1280这类分辨率,刚好是32或16的整数倍(x264等编码器默认的对齐边界),此时linesize[0]等于图像宽度,直接整块复制不会有问题;但1440x1080中,虽然宽度1440是2的倍数,但FFmpeg可能会将linesize[0]对齐到更大的边界(比如32字节,1440向上取整到1472),这时候整块复制就会导致每行数据没有填满linesize的长度,后续行的起始位置偏移,画面出现错位、花屏。
修复步骤
1. 修改帧数据的复制方式,按行处理对齐
不要直接整块memcpy,而是逐行复制数据,匹配AVFrame的linesize:
// 替换main函数里的memcpy部分 for (int i = 0; i < frameCount; i++) { // Y分量逐行复制 for (int y = 0; y < height; y++) { memcpy(avFrame->data[0] + y * avFrame->linesize[0], frames[i][0] + y * width, width); } // U分量逐行复制 int uv_width = width / 2; int uv_height = height / 2; for (int y = 0; y < uv_height; y++) { memcpy(avFrame->data[1] + y * avFrame->linesize[1], frames[i][1] + y * uv_width, uv_width); } // V分量逐行复制 for (int y = 0; y < uv_height; y++) { memcpy(avFrame->data[2] + y * avFrame->linesize[2], frames[i][2] + y * uv_width, uv_width); } encoder.encode(avFrame, 0); }
2. 优化fill_yuv_image3函数,直接在AVFrame内存上生成数据(可选更高效)
跳过自己分配内存的步骤,直接操作AVFrame的内存,自动适配linesize:
void fill_yuv_image3(AVFrame* frame, int frame_index) { int width = frame->width; int height = frame->height; int y_stride = frame->linesize[0]; int uv_stride = frame->linesize[1]; int i = frame_index; // Y分量 for (int y = 0; y < height; y++) { uint8_t* row = frame->data[0] + y * y_stride; for (int x = 0; x < width; x++) { row[x] = x + y + i * 3; } } // U分量 for (int y = 0; y < height / 2; y++) { uint8_t* row = frame->data[1] + y * uv_stride; for (int x = 0; x < width / 2; x++) { row[x] = 128 + y + i * 2; } } // V分量 for (int y = 0; y < height / 2; y++) { uint8_t* row = frame->data[2] + y * uv_stride; for (int x = 0; x < width / 2; x++) { row[x] = 64 + x + i * 5; } } }
然后在main里可以简化为:
// 去掉原来的frames分配代码 for (int i = 0; i < frameCount; i++) { fill_yuv_image3(avFrame, i); encoder.encode(avFrame, 0); }
3. 修正AVPacket的内存管理(潜在内存泄漏问题)
你的代码中用new AVPacket()和delete pkt是错误的,FFmpeg结构体需要用专用内存管理函数:
void FFmpegEncoder::encode(AVFrame* frame, int _frameLength) { AVPacket* pkt = av_packet_alloc(); // 替代new if (!pkt) return; av_init_packet(pkt); pkt->data = NULL; pkt->size = 0; frame->pts = nextFrameIndex++; int ret = avcodec_send_frame(avCodecContext, frame); if (ret < 0) { av_packet_free(&pkt); return; } while (ret >= 0) { ret = avcodec_receive_packet(avCodecContext, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { av_packet_free(&pkt); return; } av_packet_rescale_ts(pkt, avCodecContext->time_base, avStream->time_base); pkt->stream_index = avStream->index; av_interleaved_write_frame(avFormatContext, pkt); // 用av_interleaved_write_frame更适合MP4封装 av_packet_unref(pkt); // 重置packet,准备接收下一个 } av_packet_free(&pkt); // 替代delete }
为什么FFmpeg命令行可以正常工作?
因为FFmpeg命令行工具内部会自动处理帧数据的对齐问题,它会根据编码器的要求调整linesize,确保数据正确填充,所以不会出现显示异常。
内容的提问来源于stack exchange,提问作者Crigges
相关产品推荐
相关产品推荐

