今天开始撸代码,首先使用FFmpeg的API抽取一个MP4文件的音频数据。
IDE
应该是第一次在Mac上做C/C++开发,纠结过后选择使用CLion 开发。CLion是 JetBrains下专门用来开发C/C++的IDE,已经用习惯了Android studio和IntelliJ IDEA ,所以CLion用起来还是很顺手的。
在新建一个C项目后,需要把FFmpeg的库导入才能正常运行。我们修改项目的CMakeLists.txt文件。
抽取音频AAC数据
其实我们要做的主要就是一个文件的操作,把一个文件打开,从里面拿出它的一部分数据,再把这部分数据放到另一个文件中保存。
定义参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <libavutil/log.h> #include <libavformat/avformat.h>
AVFormatContext *fmt_ctx = NULL; AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *output_fmt = NULL;
AVStream *in_stream = NULL;
AVStream *out_stream = NULL;
AVPacket packet;
int audio_stream_index = -1;
|
1.打开输入文件,提取参数
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
| err_code = avformat_open_input(&fmt_ctx, src_fileName, NULL, NULL);
if (err_code < 0) { av_log(NULL, AV_LOG_ERROR, "cant open file:%s\n", av_err2str(err_code)); return -1; }
if(fmt_ctx->nb_streams<2){ av_log(NULL, AV_LOG_ERROR, "输入文件错误,流不足2条\n"); exit(1); }
in_stream = fmt_ctx->streams[1]; AVCodecParameters *in_codecpar = in_stream->codecpar;
audio_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if(audio_stream_index < 0){ av_log(NULL, AV_LOG_DEBUG, "寻找最好音频流失败,请检查输入文件!\n"); return AVERROR(EINVAL); }
|
2.准备输出文件,输出流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ofmt_ctx = avformat_alloc_context();
output_fmt = av_guess_format(NULL,dst_fileName,NULL); if(!output_fmt){ av_log(NULL, AV_LOG_DEBUG, "根据目标生成输出容器失败!\n"); exit(1); }
ofmt_ctx->oformat = output_fmt;
out_stream = avformat_new_stream(ofmt_ctx, NULL); if(!out_stream){ av_log(NULL, AV_LOG_DEBUG, "创建输出流失败!\n"); exit(1); }
|
3. 数据拷贝
3.1 参数信息
1 2 3 4 5 6
| if((err_code = avcodec_parameters_copy(out_stream->codecpar, in_codecpar)) < 0 ){ av_strerror(err_code, errors, ERROR_STR_SIZE); av_log(NULL, AV_LOG_ERROR,"拷贝编码参数失败!, %d(%s)\n", err_code, errors); }
|
3.2 初始化AVIOContext
1 2 3 4 5 6 7 8 9
| if((err_code = avio_open(&ofmt_ctx->pb, dst_fileName, AVIO_FLAG_WRITE)) < 0) { av_strerror(err_code, errors, 1024); av_log(NULL, AV_LOG_DEBUG, "Could not open file %s, %d(%s)\n", dst_fileName, err_code, errors); exit(1); }
|
3.3 开始拷贝
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 26 27 28 29 30 31 32 33 34 35
|
av_init_packet(&packet); packet.data = NULL; packet.size = 0;
if (avformat_write_header(ofmt_ctx, NULL) < 0) { av_log(NULL, AV_LOG_DEBUG, "Error occurred when opening output file"); exit(1); }
while(av_read_frame(fmt_ctx, &packet) >=0 ){ if(packet.stream_index == audio_stream_index){ packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet.dts = packet.pts; packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); packet.pos = -1; packet.stream_index = 0; av_interleaved_write_frame(ofmt_ctx, &packet); av_packet_unref(&packet); } }
av_write_trailer(ofmt_ctx);
avformat_close_input(&fmt_ctx); avio_close(ofmt_ctx->pb);
|
执行
./MyC /Users/david/Desktop/1080p.mov /Users/david/Desktop/test.aac
抽取视频数据
抽取视频信息并保存在文件中的流程甚至代码和上面抽取音频基本一致。
1 2 3 4 5 6 7
| in_stream = fmt_ctx->streams[1];
video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
|
基本上就是一些参数的改变,所有流程和代码保持不变,就可以把一个音视频文件中的视频数据抽取出来了,mp4、H264等格式随便,就是这么简单。。。