mp4文件解封装为h264+aac(dts)两个文件
1、原理
mp4的文件一般情况下是有video的编码文件+audio的编码文件封装而成,典型mp4文件,是有h264+aac组成(代码示例中的mp4音频是dts编码),如下图
2、流程
上面的流程图和代码的注释是一一对应的,可以结合代码理解一下。
3、代码
代码的运行环境是visual studio2019,如果要运行在linux上,代码基本上差不多,需要在linux上编译ffmpeg的库,然后通过cmake编译运行。
visual studio环境代码:
/*
将mp4文件解封装为h264+aac
*/
#include "Demux.h"
#include "iostream"
extern "C"
{
#include "libavformat/avformat.h"
}
int Demux::demux(std::string filename)
{
int ret;
int video_index= -1, audio_index = -1;
std::string h264_file = "";
std::string aac_file = "";
const char* mp4 = filename.data();
AVFormatContext* ifmt_ctx = NULL,*video_fmt_ctx=NULL,*audio_fmt_ctx=NULL;
AVStream* in_stream = NULL,*out_stream = NULL;
AVPacket avpacket;
h264_file.append(filename).append(".h264");
//aac_file.append(filename).append(".aac");
// 1、打开mp4文件,并读取文件header
avformat_open_input(&ifmt_ctx, mp4, NULL, NULL);
// 2、读取流信息
avformat_find_stream_info(ifmt_ctx, NULL);
av_dump_format(ifmt_ctx, 0, mp4, 0);
// 3、h264文件分配avformatcontext
ret = avformat_alloc_output_context2(&video_fmt_ctx, NULL, NULL, h264_file.data());
// 4、aac文件分配avformatcontext
//ret = avformat_alloc_output_context2(&audio_fmt_ctx, NULL, NULL, aac_file.data());
// 5、分别为h264、aac文件添加流
for (size_t i = 0; i < ifmt_ctx->nb_streams; i++)
{
in_stream = ifmt_ctx->streams[i];
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
out_stream = avformat_new_stream(video_fmt_ctx, NULL);
video_index = i;
}
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
if (in_stream->codecpar->codec_id == AV_CODEC_ID_DTS)
{
aac_file.append(filename).append(".dts");
}
else
{
aac_file.append(filename).append(".aac");
}
avformat_alloc_output_context2(&audio_fmt_ctx, NULL, NULL, aac_file.data());
out_stream = avformat_new_stream(audio_fmt_ctx, NULL);
audio_index = i;
}
if (out_stream)
{
// 拷贝编码参数
avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
}
}
// 打印输出文件信息
std::cout << "--------------------------------------------------" << std::endl;
av_dump_format(video_fmt_ctx, 0, h264_file.data(), 1);
av_dump_format(audio_fmt_ctx, 0, aac_file.data(), 1);
// 6、打开视频文件
ret = avio_open(&video_fmt_ctx->pb, h264_file.data(), AVIO_FLAG_WRITE);
if (ret < 0)
{
std::cout << "create and init avio context for access h264 file error,err=" << ret;
return -1;
}
// 7、打开音频文件
ret = avio_open(&audio_fmt_ctx->pb, aac_file.data(), AVIO_FLAG_WRITE);
if (ret < 0)
{
std::cout << "create and init avio context for access aac file error,err=" << ret;
return -2;
}
// 8、视频文件写header
ret = avformat_write_header(video_fmt_ctx, NULL);
if (ret < 0)
{
std::cout << "write h264 file header error,err=" << ret;
return -3;
}
// 9、音频文件写header
ret = avformat_write_header(audio_fmt_ctx, NULL);
if (ret < 0)
{
std::cout << "write aac file header error,err=" << ret;
return -4;
}
// 10、开始写内容
while (true)
{
ret = av_read_frame(ifmt_ctx, &avpacket);
if (ret < 0)
{
break;
}
if (avpacket.stream_index == video_index)
{
avpacket.stream_index = 0;
av_interleaved_write_frame(video_fmt_ctx, &avpacket);
}
else if (avpacket.stream_index == audio_index)
{
avpacket.stream_index = 0;
av_interleaved_write_frame(audio_fmt_ctx, &avpacket);
}
av_packet_unref(&avpacket);
}
// 11、写文件尾
av_write_trailer(video_fmt_ctx);
av_write_trailer(audio_fmt_ctx);
// 12、关闭输入、输出文件,释放资源
avformat_close_input(&ifmt_ctx);
avio_close(video_fmt_ctx->pb);
avio_close(audio_fmt_ctx->pb);
avformat_free_context(video_fmt_ctx);
avformat_free_context(audio_fmt_ctx);
return 0;
}
linux环境下代码如下,基本上是一样的:
/*
将mp4文件解封装为h264+aac
*/
#define __STDC_CONSTANT_MACROS
#include "iostream"
extern "C"
{
#include "libavformat/avformat.h"
int demux(const char *filename)
{
int ret;
int video_index= -1, audio_index = -1;
std::string h264_file = "";
std::string aac_file = "";
AVFormatContext* ifmt_ctx = NULL,*video_fmt_ctx=NULL,*audio_fmt_ctx=NULL;
AVStream* in_stream = NULL,*out_stream = NULL;
AVPacket avpacket;
h264_file.append(filename).append(".h264");
//aac_file.append(filename).append(".aac");
// 1、打开mp4文件,并读取文件header
avformat_open_input(&ifmt_ctx, filename, NULL, NULL);
// 2、读取流信息
avformat_find_stream_info(ifmt_ctx, NULL);
av_dump_format(ifmt_ctx, 0, filename, 0);
// 3、h264文件分配avformatcontext
ret = avformat_alloc_output_context2(&video_fmt_ctx, NULL, NULL, h264_file.data());
// 4、aac文件分配avformatcontext
//ret = avformat_alloc_output_context2(&audio_fmt_ctx, NULL, NULL, aac_file.data());
// 5、分别为h264、aac文件添加流
for (size_t i = 0; i < ifmt_ctx->nb_streams; i++)
{
in_stream = ifmt_ctx->streams[i];
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
out_stream = avformat_new_stream(video_fmt_ctx, NULL);
video_index = i;
}
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
if (in_stream->codecpar->codec_id == AV_CODEC_ID_DTS)
{
aac_file.append(filename).append(".dts");
}
else
{
aac_file.append(filename).append(".aac");
}
avformat_alloc_output_context2(&audio_fmt_ctx, NULL, NULL, aac_file.data());
out_stream = avformat_new_stream(audio_fmt_ctx, NULL);
audio_index = i;
}
if (out_stream)
{
// 拷贝编码参数
avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
}
}
// 打印输出文件信息
std::cout << "--------------------------------------------------" << std::endl;
av_dump_format(video_fmt_ctx, 0, h264_file.data(), 1);
av_dump_format(audio_fmt_ctx, 0, aac_file.data(), 1);
// 6、打开视频文件
ret = avio_open(&video_fmt_ctx->pb, h264_file.data(), AVIO_FLAG_WRITE);
if (ret < 0)
{
std::cout << "create and init avio context for access h264 file error,err=" << ret;
return -1;
}
// 7、打开音频文件
ret = avio_open(&audio_fmt_ctx->pb, aac_file.data(), AVIO_FLAG_WRITE);
if (ret < 0)
{
std::cout << "create and init avio context for access aac file error,err=" << ret;
return -2;
}
// 8、视频文件写header
ret = avformat_write_header(video_fmt_ctx, NULL);
if (ret < 0)
{
std::cout << "write h264 file header error,err=" << ret;
return -3;
}
// 9、音频文件写header
ret = avformat_write_header(audio_fmt_ctx, NULL);
if (ret < 0)
{
std::cout << "write aac file header error,err=" << ret;
return -4;
}
// 10、开始写内容
while (true)
{
ret = av_read_frame(ifmt_ctx, &avpacket);
if (ret < 0)
{
break;
}
if (avpacket.stream_index == video_index)
{
avpacket.stream_index = 0;
av_interleaved_write_frame(video_fmt_ctx, &avpacket);
}
else if (avpacket.stream_index == audio_index)
{
avpacket.stream_index = 0;
av_interleaved_write_frame(audio_fmt_ctx, &avpacket);
}
av_packet_unref(&avpacket);
}
// 11、写文件尾
av_write_trailer(video_fmt_ctx);
av_write_trailer(audio_fmt_ctx);
// 12、关闭输入、输出文件,释放资源
avformat_close_input(&ifmt_ctx);
avio_close(video_fmt_ctx->pb);
avio_close(audio_fmt_ctx->pb);
avformat_free_context(video_fmt_ctx);
avformat_free_context(audio_fmt_ctx);
return 0;
}
}
4、结果
最后代码运行后,会生成两个文件,一个是h264文件,一个是音频aac或者dts文件,如图:
5、代码链接
visual studio环境:https://github.com/liupengh3c/goffmpeg
linux(Ubuntu18.04)环境:https://github.com/liupengh3c/myffmpeg
注意:linux下环境代码,进入build文件夹,直接运行:sh build.sh即可进行编译。
在运行可执行文件之前,需要设置环境变量LD_LIBRARY_PATH,将本代码库的lib文件夹加入到这个环境变量中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/liupeng/ffmpeg/myffmpeg/lib,这样即可运行。
6、播放
解封装出的h264和dts文件,可以通过fplay进行播放,命令行很简单,比如我的windows环境上:
./study/ffmpeg-20200617-0b3bd00-win64-static/bin/ffplay.exe toy3.mp4.h264
./study/ffmpeg-20200617-0b3bd00-win64-static/bin/ffplay.exe toy3.mp4.dts
觉着这篇文章对自己有益的土豪朋友可以扫描屏幕下方二维码金额随意,感谢大家支持,增加写作动力。
本文地址:https://blog.csdn.net/liupenglove/article/details/107282824