参考:https://blog.csdn.net/leixiaohua1020/article/details/39802819
流程图
程序源码
/*
*
* 本程序可以将封装格式中的视频码流数据和音频码流数据分离出来
*
*/
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
}
/*
FIX: H.264 in some container format (FLV, MP4, MKV etc.) need
"h264_mp4toannexb" bitstream filter (BSF)
*Add SPS,PPS in front of IDR frame
*Add start code ("0,0,0,1") in front of NALU
H.264 in some container (MPEG2TS) don't need this BSF.
*/
//'1': Use H.264 Bitstream Filter
#define USE_H264BSF 0
int open_codec_context(int *streamIndex, AVFormatContext *&ofmtCtx, AVFormatContext *ifmtCtx, AVMediaType type)
{
AVStream *outStream = NULL, *inStream = NULL;
int ret = -1, index = -1;
index = av_find_best_stream(ifmtCtx, type, -1, -1, NULL, 0);
if (index < 0)
{
printf("can't find %s stream in input file\n", av_get_media_type_string(type));
return ret;
}
inStream = ifmtCtx->streams[index];
outStream = avformat_new_stream(ofmtCtx, NULL);
if (!outStream)
{
printf("failed to allocate output stream\n");
return ret;
}
ret = avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
if (ret < 0)
{
printf("failed to copy codec parametes\n");
return ret;
}
outStream->codecpar->codec_tag = 0;
if (ofmtCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
*streamIndex = index;
return 0;
}
int main(int argc, char *argv[])
{
AVFormatContext *ifmtCtx = NULL, *ofmtCtxAudio = NULL, *ofmtCtxVideo = NULL;
// AVCodecContext *codecCtxVideo = NULL, *codecCtxAudio = NULL;
AVPacket packet;
int videoIndex = -1, audioIndex = -1;
int ret = 0;
char inFilename[128] = "input.mp4";
char outFilenameAudio[128] = "output.aac";
char outFilenameVideo[128] = "output.h264";
#if USE_H264BSF
AVBitStreamFilterContext *h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif
//注册设备
avdevice_register_all();
//打开输入流
ret = avformat_open_input(&ifmtCtx, inFilename, 0, 0);
if (ret < 0)
{
printf("can't open input file\n");
goto end;
}
//获取流信息
ret = avformat_find_stream_info(ifmtCtx, 0);
if (ret < 0)
{
printf("can't retrieve input stream information\n");
goto end;
}
//创建输出上下文:视频
avformat_alloc_output_context2(&ofmtCtxVideo, NULL, NULL, outFilenameVideo);
if (!ofmtCtxVideo)
{
printf("can't create video output context");
goto end;
}
//创建输出上下文:音频
avformat_alloc_output_context2(&ofmtCtxAudio, NULL, NULL, outFilenameAudio);
if (!ofmtCtxAudio)
{
printf("can't create audio output context");
goto end;
}
#if 1
ret = open_codec_context(&videoIndex, ofmtCtxVideo, ifmtCtx, AVMEDIA_TYPE_VIDEO);
if (ret < 0)
{
printf("can't decode video context\n");
goto end;
}
ret = open_codec_context(&audioIndex, ofmtCtxAudio, ifmtCtx, AVMEDIA_TYPE_AUDIO);
if (ret < 0)
{
printf("can't decode video context\n");
goto end;
}
#endif
#if 0
for (i = 0; i < ifmtCtx->nb_streams; ++i)
{
AVFormatContext *ofmtCtx;
AVStream *inStream = ifmtCtx->streams[i];
AVStream *outStream = NULL;
if (ifmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoIndex = i;
outStream = avformat_new_stream(ofmtCtxVideo, inStream->codec->codec);
ofmtCtx = ofmtCtxVideo;
}
else if (ifmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
audioIndex = i;
outStream = avformat_new_stream(ofmtCtxAudio, inStream->codec->codec);
ofmtCtx = ofmtCtxAudio;
}
else
{
break;
}
if (!outStream)
{
printf("failed to allocate output stream\n");
goto end;
}
if (avcodec_copy_context(outStream->codec, inStream->codec) < 0)
{
printf("failed to copy context from input to output stream codec context\n");
goto end;
}
outStream->codec->codec_tag = 0;
if (ofmtCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
#endif
//Dump Format------------------
printf("\n==============Input Video=============\n");
av_dump_format(ifmtCtx, 0, inFilename, 0);
printf("\n==============Output Video============\n");
av_dump_format(ofmtCtxVideo, 0, outFilenameVideo, 1);
printf("\n==============Output Audio============\n");
av_dump_format(ofmtCtxAudio, 0, outFilenameAudio, 1);
printf("\n======================================\n");
//打开输出文件:视频
if (!(ofmtCtxVideo->oformat->flags & AVFMT_NOFILE))
{
if (avio_open(&ofmtCtxVideo->pb, outFilenameVideo, AVIO_FLAG_WRITE) < 0)
{
printf("can't open output file: %s\n", outFilenameVideo);
goto end;
}
}
//打开输出文件:音频
if (!(ofmtCtxAudio->oformat->flags & AVFMT_NOFILE))
{
if (avio_open(&ofmtCtxAudio->pb, outFilenameAudio, AVIO_FLAG_WRITE) < 0)
{
printf("can't open output file: %s\n", outFilenameVideo);
goto end;
}
}
//写文件头
if (avformat_write_header(ofmtCtxVideo, NULL) < 0)
{
printf("Error occurred when opening video output file\n");
goto end;
}
if (avformat_write_header(ofmtCtxAudio, NULL) < 0)
{
printf("Error occurred when opening audio output file\n");
goto end;
}
while (1)
{
AVFormatContext *ofmtCtx;
AVStream *inStream, *outStream;
if (av_read_frame(ifmtCtx, &packet) < 0)
{
break;
}
inStream = ifmtCtx->streams[packet.stream_index];
if (packet.stream_index == videoIndex)
{
outStream = ofmtCtxVideo->streams[0];
ofmtCtx = ofmtCtxVideo;
#if USE_H264BSF
av_bitstream_filter_filter(h264bsfc, inStream->codec, NULL, &packet.data, &packet.size, packet.data,
packet.size, 0);
#endif
}
else if (packet.stream_index == audioIndex)
{
outStream = ofmtCtxAudio->streams[0];
ofmtCtx = ofmtCtxAudio;
}
else
{
continue;
}
//convert PTS/DTS
packet.pts = av_rescale_q_rnd(packet.pts, inStream->time_base, outStream->time_base,
(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.dts = av_rescale_q_rnd(packet.dts, inStream->time_base, outStream->time_base,
(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, inStream->time_base, outStream->time_base);
packet.pos = -1;
packet.stream_index = 0;
//write
if (av_interleaved_write_frame(ofmtCtx, &packet) < 0)
{
printf("Error muxing packet\n");
break;
}
av_packet_unref(&packet);
}
#if USE_H264BSF
av_bitstream_filter_close(h264bsfc);
#endif
//write file trailer
av_write_trailer(ofmtCtxVideo);
av_write_trailer(ofmtCtxAudio);
end:
avformat_close_input(&ifmtCtx);
if (ofmtCtxVideo && !(ofmtCtxVideo->oformat->flags & AVFMT_NOFILE))
{
avio_close(ofmtCtxVideo->pb);
}
if (ofmtCtxAudio && !(ofmtCtxAudio->oformat->flags & AVFMT_NOFILE))
{
avio_close(ofmtCtxAudio->pb);
}
avformat_free_context(ofmtCtxVideo);
avformat_free_context(ofmtCtxAudio);
return 0;
}