FFmpeg 抽取 音视频

利用ffmpeg 分别抽取音频和视频 , 两者 在处理流程和代码 基本大同小异 。

流程如下: 
    1. 解封装
    2. 初始化 输出环境 
    3. 获取输出流
    3. 封装

   此过程不涉及到解码操作 。 

代码: https://github.com/kongxs/ffmpeg_demos/tree/master/ffmpeg

1.  解封装,并打印视频信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//定义 宏信息 
#define LOGE(FORMAT,...) av_log(NULL, AV_LOG_ERROR, FORMAT, ##__VA_ARGS__);

char *file = "/Users/kongxiangshu/Desktop/1111.mp4";
char *outfile = "/Users/kongxiangshu/Desktop/1111_out.avi";

AVFormatContext *inputContext;

if (avformat_open_input(&inputContext, file, NULL, NULL) < 0) {
LOGE("scan not open file %s ", file);
}
//找到视频和音频流信息
if(avformat_find_stream_info(inputContext, NULL) < 0) {
LOGE("没找到音视频信息");
}

av_dump_format(inputContext, 0, file, 0);
2. 获取我们想要处理的音频/视频流 
1
2
3
4
5
6
7
//AVMEDIA_TYPE_VIDEO  是获取视频流类型
//如果想要获取音频流的话,只需要修改为 AVMEDIA_TYPE_AUDIO
int best_video_stream_index = av_find_best_stream(inputContext,AVMEDIA_TYPE_VIDEO , -1, -1, NULL, 0);

if (best_video_stream_index >= 0) {
LOGE("find best stream index : %d \n" , best_video_stream_index);
}
3. 准备输出环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//输出的上下文
AVFormatContext *outContext = NULL;
//初始化上下文,并guess输出格式
if( avformat_alloc_output_context2(&outContext, NULL, NULL, outfile) < 0) {
LOGE("输出文件上下文失败 ") ;
}

//新建输出流
AVStream *outStream = avformat_new_stream(outContext, NULL) ;

if (!outStream) {
LOGE("can not create out stream ") ;
}

//输出流 编码信息
if(avcodec_parameters_copy(outStream->codecpar, inputContext->streams[best_video_stream_index]->codecpar) < 0) {
LOGE("输出环境编码失败. .. ") ;
}

//打开输出流
if(avio_open(&outContext->pb, outfile, AVIO_FLAG_WRITE) < 0) {
LOGE("输出文件创建失败 .... ");
}
4. 不断的从输入流总读取 frame 并写到输出文件中
*in_stream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//写入头部信息
avformat_write_header(outContext, NULL);
//读取未解码的frame
while (av_read_frame(inputContext,&pkt) >= 0) {

if (pkt.stream_index == best_video_stream_index) {
//时间基转换
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, outStream->time_base,
AV_ROUND_NEAR_INF);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, outStream->time_base,
AV_ROUND_NEAR_INF );

pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, outStream->time_base);

pkt.pos = -1;
pkt.stream_index = 0;
//将包写到输出媒体文件
av_interleaved_write_frame(outContext, &pkt);
//减少引用计数,避免内存泄漏
av_packet_unref(&pkt);
}
}

av_write_trailer(outContext);

5. 至此,所有流程处理完毕,我们虽然涉及到不少的api,但总体还是有迹可循的 。 
只要稍加练习,必能了然于胸