【FFMpeg视频开发与应用基础】六、调用FFMpeg SDK实现视频文件的转封装
2016-06-08 10:12
609 查看
《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK
工程代码地址:FFmpeg_Tutorial
有时候我们可能会面对这样的一种需求,即我们不需要对视频内的音频或视频信号进行什么实际的操作,只是希望能把文件的封装格式进行转换,例如从avi转换为mp4格式或者flv格式等。实际上,转封装不需要对内部的音视频进行解码,只需要根据从输入文件中获取包含的数据流添加到输出文件中,然后将输入文件中的数据包按照规定格式写入到输出文件中去。1、解析命令行参数
如同之前的工程一样,我们使用命令行参数传入输入和输出的文件名。为此,我们定义了如下的结构体和函数来实现传入输入输出文件的过程:typedef struct _IOFiles { const char *inputName; const char *outputName; } IOFiles; static bool hello(int argc, char **argv, IOFiles &io_param) { printf("FFMpeg Remuxing Demo.\nCommand format: %s inputfile outputfile\n", argv[0]); if (argc != 3) { printf("Error: command line error, please re-check.\n"); return false; } io_param.inputName = argv[1]; io_param.outputName = argv[2]; return true; }
在main函数执行时,调用hello函数解析命令行并保存到IOFiles结构中:
int main(int argc, char **argv) { IOFiles io_param; if (!hello(argc, argv, io_param)) { return -1; } //...... }
2、所需要的结构与初始化操作
为了实现视频文件的转封装操作,我们需要以下的结构:AVOutputFormat *ofmt = NULL; AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; AVPacket pkt;
然后所需要的初始化操作有打开输入视频文件、获取其中的流信息和获取输出文件的句柄:
av_register_all(); //按封装格式打开输入视频文件 if ((ret = avformat_open_input(&ifmt_ctx, io_param.inputName, NULL, NULL)) < 0) { printf("Error: Open input file failed.\n"); goto end; } //获取输入视频文件中的流信息 if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { printf("Error: Failed to retrieve input stream information.\n"); goto end; } av_dump_format(ifmt_ctx, 0, io_param.inputName, 0); //按照文件名获取输出文件的句柄 avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, io_param.outputName); if (!ofmt_ctx) { printf("Error: Could not create output context.\n"); goto end; } ofmt = ofmt_ctx->oformat;
3、 向输出文件中添加Stream并打开输出文件
在我们获取到了输入文件中的流信息后,保持输入流中的codec不变,并以其为依据添加到输出文件中:for (unsigned int i = 0; i < ifmt_ctx->nb_streams ; i++) { AVStream *inStream = ifmt_ctx->streams[i]; AVStream *outStream = avformat_new_stream(ofmt_ctx, inStream->codec->codec); if (!outStream) { printf("Error: Could not allocate output stream.\n"); goto end; } ret = avcodec_copy_context(outStream->codec, inStream->codec); outStream->codec->codec_tag = 0; if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) { outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } } av_dump_format(ofmt_ctx, 0, io_param.outputName, 1);
这里调用了函数avcodec_copy_context函数,该函数的声明如下:
int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);
该函数的作用是将src表示的AVCodecContext中的内容拷贝到dest中。
随后,调用avio_open函数打开输出文件:
av_dump_format(ofmt_ctx, 0, io_param.outputName, 1); if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmt_ctx->pb, io_param.outputName, AVIO_FLAG_WRITE); if (ret < 0) { printf("Error: Could not open output file.\n"); goto end; } }
4、写入文件的音视频数据
首先向输出文件中写入文件头:ret = avformat_write_header(ofmt_ctx, NULL); if (ret < 0) { printf("Error: Could not write output file header.\n"); goto end; }
写入文件的视频和音频包数据,其实就是将音频和视频Packets从输入文件中读出来,正确设置pts和dts等时间量之后,再写入到输出文件中去:
while (1) { AVStream *in_stream, *out_stream; ret = av_read_frame(ifmt_ctx, &pkt); if (ret < 0) break; in_stream = ifmt_ctx->streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; /* copy packet */ pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); pkt.pos = -1; ret = av_interleaved_write_frame(ofmt_ctx, &pkt); if (ret < 0) { fprintf(stderr, "Error muxing packet\n"); break; } av_free_packet(&pkt); }
最后要做的就是写入文件尾:
av_write_trailer(ofmt_ctx);
5、 收尾工作
写入输出文件完成后,需要对打开的结构进行关闭或释放等操作。主要有关闭输入输出文件、释放输出文件的句柄等:avformat_close_input(&ifmt_ctx); /* close output */ if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_closep(&ofmt_ctx->pb); avformat_free_context(ofmt_ctx); if (ret < 0 && ret != AVERROR_EOF) { fprintf(stderr, "Error failed to write packet to output file.\n"); return 1; }
相关文章推荐
- 异步FIFO的FPGA实现
- 按xml方式封装通信数据方法
- The processing instruction target matching "[xX][mM][lL]" is not allowed.
- char 与 unsigned char
- glide图片加载库
- Java-集合、Map
- 百度计算广告学沙龙学习笔记 - 内容匹配广告
- ThreadPoolExecutor 分析
- Android项目关联Library
- 开发工具整理
- yii2 widget示例
- iOS的各种学习资源总结
- 常用数据结构及复杂度
- eclipse maven 导出项目依赖的jar包
- 全面解析jQuery $(document).ready()和JavaScript onload事件
- 装修公司网站源码模板电脑、手机端、微信三合一Asp.net
- 黎活明给程序员的忠告
- wsdl和wadl区别
- 2016/01/12 VBA学习8
- Java进阶学习第二十二天——上传与下载