利用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,但总体还是有迹可循的 。
只要稍加练习,必能了然于胸