C++ FFmpeg 创建 mp4 文件

2023-11-23

我正在尝试使用 FFmpeg 和 C++ 创建 mp4 视频文件,但结果我收到损坏的文件(Windows 播放器显示“无法播放... 0xc00d36c4”)。如果我创建 .h264 文件,它可以用“ffplay”播放,并通过 CL 成功转换为 mp4。

My code:

int main() {
    char *filename = "tmp.mp4";
    AVOutputFormat *fmt;
    AVFormatContext *fctx;
    AVCodecContext *cctx;
    AVStream *st;

    av_register_all();
    avcodec_register_all();

    //auto detect the output format from the name
    fmt = av_guess_format(NULL, filename, NULL);
    if (!fmt) {
        cout << "Error av_guess_format()" << endl; system("pause"); exit(1);
    }

    if (avformat_alloc_output_context2(&fctx, fmt, NULL, filename) < 0) {
        cout << "Error avformat_alloc_output_context2()" << endl; system("pause"); exit(1);
    }


    //stream creation + parameters
    st = avformat_new_stream(fctx, 0);
    if (!st) {
        cout << "Error avformat_new_stream()" << endl; system("pause"); exit(1);
    }

    st->codecpar->codec_id = fmt->video_codec;
    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    st->codecpar->width = 352;
    st->codecpar->height = 288;
    st->time_base.num = 1;
    st->time_base.den = 25;

    AVCodec *pCodec = avcodec_find_encoder(st->codecpar->codec_id);
    if (!pCodec) {
        cout << "Error avcodec_find_encoder()" << endl; system("pause"); exit(1);
    }

    cctx = avcodec_alloc_context3(pCodec);
    if (!cctx) {
        cout << "Error avcodec_alloc_context3()" << endl; system("pause"); exit(1);
    }

    avcodec_parameters_to_context(cctx, st->codecpar);
    cctx->bit_rate = 400000;
    cctx->width = 352;
    cctx->height = 288;
    cctx->time_base.num = 1;
    cctx->time_base.den = 25;
    cctx->gop_size = 12;
    cctx->pix_fmt = AV_PIX_FMT_YUV420P;
    if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
        av_opt_set(cctx->priv_data, "preset", "ultrafast", 0);
    }
    if (fctx->oformat->flags & AVFMT_GLOBALHEADER) {
        cctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
    avcodec_parameters_from_context(st->codecpar, cctx);

    av_dump_format(fctx, 0, filename, 1);

    //OPEN FILE + WRITE HEADER
    if (avcodec_open2(cctx, pCodec, NULL) < 0) {
        cout << "Error avcodec_open2()" << endl; system("pause"); exit(1);
    }
    if (!(fmt->flags & AVFMT_NOFILE)) {
        if (avio_open(&fctx->pb, filename, AVIO_FLAG_WRITE) < 0) {
            cout << "Error avio_open()" << endl; system("pause"); exit(1);
        }
    }
    if (avformat_write_header(fctx, NULL) < 0) {
        cout << "Error avformat_write_header()" << endl; system("pause"); exit(1);
    }


    //CREATE DUMMY VIDEO
    AVFrame *frame = av_frame_alloc();
    frame->format = cctx->pix_fmt;
    frame->width = cctx->width;
    frame->height = cctx->height;
    av_image_alloc(frame->data, frame->linesize, cctx->width, cctx->height, cctx->pix_fmt, 32);

    AVPacket pkt;
    double video_pts = 0;
    for (int i = 0; i < 50; i++) {
        video_pts = (double)cctx->time_base.num / cctx->time_base.den * 90 * i;

        for (int y = 0; y < cctx->height; y++) {
            for (int x = 0; x < cctx->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
                if (y < cctx->height / 2 && x < cctx->width / 2) {
                    /* Cb and Cr */
                    frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                    frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
                }
            }
        }

        av_init_packet(&pkt);
        pkt.flags |= AV_PKT_FLAG_KEY;
        pkt.pts = frame->pts = video_pts;
        pkt.data = NULL;
        pkt.size = 0;
        pkt.stream_index = st->index;

        if (avcodec_send_frame(cctx, frame) < 0) {
            cout << "Error avcodec_send_frame()" << endl; system("pause"); exit(1);
        }
        if (avcodec_receive_packet(cctx, &pkt) == 0) {
            //cout << "Write frame " << to_string((int) pkt.pts) << endl;
            av_interleaved_write_frame(fctx, &pkt);
            av_packet_unref(&pkt);
        }
    }

    //DELAYED FRAMES
    for (;;) {
        avcodec_send_frame(cctx, NULL);
        if (avcodec_receive_packet(cctx, &pkt) == 0) {
            //cout << "-Write frame " << to_string((int)pkt.pts) << endl;
            av_interleaved_write_frame(fctx, &pkt);
            av_packet_unref(&pkt);
        }
        else {
            break;
        }
    }

    //FINISH
    av_write_trailer(fctx);
    if (!(fmt->flags & AVFMT_NOFILE)) {
        if (avio_close(fctx->pb) < 0) {
            cout << "Error avio_close()" << endl; system("pause"); exit(1);
        }
    }
    av_frame_free(&frame);
    avcodec_free_context(&cctx);
    avformat_free_context(fctx);

    system("pause");
    return 0;
}

程序输出:

Output #0, mp4, to 'tmp.mp4':
    Stream #0:0: Video: h264, yuv420p, 352x288, q=2-31, 400 kb/s, 25 tbn
[libx264 @ 0000021c4a995ba0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0000021c4a995ba0] profile Constrained Baseline, level 2.0
[libx264 @ 0000021c4a995ba0] 264 - core 152 r2851 ba24899 - H.264/MPEG-4 AVC codec - Copyleft 2003-2017 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=0:0:0 analyse=0:0 me=dia subme=0 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=12 keyint_min=1 scenecut=0 intra_refresh=0 rc=abr mbtree=0 bitrate=400 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=0
[libx264 @ 0000021c4a995ba0] frame I:5     Avg QP: 7.03  size:  9318
[libx264 @ 0000021c4a995ba0] frame P:45    Avg QP: 4.53  size:  4258
[libx264 @ 0000021c4a995ba0] mb I  I16..4: 100.0%  0.0%  0.0%
[libx264 @ 0000021c4a995ba0] mb P  I16..4:  0.0%  0.0%  0.0%  P16..4: 100.0%  0.0%  0.0%  0.0%  0.0%    skip: 0.0%
[libx264 @ 0000021c4a995ba0] final ratefactor: 9.11
[libx264 @ 0000021c4a995ba0] coded y,uvDC,uvAC intra: 18.9% 21.8% 14.5% inter: 7.8% 100.0% 15.5%
[libx264 @ 0000021c4a995ba0] i16 v,h,dc,p:  4%  5%  5% 86%
[libx264 @ 0000021c4a995ba0] i8c dc,h,v,p:  2%  9%  6% 82%
[libx264 @ 0000021c4a995ba0] kb/s:264.68

如果我尝试使用“ffplay”播放 mp4 文件,则会打印:

[mov,mp4,m4a,3gp,3g2,mj2 @ 00000000026bf900] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 352x288, 138953 kb/s): unspecified pixel format
[h264 @ 00000000006c6ae0] non-existing PPS 0 referenced
[h264 @ 00000000006c6ae0] decode_slice_header error
[h264 @ 00000000006c6ae0] no frame!

我花了很多时间没有成功找到问题,这可能是什么原因?

感谢您的帮助!


当前的解决方案是错误的。这充其量只是一种解决方法。它首先将视频编码为 h264,然后将其重新混合为 mp4。这是没有必要的。

真正的解决方案是删除:

cctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

这是我的完整解决方案:

#include <iostream>
extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
    #include <libavutil/time.h>
    #include <libavutil/opt.h>
    #include <libswscale/swscale.h>
}

AVFrame* videoFrame = nullptr;
AVCodecContext* cctx = nullptr;
SwsContext* swsCtx = nullptr;
int frameCounter = 0;
AVFormatContext* ofctx = nullptr;
AVOutputFormat* oformat = nullptr;
int fps = 30;
int width = 1920;
int height = 1080;
int bitrate = 2000;

static void pushFrame(uint8_t* data) {
    int err;
    if (!videoFrame) {
        videoFrame = av_frame_alloc();
        videoFrame->format = AV_PIX_FMT_YUV420P;
        videoFrame->width = cctx->width;
        videoFrame->height = cctx->height;
        if ((err = av_frame_get_buffer(videoFrame, 32)) < 0) {
            std::cout << "Failed to allocate picture" << err << std::endl;
            return;
        }
    }
    if (!swsCtx) {
        swsCtx = sws_getContext(cctx->width, cctx->height, AV_PIX_FMT_RGB24, cctx->width, 
            cctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, 0, 0, 0);
    }
    int inLinesize[1] = { 3 * cctx->width };
    // From RGB to YUV
    sws_scale(swsCtx, (const uint8_t* const*)&data, inLinesize, 0, cctx->height, 
        videoFrame->data, videoFrame->linesize);
    videoFrame->pts = (1.0 / 30.0) * 90000 * (frameCounter++);
    std::cout << videoFrame->pts << " " << cctx->time_base.num << " " << 
        cctx->time_base.den << " " << frameCounter << std::endl;
    if ((err = avcodec_send_frame(cctx, videoFrame)) < 0) {
        std::cout << "Failed to send frame" << err << std::endl;
        return;
    }
    AV_TIME_BASE;
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    pkt.flags |= AV_PKT_FLAG_KEY;
    if (avcodec_receive_packet(cctx, &pkt) == 0) {
        static int counter = 0;
        if (counter == 0) {
            FILE* fp = fopen("dump_first_frame1.dat", "wb");
            fwrite(pkt.data, pkt.size, 1, fp);
            fclose(fp);
        }
        std::cout << "pkt key: " << (pkt.flags & AV_PKT_FLAG_KEY) << " " << 
            pkt.size << " " << (counter++) << std::endl;
        uint8_t* size = ((uint8_t*)pkt.data);
        std::cout << "first: " << (int)size[0] << " " << (int)size[1] << 
            " " << (int)size[2] << " " << (int)size[3] << " " << (int)size[4] << 
            " " << (int)size[5] << " " << (int)size[6] << " " << (int)size[7] << 
            std::endl;
        av_interleaved_write_frame(ofctx, &pkt);
        av_packet_unref(&pkt);
    }
}

static void finish() {
    //DELAYED FRAMES
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    for (;;) {
        avcodec_send_frame(cctx, NULL);
        if (avcodec_receive_packet(cctx, &pkt) == 0) {
            av_interleaved_write_frame(ofctx, &pkt);
            av_packet_unref(&pkt);
        }
        else {
            break;
        }
    }

    av_write_trailer(ofctx);
    if (!(oformat->flags & AVFMT_NOFILE)) {
        int err = avio_close(ofctx->pb);
        if (err < 0) {
            std::cout << "Failed to close file" << err << std::endl;
        }
    }
}

static void free() {
    if (videoFrame) {
        av_frame_free(&videoFrame);
    }
    if (cctx) {
        avcodec_free_context(&cctx);
    }
    if (ofctx) {
        avformat_free_context(ofctx);
    }
    if (swsCtx) {
        sws_freeContext(swsCtx);
    }
}

int main(int argc, char* argv[])
{
    av_register_all();
    avcodec_register_all();

    oformat = av_guess_format(nullptr, "test.mp4", nullptr);
    if (!oformat)
    {
        std::cout << "can't create output format" << std::endl;
        return -1;
    }
    //oformat->video_codec = AV_CODEC_ID_H265;

    int err = avformat_alloc_output_context2(&ofctx, oformat, nullptr, "test.mp4");

    if (err)
    {
        std::cout << "can't create output context" << std::endl;
        return -1;
    }

    AVCodec* codec = nullptr;

    codec = avcodec_find_encoder(oformat->video_codec);
    if (!codec)
    {
        std::cout << "can't create codec" << std::endl;
        return -1;
    }

    AVStream* stream = avformat_new_stream(ofctx, codec);

    if (!stream)
    {
        std::cout << "can't find format" << std::endl;
        return -1;
    }

    cctx = avcodec_alloc_context3(codec);

    if (!cctx)
    {
        std::cout << "can't create codec context" << std::endl;
        return -1;
    }

    stream->codecpar->codec_id = oformat->video_codec;
    stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    stream->codecpar->width = width;
    stream->codecpar->height = height;
    stream->codecpar->format = AV_PIX_FMT_YUV420P;
    stream->codecpar->bit_rate = bitrate * 1000;
    avcodec_parameters_to_context(cctx, stream->codecpar);
    cctx->time_base = (AVRational){ 1, 1 };
    cctx->max_b_frames = 2;
    cctx->gop_size = 12;
    cctx->framerate = (AVRational){ fps, 1 };
    //must remove the following
    //cctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    if (stream->codecpar->codec_id == AV_CODEC_ID_H264) {
        av_opt_set(cctx, "preset", "ultrafast", 0);
    }
    else if (stream->codecpar->codec_id == AV_CODEC_ID_H265)
    {
        av_opt_set(cctx, "preset", "ultrafast", 0);
    }

    avcodec_parameters_from_context(stream->codecpar, cctx);

    if ((err = avcodec_open2(cctx, codec, NULL)) < 0) {
        std::cout << "Failed to open codec" << err << std::endl;
        return -1;
    }

    if (!(oformat->flags & AVFMT_NOFILE)) {
        if ((err = avio_open(&ofctx->pb, "test.mp4", AVIO_FLAG_WRITE)) < 0) {
            std::cout << "Failed to open file" << err << std::endl;
            return -1;
        }
    }

    if ((err = avformat_write_header(ofctx, NULL)) < 0) {
        std::cout << "Failed to write header" << err << std::endl;
        return -1;
    }

    av_dump_format(ofctx, 0, "test.mp4", 1);

    uint8_t* frameraw = new uint8_t[1920 * 1080 * 4];
    memset(frameraw, 222, 1920 * 1080 * 4);
    for (int i = 0;i < 60;++i) {
        pushFrame(frameraw);
    }

    delete[] frameraw;
    finish();
    free();
    return 0;
}

这是我调试问题的方法:

我首先从提问者的解决方案开始。我比较了通过编码和重新复用生成的数据包,

在第一次编码期间,第一个数据包丢失了标头:

enter image description here

重新复用时,第一个数据包有一个标头:

enter image description here

然后我注意到 AV_CODEC_FLAG_GLOBAL_HEADER。从它的名字来看,它似乎写了一个全局头并剥离了数据包头。

所以我删除了它,一切正常。我详细介绍了我的发现here.

代码和makefile在这里:https://github.com/shi-yan/videosamples

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ FFmpeg 创建 mp4 文件 的相关文章

  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • 复制 std::function 的成本有多高?

    While std function是可移动的 但在某些情况下不可能或不方便 复制它会受到重大处罚吗 它是否可能取决于捕获变量的大小 如果它是使用 lambda 表达式创建的 它依赖于实现吗 std function通常被实现为值语义 小缓
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • 单个对象的 Monogame XNA 变换矩阵?

    我读过一些解释 XNA Monogame 变换矩阵的教程 问题是这些矩阵应用于 SpriteBatch Begin matrix 这意味着所有 Draw 代码都将被转换 如何将变换矩阵应用于单个可绘制对象 就我而言 我想转换滚动背景 使其自
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • 如何在 32 位或 64 位配置中以编程方式运行任何 CPU .NET 可执行文件?

    我有一个可在 32 位和 64 位处理器上运行的 C 应用程序 我试图枚举给定系统上所有进程的模块 当尝试从 64 位应用程序枚举 32 位进程模块时 这会出现问题 Windows 或 NET 禁止它 我认为如果我可以从应用程序内部重新启动
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • 为什么 std::strstream 被弃用?

    我最近发现std strstream已被弃用 取而代之的是std stringstream 我已经有一段时间没有使用它了 但它做了我当时需要做的事情 所以很惊讶听到它的弃用 我的问题是为什么做出这个决定 有什么好处std stringstr
  • 等待进程释放文件

    我如何等待文件空闲以便ss Save 可以用新的覆盖它吗 如果我紧密地运行两次 左右 我会得到一个generic GDI error
  • AES 128 CBC 蒙特卡罗测试

    我正在 AES 128 CBC 上执行 MCT 如中所述http csrc nist gov groups STM cavp documents aes AESAVS pdf http csrc nist gov groups STM ca
  • 使用管道时,如果子进程数量大于处理器数量,进程是否会被阻塞?

    当子进程数量很大时 我的程序停止运行 我不知道问题是什么 但我猜子进程在运行时以某种方式被阻止 下面是该程序的主要工作流程 void function int process num int i initial variables for
  • 如何设置 log4net 每天将我的文件记录到不同的文件夹中?

    我想将每天的所有日志保存在名为 YYYYMMdd 的文件夹中 log4net 应该根据系统日期时间处理创建新文件夹 我如何设置它 我想将一天中的所有日志保存到 n 个 1MB 的文件中 我不想重写旧文件 但想真正拥有一天中的所有日志 我该如
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 使用 .NET Process.Start 运行时挂起进程 - 出了什么问题?

    我在 svn exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其执行某些操作 但对于某些输入 它偶尔会重复挂起并且无法完成 例如 一个调用是 svn list svn list http myserver 84 svn Docum
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看
  • 如何将 PostgreSql 与 EntityFramework 6.0.2 集成? [复制]

    这个问题在这里已经有答案了 我收到以下错误 实体框架提供程序类型的 实例 成员 Npgsql NpgsqlServices Npgsql 版本 2 0 14 2 文化 中性 PublicKeyToken 5d8b90d52f46fda7 没

随机推荐