首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

如何通过libavformat将H264流复用成MP4文件

基础概念

libavformat 是 FFmpeg 项目中的一个库,用于处理多媒体文件的格式解析和封装。H.264 是一种视频编码标准,广泛用于高清视频传输和存储。MP4 是一种多媒体容器格式,可以包含视频、音频、字幕等多种媒体流。

相关优势

  1. 兼容性:MP4 格式在各种设备和播放器上都有很好的兼容性。
  2. 灵活性:可以包含多种媒体流,并且可以灵活地添加或删除音轨、字幕等。
  3. 压缩效率:H.264 编码在保证视频质量的同时,能够有效减少文件大小。

类型

  • 视频流:H.264 编码的视频流。
  • 音频流:通常为 AAC 编码的音频流。
  • 字幕流:可选的 SRT 或其他格式的字幕流。

应用场景

  • 视频会议记录。
  • 在线视频流媒体服务。
  • 移动设备上的视频播放。

实现步骤

以下是一个简单的示例代码,展示如何使用 libavformat 将 H.264 流复用成 MP4 文件:

代码语言:txt
复制
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>

int main(int argc, char *argv[]) {
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *video_dec_ctx = NULL, *video_enc_ctx = NULL;
    AVStream *video_st = NULL;
    AVPacket pkt;
    int ret, i;

    // 初始化 libavformat 和注册所有编解码器、格式和协议
    av_register_all();

    // 打开输入文件
    if ((ret = avformat_open_input(&fmt_ctx, "input.h264", NULL, NULL)) < 0) {
        fprintf(stderr, "Could not open input file '%s'", argv[1]);
        goto end;
    }

    // 查找流信息
    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        goto end;
    }

    // 查找视频流
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_st = fmt_ctx->streams[i];
            break;
        }
    }

    if (!video_st) {
        fprintf(stderr, "Could not find video stream in the input, aborting\n");
        ret = -1;
        goto end;
    }

    // 获取解码器参数并打开解码器
    AVCodec *video_dec = avcodec_find_decoder(video_st->codecpar->codec_id);
    if (!video_dec) {
        fprintf(stderr, "Failed to find decoder for stream #%u\n", video_st->index);
        ret = AVERROR_DECODER_NOT_FOUND;
        goto end;
    }

    video_dec_ctx = avcodec_alloc_context3(video_dec);
    if (!video_dec_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ret = avcodec_parameters_to_context(video_dec_ctx, video_st->codecpar);
    if (ret < 0) {
        fprintf(stderr, "Failed to copy decoder parameters to input context\n");
        goto end;
    }

    if ((ret = avcodec_open2(video_dec_ctx, video_dec, NULL)) < 0) {
        fprintf(stderr, "Failed to open decoder for stream #%u\n", video_st->index);
        goto end;
    }

    // 创建输出格式上下文
    AVOutputFormat *out_fmt = av_guess_format(NULL, "output.mp4", NULL);
    if (!out_fmt) {
        fprintf(stderr, "Could not deduce output format from file extension: using MPEG-4.\n");
        out_fmt = av_guess_format("mp4", NULL, NULL);
    }

    if (!out_fmt) {
        fprintf(stderr, "Could not find suitable output format\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    AVFormatContext *out_fmt_ctx = NULL;
    avformat_alloc_output_context2(&out_fmt_ctx, out_fmt, NULL, "output.mp4");
    if (!out_fmt_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    // 复制视频流
    AVCodec *video_enc = avcodec_find_encoder(out_fmt->video_codec);
    if (!video_enc) {
        fprintf(stderr, "Necessary encoder not found\n");
        ret = AVERROR_ENCODER_NOT_FOUND;
        goto end;
    }

    video_enc_ctx = avcodec_alloc_context3(video_enc);
    if (!video_enc_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    video_enc_ctx->width = video_dec_ctx->width;
    video_enc_ctx->height = video_dec_ctx->height;
    video_enc_ctx->sample_aspect_ratio = video_dec_ctx->sample_aspect_ratio;
    video_enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    video_enc_ctx->time_base = video_st->time_base;

    if (out_fmt->flags & AVFMT_GLOBALHEADER)
        video_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    ret = avcodec_parameters_from_context(video_enc_ctx->codecpar, video_dec_ctx);
    if (ret < 0) {
        fprintf(stderr, "Could not copy the stream parameters\n");
        goto end;
    }

    ret = avcodec_open2(video_enc_ctx, video_enc, NULL);
    if (ret < 0) {
        fprintf(stderr, "Cannot open video encoder for stream #%u\n", video_st->index);
        goto end;
    }

    AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
    if (!out_stream) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ret = avcodec_parameters_copy(out_stream->codecpar, video_st->codecpar);
    if (ret < 0) {
        fprintf(stderr, "Failed to copy encoder parameters to output stream #%u\n", out_stream->index);
        goto end;
    }

    out_stream->time_base = video_enc_ctx->time_base;

    // 打开输出文件
    if (!(out_fmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&out_fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'", "output.mp4");
            goto end;
        }
    }

    // 写入文件头
    ret = avformat_write_header(out_fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        goto end;
    }

    // 读取输入文件并写入输出文件
    while (1) {
        ret = av_read_frame(fmt_ctx, &pkt);
        if (ret < 0)
            break;

        if (pkt.stream_index == video_st->index) {
            ret = avcodec_send_packet(video_dec_ctx, &pkt);
            if (ret < 0) {
                fprintf(stderr, "Error sending a packet for decoding\n");
                goto end;
            }

            while (ret >= 0) {
                ret = avcodec_receive_frame(video_dec_ctx, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if (ret < 0) {
                    fprintf(stderr, "Error during decoding\n");
                    goto end;
                }

                frame->pts = frame->pkt_dts;

                ret = avcodec_send_frame(video_enc_ctx, frame);
                if (ret < 0) {
                    fprintf(stderr, "Error sending a frame for encoding\n");
                    goto end;
                }

                while (ret >= 0) {
                    ret = avcodec_receive_packet(video_enc_ctx, &out_pkt);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        break;
                    else if (ret < 0) {
                        fprintf(stderr, "Error during encoding\n");
                        goto end;
                    }

                    out_pkt.stream_index = out_stream->index;
                    ret = av_interleaved_write_frame(out_fmt_ctx, &out_pkt);
                    if (ret < 0) {
                        fprintf(stderr, "Error while writing output packet\n");
                        goto end;
                    }
                    av_packet_unref(&out_pkt);
                }
            }
        }
        av_packet_unref(&pkt);
    }

    // 写入文件尾
    av_write_trailer(out_fmt_ctx);

end:
    av_packet_unref(&pkt);
    if (video_dec_ctx)
        avcodec_close(video_dec_ctx);
    if (video_enc_ctx)
        avcodec_close(video_enc_ctx);
    if (fmt_ctx && !(fmt_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&fmt_ctx->pb);
    if (out_fmt_ctx && !(out_fmt->flags & AVFMT_NOFILE))
        avio_closep(&out_fmt_ctx->pb);
    avformat_free_context(fmt_ctx);
    avformat_free_context(out_fmt_ctx);

    return ret < 0;
}

参考链接

常见问题及解决方法

  1. 编码器未找到
    • 确保 FFmpeg 编译时包含了所需的编码器。
    • 检查编码器名称是否正确。
  • 解码器未找到
    • 确保 FFmpeg 编译时包含了所需的解码器。
    • 检查解码器名称是否正确。
  • 文件打开失败
    • 确保输入文件存在且可读。
    • 确保输出文件路径可写。
  • 编码或解码错误
    • 检查输入数据的格式是否正确。
    • 确保编码和解码参数一致。

通过以上步骤和示例代码,你可以将 H.264 流复用成 MP4 文件。如果遇到具体问题,请根据错误信息进行排查和解决。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

ffmpeg常用库、术语、API、数据结构总结

(Conainer/File):指特定格式的多媒体文件,比如mp4、flv、mov等视频格式; 媒体(Stream):一个容器(如mp4文件)中可存在多个,可以是视频、音频、字幕。...: h264,一路是音频Audio: aac。 ​...二、常用概念 音视频领域-复用器和解复用复用器:把多路原始按一定的规则组成一个新的音视频文件(例如:把一个纯视频、一个纯音频和一个纯字幕组合成一个mp4文件); 解复用器:把一个新的音视频文件按一定的规则拆分成多路原始...(例如:把一个mp4文件,拆分成纯视频、纯音频和纯字幕)。...例如:把一段音频的声道由双声道转换成单声道的数据,就可以通过该库进行操作。 libswscale(*重要) 该模块提供了图像进行格式信息转换的模块。

1K30

如何使用libavcodec.h264文件解码为.yuv图像序列?

一.打开和关闭输入文件和输出文件 //io_data.cpp static FILE* input_file= nullptr; static FILE* output_file= nullptr;...AVCodecParserContext是码解析器的句柄,其作用是从一串二进制数据中解析出 符合某种编码标准的码包。...<<endl; return -1; } return 0; } 三.解码循环体   解码循环体至少需要实现以下三个功能:     1.从输入源中循环获取码包     ...2.当前帧传入解码器,获取输出的图像帧     3.输出解码获取的图像帧到输出文件   从输入文件中读取数据添加到缓存,并判断输入文件是否到达结尾: io_data.cpp int32_t end_of_input_file...当调用av_parser_parse2()函数时,首先通过参数指定保存 某一段码数据的缓存区及其长度,然后通过输出poutbuf指针或poutbuf_size的值来判断是否读取了一个完整的AVPacket

23720
  • IjkPlayer起播速度优化

    如Http的链接,对应libavformat/http.c 而http的请求后续会转换成Tcp的协议,对应libavformat/tcp.c 进行DNS解析ip地址,并且解析完后进行缓存,以便下次复用...从链路中读取数据到Buffer 有可能从tcp链路,也有可能从磁盘链路 TCP链路则会需要等待三次握手的时间 读取Buffer进行文件类型的probe 探测文件格式,判断是mp4,flv等等 读取Buffer...的头部信息进行解析 解析文件头部,判断是否为该格式文件,如果失败则返回错误 解析audio,video,subtitle 根据文件信息找到多媒体 优先使用H264的视频 根据信息找到解码器...开启各个线程开始对各个流进行解码packet 同步到read_thread线程后,装入pakcetQueue中 在video_refresh_thread线程中,读取packetQueue中的包,进行时钟同步...设置最长分析时长 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100L); // 通过立即清理数据包来减少等待时长

    4.9K20

    FFmpeg封装格式处理

    概述 1.1 封装格式简介 封装格式(container format)可以看作是编码(音频、视频等)数据的一层外壳,编码后的数据存储于此封装格式的文件之内。...在FFmpeg中,mux指复用,是multiplex的缩写,表示多路(视频、音频、字幕等)混入一路输出中(普通文件等)。...文件格式由文件扩展名标识,主要起提示作用,通过扩展名提示文件类型(或封装格式)信息。.../aac裸封装格式 h264封装格式和aac裸封装格式在后面的解复用复用例程中会用到,这里先讨论一下。...packet交织是指:不同的packet在输出媒体文件中应严格按照packet中dts递增的顺序交错存放。 本函数直接packet写入复用器(muxer),不会缓存或记录任何packet。

    3.1K20

    2023-03-24:音视频mp3和h264混合(muxer)编码为mp4,用go语言编写。

    2023-03-24:音视频mp3和h264混合(muxer)编码为mp4,用go语言编写。...答案2023-03-24: 这是一个使用FFmpeg库MP3和H.264混合编码为MP4的Go语言程序。程序的大体过程如下: 1.设置FFmpeg库路径和环境变量。...2.检查并创建输出目录,以及输入视频和音频文件。 3.打开输入视频文件并查找视频,打开输入音频文件并查找音频。 4.新建输出上下文,并为视频和音频创建新的输出。 5.打开输出文件。...6.写输出文件头。 7.读取输入视频和音频帧,将它们转换为输出格式,写入输出文件。在这个过程中,需要进行PTS/DTS转换和比特流过滤(如果需要)。 8.写输出文件尾。.../examples/a24.video_muxer_mp3h2642mp4/main.go 代码参考[24:音视频mp3和h264混合(muxer)编码为mp4](https://feater.top/

    26810

    FFmpeg封装格式处理2-解复用例程

    复用例程 解复用(demux),表示从一路输入中分离出多路(视频、音频、字幕等)。 本例实现,输入文件中的视频和音频分离出来,保存为单独的文件,所保存的文件是不含封装格式的裸文件。 ?...#include int main (int argc, char **argv) { if (argc !...'tnshih.flv'封装格式为flv,包含一路h264编码的视频和一路aac编码的音频。...原因参考雷霄骅博士的文章: “使用FFMPEG类库分离出多媒体文件中的H.264码” “最简单的基于FFmpeg的封装格式处理:视音频分离器简化版” 本节代码仅关注最简单的解复用功能,FLV、MP4...等特定容器中分离出来的h264视频和aac音频无法播放。

    1.1K30

    ffmpeg解码之使用C语言打印音视频信息

    文章目录 一、通过此文可以得到什么 二、实现思路 三、实现效果 四、实现源代码 一、通过此文可以得到什么 通过此练习: 1、知道了如何计算一个音频和视频的播放时间; 2、知道了音视频解码的思路的大体流程...,之后无非就是在这个流程上进行扩充细节; 3、知道了如何通过C语言或者C++编程语言结合ffmpeg拿到一些音视频的关键信息,例如:帧率等; 二、实现思路 三、实现效果 zhenghui@zh-pc:...include #include #include #include <libavformat.../视频/1080P.mp4"; printf("decode video fileName=%s \n",fileName); int ret = 0; // 1、分配解复用器上下文...AVFormatContext *ctx = NULL; ctx = avformat_alloc_context(); // 2、根据url打开本地文件或网络

    1.3K30

    EasyDSS如何MP4点播文件转化成RTSP视频

    EasyDSS视频直播点播平台支持Flash、H5播放,可兼容多操作系统,平台可支持视频直播、点播、转码、分发、存储等功能,支持用户自行上传视频文件,以及将上传的点播文件作为虚拟直播进行播放等等。...EasyDSS平台能胜任高强度、大数据量的视频资源转码工作,支持各种格式的视频资源进行快速转码。...有用户提出需求,需要将mp4文件上传到EasyDSS平台,然后将其转化成RTSP视频分发到用户自己的智能视频分析平台。该需求可以通过EasyDSS平台自带的点播、转码、虚拟直播功能来实现。...接来下为大家介绍一下如何上传MP4文件并转码RTSP视频。...1)首先,MP4文件上传到EasyDSS平台:2)打开虚拟直播,配置点播文件:3)如图,点击编辑,此时可以获取到RTSP的视频流了:EasyDSS平台在流媒体视频功能上具有很强的灵活性,在视频直播、点播方面

    69120

    FFmpeg使用手册 - FFmpeg 的基本组成

    根据图中的转码工作流程可以看出,首先ffmpeg读取输入源,然后通过Demuxer音视频包分解开,这个动作通过调用libavformat中的接口即可实现,接下来通过Decoder进行解码,音视频通过...接下来编码后的音视频数据包通过Muxer进行封装,Muxer封装通过libavformat中的接口即可实现,输出成为输出。...根据图中可以看到,使用ffprobe能够查看到mp4文件容器中的的信息,包含了一个视频,由于该文件中只有视频相关的信息通过[STREAM][/STREAM]的方式展现出来,在[STREAM]与...[/STREAM]之间的信息为该mp4文件的视频信息。...当视频文件容器中包含音频与视频或者更多路时,会通过[STREAM]与[/STREAM]进行多个的分隔,分割后区分流的索引信息采用的是index来进行区分。

    2.8K30

    2023-03-24:音视频mp3和h264混合(muxer)编码为mp4,用go语言编写。

    2023-03-24:音视频mp3和h264混合(muxer)编码为mp4,用go语言编写。答案2023-03-24:这是一个使用FFmpeg库MP3和H.264混合编码为MP4的Go语言程序。...2.检查并创建输出目录,以及输入视频和音频文件。3.打开输入视频文件并查找视频,打开输入音频文件并查找音频。4.新建输出上下文,并为视频和音频创建新的输出。5.打开输出文件。6.写输出文件头。...7.读取输入视频和音频帧,将它们转换为输出格式,写入输出文件。在这个过程中,需要进行PTS/DTS转换和比特流过滤(如果需要)。8.写输出文件尾。9.清理资源并关闭输入和输出文件。.../examples/a24.video_muxer_mp3h2642mp4/main.go代码参考24:音视频mp3和h264混合(muxer)编码为mp4,代码如下:// https://feater.top..., libavformat.AVIO_FLAG_WRITE) < 0 {fmt.Printf("can't open out file\n")goto end}}//写文件头if ofmtCtx.AvformatWriteHeader

    45000

    如何视频文件.h264和音频文件.mp3复用为输出文件output.mp4?

    一.初始化复用器   在这个部分我们可以分三步进行:(1)打开输入视频文件上下文句柄 (2)打开输入音频文件上下文句柄 (3)打开输出文件上下文句柄   1.打开输入视频文件上下文句柄     在这一步...(),在创建了输出文件上下文句柄后,我们需要添加一路音频和一路视频,此时我们需要用到函数avformat_new_stream();在调用此函数后,我们会得到AVStream *类型的指针。...然后,我们需要将输入视频文件和音频文件的编码器相关参数复制到输出的视频和音频编码器中。最后,打开输出文件文件的I/O结构对应到输出文件的AVFormatContext结构。...} result=init_output(output_file); if(result<0){ return -1; } return 0; } 二.复用音频和视频...time_base为基准的,在写入输出文件时,需要转换为以输出的time_base为基准。

    24120

    Android FFmpeg 简介(十三)

    FFmpeg 的原理 整个过程基本可以说:解复用 => 解码 => 编码 => 复用器。...image.png FFmpeg主要构成 ffmpeg——一个命令行工具,用来对视频文件转换格式,也支持对电视卡即时编码 ffserver——一个HTTP多媒体即时广播服务器,支持时光平移 ffplay...——一个简单的播放器,基于SDL与FFmpeg库 libavcodec——包含全部FFmpeg音频/视频编解码库 libavformat——包含demuxers和muxer库 libavutil——包含一些工具库...解码(h264, mpeg2, aac, mp3, …) AVStream存储一个视频/音频的相关数据; 每个AVStream对应一个AVCodecContext,存储该视频/音频使用解码方式的相关数据...Mac 安装: brew install ffmpeg 命令行语法: ffmpeg[全局参数][输入文件参数]-i[输入文件][输出文件参数][输出文件] FFmpeg常用参数 主要参数 -i 设定输入流

    1.8K30

    AVFormatContext封装层:理论与实战

    在 FFmpeg 中,mux 指复用,是 multiplex 的缩写,表示多路(视频、音频、字幕等)混入一路输出中(普通文件等)。...FFmpeg 支持的封装非常多, 下面仅列出最常用的几种: h264/aac 裸封装格式 h264封装格式和 aac 裸封装格式在后面的解复用复用例程中会用到,这里先讨论一下。...本函数直接 packet 写入复用器(muxer),不会缓存或记录任何 packet。本函数不负责不同的 packet 交织问题。,由调用者负责。...三、 实战 1:解封装 1、原理讲解 本例子实现的是音视频分离,例如封装格式为 FLV、MKV、MP4、AVI 等封装格式的文件音频、视频读取出来并打印。...而是直接视音频压缩码从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件

    43410

    FFmepg 核心开发库及重要数据结构与API

    (注意:FFMPEG 中文件也被当做一种协议“file” ) 解封装(flv,avi,rmvb,mp4) AVFormatContext 主要存储视音频封装格式中包含的信息;AVInputFormat...填充上正确的信息 CODEC_TYPE_VIDEO; //通过判断得到视频类型 avcodec_find_decoder(); //查找解码器 avcodec_open(); //打开编解码器 avcodec_alloc_frame...(转码流程) demuxer:解复用;即将音视频分离出来,上图中的视频是 AVC(H264)格式,音频是 AAC 格式; decoder:解码器; filter: 帧处理,这里原始 19201080...转换为 1280720; encoder:编码器,这里是 H265 的视频格式; muxer:复用音频和视频进行重新封装。...所以解复用这一步就是文件中捆绑的音频和视频分开来以方便后面分别对它们进行解码。

    31710
    领券