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

使用特殊分辨率时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:53:22