【FFMpeg视频开发与应用基础】六、调用FFMpeg SDK实现视频文件的转封装

简介: 《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK工程代码地址:FFmpeg_Tutorial有时候我们可能会面对这样的一种需求,即我们不需要对视频内的音频或视频信号进行什么实际的操作,只是希望能把文件的封装格式进行转换,例如从avi转换为mp4格式或者flv格式等。

《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK

工程代码地址:FFmpeg_Tutorial


有时候我们可能会面对这样的一种需求,即我们不需要对视频内的音频或视频信号进行什么实际的操作,只是希望能把文件的封装格式进行转换,例如从avi转换为mp4格式或者flv格式等。实际上,转封装不需要对内部的音视频进行解码,只需要根据从输入文件中获取包含的数据流添加到输出文件中,然后将输入文件中的数据包按照规定格式写入到输出文件中去。


1、解析命令行参数

如同之前的工程一样,我们使用命令行参数传入输入和输出的文件名。为此,我们定义了如下的结构体和函数来实现传入输入输出文件的过程:

typedef struct _IOFiles
{
    const char *inputName;
    const char *outputName;
} IOFiles;

static bool hello(int argc, char **argv, IOFiles &io_param)
{
    printf("FFMpeg Remuxing Demo.\nCommand format: %s inputfile outputfile\n", argv[0]);
    if (argc != 3)
    {
        printf("Error: command line error, please re-check.\n");
        return false;
    }

    io_param.inputName = argv[1];
    io_param.outputName = argv[2];

    return true;
}

在main函数执行时,调用hello函数解析命令行并保存到IOFiles结构中:

int main(int argc, char **argv)
{
    IOFiles io_param;
    if (!hello(argc, argv, io_param))
    {
        return -1;
    }
    //......
}

2、所需要的结构与初始化操作

为了实现视频文件的转封装操作,我们需要以下的结构:

AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;

然后所需要的初始化操作有打开输入视频文件、获取其中的流信息和获取输出文件的句柄:

av_register_all();

//按封装格式打开输入视频文件
if ((ret = avformat_open_input(&ifmt_ctx, io_param.inputName, NULL, NULL)) < 0)
{
    printf("Error: Open input file failed.\n");
    goto end;
}

//获取输入视频文件中的流信息
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
{
    printf("Error: Failed to retrieve input stream information.\n");
    goto end;
}
av_dump_format(ifmt_ctx, 0, io_param.inputName, 0);

//按照文件名获取输出文件的句柄
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, io_param.outputName);
if (!ofmt_ctx)
{
    printf("Error: Could not create output context.\n");
    goto end;
}
ofmt = ofmt_ctx->oformat;

3、 向输出文件中添加Stream并打开输出文件

在我们获取到了输入文件中的流信息后,保持输入流中的codec不变,并以其为依据添加到输出文件中:

for (unsigned int i = 0; i < ifmt_ctx->nb_streams ; i++)
{
    AVStream *inStream = ifmt_ctx->streams[i];
    AVStream *outStream = avformat_new_stream(ofmt_ctx, inStream->codec->codec);
    if (!outStream)
    {
        printf("Error: Could not allocate output stream.\n");
        goto end;
    }

    ret = avcodec_copy_context(outStream->codec, inStream->codec);
    outStream->codec->codec_tag = 0;
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
    {
        outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
}

av_dump_format(ofmt_ctx, 0, io_param.outputName, 1);

这里调用了函数avcodec_copy_context函数,该函数的声明如下:

int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);

该函数的作用是将src表示的AVCodecContext中的内容拷贝到dest中。

随后,调用avio_open函数打开输出文件:

av_dump_format(ofmt_ctx, 0, io_param.outputName, 1);

if (!(ofmt->flags & AVFMT_NOFILE))
{
    ret = avio_open(&ofmt_ctx->pb, io_param.outputName, AVIO_FLAG_WRITE);
    if (ret < 0)
    {
        printf("Error: Could not open output file.\n");
        goto end;
    }
}

4、写入文件的音视频数据

首先向输出文件中写入文件头:

ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) 
{
    printf("Error: Could not write output file header.\n");
    goto end;
}

写入文件的视频和音频包数据,其实就是将音频和视频Packets从输入文件中读出来,正确设置pts和dts等时间量之后,再写入到输出文件中去:

while (1) 
{
    AVStream *in_stream, *out_stream;

    ret = av_read_frame(ifmt_ctx, &pkt);
    if (ret < 0)
        break;

    in_stream  = ifmt_ctx->streams[pkt.stream_index];
    out_stream = ofmt_ctx->streams[pkt.stream_index];

    /* copy packet */
    pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
    pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
    pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    pkt.pos = -1;

    ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    if (ret < 0) 
    {
        fprintf(stderr, "Error muxing packet\n");
        break;
    }
    av_free_packet(&pkt);
}

最后要做的就是写入文件尾:

av_write_trailer(ofmt_ctx);

5、 收尾工作

写入输出文件完成后,需要对打开的结构进行关闭或释放等操作。主要有关闭输入输出文件、释放输出文件的句柄等:

avformat_close_input(&ifmt_ctx);

/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
    avio_closep(&ofmt_ctx->pb);

avformat_free_context(ofmt_ctx);

if (ret < 0 && ret != AVERROR_EOF) 
{
    fprintf(stderr, "Error failed to write packet to output file.\n");
    return 1;
}
目录
相关文章
|
20天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
21天前
|
Unix Linux Shell
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
在Linux环境下交叉编译Android所需的FFmpeg so库,首先下载`android-ndk-r21e`,然后解压。接着,上传FFmpeg及相关库(如x264、freetype、lame)源码,修改相关sh文件,将`SYSTEM=windows-x86_64`改为`SYSTEM=linux-x86_64`并删除回车符。对x264的configure文件进行修改,然后编译x264。同样编译其他第三方库。设置环境变量`PKG_CONFIG_PATH`,最后在FFmpeg源码目录执行配置、编译和安装命令,生成的so文件复制到App工程指定目录。
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
|
20小时前
|
算法 Linux Windows
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
在Windows环境下为FFmpeg集成字幕渲染库libass涉及多个步骤,包括安装freetype、libxml2、gperf、fontconfig、fribidi、harfbuzz和libass。每个库的安装都需要下载源码、配置、编译和安装,并更新PKG_CONFIG_PATH环境变量。最后,重新配置并编译FFmpeg以启用libass及相关依赖。完成上述步骤后,通过`ffmpeg -version`确认libass已成功集成。
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
|
4天前
|
编解码 C语言
FFMPEG 获取视频PTS
FFMPEG 获取视频PTS
12 0
|
6天前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
|
7天前
|
Web App开发 Windows
FFmpeg开发笔记(十五)详解MediaMTX的推拉流
MediaMTX是开源轻量级流媒体服务器,提供RTSP, RTMP, HLS, WebRTC和SRT服务。启动后,它在不同端口监听。通过FFmpeg的推拉流测试,证明了MediaMTX成功实现HLS流媒体转发,但HLS播放兼容性问题可能因缺少音频流导致。推流地址为rtsp://127.0.0.1:8554/stream,RTMP地址为rtmp://127.0.0.1:1935/stream,HLS播放地址为http://127.0.0.1:8888/stream(Chrome)和http://127.0.0.1:8888/stream/index.m3u8(其他播放器可能不支持)。
FFmpeg开发笔记(十五)详解MediaMTX的推拉流
|
9天前
|
Web App开发 编解码 vr&ar
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
41 2
|
10天前
|
缓存 编解码
FFmpeg开发笔记(十四)FFmpeg音频重采样的缓存
FFmpeg在视频流重编码和音频重采样中使用缓存机制。在音频文件格式转换时,特别是对于帧长度不固定的格式如ogg、amr、wma,需处理重采样缓存。通过调用`swr_convert`,传入空输入和0大小来清空缓存。在`swrmp3.c`中,修改帧样本数处理,并在循环结束后添加代码以冲刷缓存。编译并运行程序,将ogg文件重采样为MP3,日志显示操作成功,播放转换后的文件确认功能正常。
FFmpeg开发笔记(十四)FFmpeg音频重采样的缓存
|
13天前
|
编解码 Linux Windows
FFmpeg开发笔记(十三)Windows环境给FFmpeg集成libopus和libvpx
本文档介绍了在Windows环境下如何为FFmpeg集成libopus和libvpx库。首先,详细阐述了安装libopus的步骤,包括下载源码、配置、编译和安装,并更新环境变量。接着,同样详细说明了libvpx的安装过程,注意需启用--enable-pic选项以避免编译错误。最后,介绍了重新配置并编译FFmpeg以启用这两个库,通过`ffmpeg -version`检查是否成功集成。整个过程参照了《FFmpeg开发实战:从零基础到短视频上线》一书的相关章节。
FFmpeg开发笔记(十三)Windows环境给FFmpeg集成libopus和libvpx
|
14天前
|
编解码 Linux
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
在《FFmpeg开发实战》一书中,介绍了如何在Linux环境下为FFmpeg集成libopus和libvpx,以支持WebM格式的Opus和VP8/VP9编码。首先,下载并安装libopus。接着,下载并安装libvpx。最后,在FFmpeg源码目录下,重新配置FFmpeg,启用libopus和libvpx,编译并安装。通过`ffmpeg -version`检查版本信息,确认libopus和libvpx已启用。
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx