ffmpeg用代码实现自己的muxer






1、概述

本代码实现了一个muxer并加入到ffmpeg的AVOutputFormat 链表中去,使代码能直接调用。实现的意义一是了解ffmpeg代码结构,二是可以自己整自己的视频格式,让别人播放不了。

2、代码

简单看下代码:

[cpp]  view plain  copy
  1. /* 
  2. *本程序主要实现一个自己的muxer并加入到muxer链中去,使其可用,只有视频 
  3. *作者:缪国凯(MK) 
  4. *821486004@qq.com 
  5. *2015-6-2 
  6. */  
  7.   
  8. #include "stdafx.h"  
  9.   
  10. #ifdef __cplusplus  
  11. extern "C"  
  12. {  
  13. #endif  
  14. #include <libavformat/avformat.h>  
  15. #include <libavcodec/avcodec.h>  
  16. #  
  17. #ifdef __cplusplus  
  18. };  
  19. #endif  
  20.   
  21. #pragma comment(lib, "avcodec.lib")  
  22. #pragma comment(lib, "avformat.lib")  
  23. #pragma comment(lib, "avutil.lib")  
  24. //#pragma comment(lib, "avdevice.lib")  
  25. //#pragma comment(lib, "avfilter.lib")  
  26. //#pragma comment(lib, "postproc.lib")  
  27. //#pragma comment(lib, "swresample.lib")  
  28. //#pragma comment(lib, "swscale.lib")  
  29.   
  30. int mk_write_header(struct AVFormatContext *fmt)  
  31. {  
  32.     //这些地方就可以加入自己的格式定义  
  33.     return 0;  
  34. }  
  35.   
  36. int mk_write_packet(struct AVFormatContext *fmt, AVPacket *pkt)  
  37. {  
  38.     avio_write(fmt->pb, pkt->data, pkt->size);//简单的用file协议写文件  
  39.     return 0;  
  40. }  
  41.   
  42. int mk_write_trailer(struct AVFormatContext *fmt)  
  43. {  
  44.     //这些地方就可以加入自己的格式定义  
  45.     return 0;  
  46. }  
  47.   
  48. AVOutputFormat ff_mk_muxer =   
  49. {  
  50.     /*.name         = */"mk",  
  51.     /*.long_name        = */"mk (MK Video Container)",  
  52.     /*.mime_type        = */"mkvideo/x-msvideo",  
  53.     /*.extensions       = */"mk",  
  54.   
  55.     /*.audio_codec      = */AV_CODEC_ID_NONE,  
  56.     /*.video_codec      = */AV_CODEC_ID_RAWVIDEO,//这里先用ffmpeg自带的yuv编码器,以后改成自己的  
  57.     /*.subtitle_codec   = */AV_CODEC_ID_NONE,  
  58.   
  59.     /*.flags        = */AVFMT_NOTIMESTAMPS,  
  60.     /*.codec_tag        = */NULL,  
  61.     /*.priv_class       = */NULL,  
  62.     /*.next             = */NULL,  
  63.     /*.priv_data_size   = */0,    
  64.   
  65.     /*.write_header     = */mk_write_header,  
  66.     /*.write_packet     = */mk_write_packet,  
  67.     /*.write_trailer    = */mk_write_trailer,  
  68. };  
  69.   
  70. void help()  
  71. {  
  72.     printf("**********************************************\n");  
  73.     printf("Usage:\n");  
  74.     printf("    MyMuxer [inputfile] [outputfile.mk]\n");  
  75.     printf("\n");  
  76.     printf("Examples: \n");  
  77.     printf("    MyMuxer a.avi a.mk\n");  
  78.     printf("**********************************************\n");    
  79. }  
  80.   
  81. int _tmain(int argc, _TCHAR* argv[])  
  82. {  
  83.     if(argc < 3 || (!strcmp(argv[1],"--help")))  
  84.     {  
  85.         help();  
  86.         return 0;  
  87.     }  
  88.   
  89.     AVFormatContext *in_fxt = NULL, *out_fxt = NULL;  
  90.     AVStream *out_stream = NULL;  
  91.     int video_index = -1;  
  92.   
  93.     av_register_all();  
  94.     av_register_output_format(&ff_mk_muxer);//把自己的muxer加入链表  
  95.   
  96.     if (avformat_open_input(&in_fxt, argv[1], NULL, NULL) < 0)  
  97.     {  
  98.         printf("can not open the input file context!\n");  
  99.         goto end;  
  100.     }  
  101.     if (avformat_find_stream_info(in_fxt, NULL) < 0)  
  102.     {  
  103.         printf("can not find the stream info!\n");  
  104.         goto end;  
  105.     }  
  106.   
  107.     if(avformat_alloc_output_context2(&out_fxt, NULL, NULL, argv[2]) < 0)  
  108.     {  
  109.         printf("can not alloc output context!\n");  
  110.         goto end;  
  111.     }  
  112.   
  113.     for (int i = 0; i < in_fxt->nb_streams; i++)  
  114.     {  
  115.         if (in_fxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)  
  116.         {  
  117.             //open decoder  
  118.             if(0 > avcodec_open2(in_fxt->streams[i]->codec, avcodec_find_decoder(in_fxt->streams[i]->codec->codec_id), NULL))  
  119.             {  
  120.                 printf("can not find or open decoder!\n");  
  121.                 goto end;  
  122.             }  
  123.             video_index = i;  
  124.             //new stream  
  125.             out_stream = avformat_new_stream(out_fxt, NULL);  
  126.             if (!out_stream)  
  127.             {  
  128.                 printf("can not new stream for output!\n");  
  129.                 goto end;  
  130.             }  
  131.             //set codec context param  
  132.             out_stream->codec->codec = avcodec_find_encoder(out_fxt->oformat->video_codec);  
  133.             out_stream->codec->height = in_fxt->streams[i]->codec->height;  
  134.             out_stream->codec->width = in_fxt->streams[i]->codec->width;  
  135.   
  136.             out_stream->codec->time_base.num = in_fxt->streams[i]->avg_frame_rate.den;  
  137.             out_stream->codec->time_base.den = in_fxt->streams[i]->avg_frame_rate.num;  
  138.   
  139.             out_stream->codec->sample_aspect_ratio = in_fxt->streams[i]->codec->sample_aspect_ratio;           
  140.             out_stream->codec->pix_fmt = in_fxt->streams[i]->codec->pix_fmt;  
  141.             if (!out_stream->codec->codec)  
  142.             {  
  143.                 printf("can not find the encoder!\n");  
  144.                 goto end;  
  145.             }  
  146.             if ((avcodec_open2(out_stream->codec, out_stream->codec->codec, NULL)) < 0)  
  147.             {  
  148.                 printf("can not open the encoder\n");  
  149.                 goto end;  
  150.             }  
  151.             if (out_fxt->oformat->flags & AVFMT_GLOBALHEADER)  
  152.                 out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;  
  153.             break;  
  154.         }  
  155.     }  
  156.   
  157.     if (-1 == video_index)  
  158.     {  
  159.         printf("found no video stream in input file!\n");  
  160.         goto end;  
  161.     }  
  162.   
  163.     if (!(out_fxt->oformat->flags & AVFMT_NOFILE))  
  164.     {  
  165.         if(avio_open(&out_fxt->pb, argv[2], AVIO_FLAG_WRITE) < 0)  
  166.         {  
  167.             printf("can not open output file handle!\n");  
  168.             goto end;  
  169.         }  
  170.     }  
  171.   
  172.     if(avformat_write_header(out_fxt, NULL) < 0)  
  173.     {  
  174.         printf("can not write the header of the output file!\n");  
  175.         goto end;  
  176.     }  
  177.   
  178.     AVPacket pkt_in, pkt_out;  
  179.     AVFrame *frame;  
  180.     frame = av_frame_alloc();  
  181.     av_init_packet(&pkt_in);  
  182.     av_init_packet(&pkt_out);  
  183.     int got_frame, got_picture;  
  184.     int i = 0, frame_index = 0;  
  185.     while(1)  
  186.     {  
  187.         got_frame = -1;  
  188.         got_picture = -1;  
  189.         if (av_read_frame(in_fxt, &pkt_in) < 0)  
  190.         {  
  191.             break;  
  192.         }  
  193.         if (avcodec_decode_video2(in_fxt->streams[video_index]->codec, frame, &got_frame, &pkt_in) < 0)  
  194.         {  
  195.             printf("can not decoder a frame");  
  196.             break;  
  197.         }  
  198.         av_free_packet(&pkt_in);  
  199.   
  200.         if (got_frame)  
  201.         {  
  202.             frame->pts = i++;  
  203.             pkt_out.data = NULL;//主要这里必须自己初始化,或者必须置为null,不然ff_alloc_packet2函数会报错  
  204.             pkt_out.size = 0;  
  205.             if (avcodec_encode_video2(out_stream->codec, &pkt_out, frame, &got_picture) < 0)  
  206.             {  
  207.                 printf("can not encode a frame!\n");  
  208.                 break;  
  209.             }  
  210.   
  211.             if (got_picture)  
  212.             {  
  213.                 printf("Succeed to encode frame: %5d\tsize:%5d\n",frame_index,pkt_out.size);  
  214.                 pkt_out.stream_index = out_stream->index;  
  215.                 frame_index++;  
  216.                 av_write_frame(out_fxt, &pkt_out);  
  217.                 av_free_packet(&pkt_out);  
  218.             }  
  219.         }  
  220.     }  
  221.     av_frame_free(&frame);  
  222.   
  223.     av_write_trailer(out_fxt);  
  224.   
  225.     //clean  
  226.     avcodec_close(out_stream->codec);  
  227.     avcodec_close(out_fxt->streams[video_index]->codec);  
  228. end:  
  229.     avformat_close_input(&in_fxt);  
  230.   
  231.     if (out_fxt && !(out_fxt->oformat->flags & AVFMT_NOFILE))  
  232.     {  
  233.         avio_close(out_fxt->pb);  
  234.     }  
  235.     avformat_free_context(out_fxt);  
  236.   
  237.     return 0;  
  238. }  


3、解释

简单说下基本原理。

首先自己实现一个AVOutputFormat,然后把这个muxer加入到链表中去,其他的根本不用自己管了,ffmpeg这套框架做的的确很漂亮,会一层层的掉用到你的muxer来,函数avformat_alloc_output_context2会根据输入的格式或后缀来在AVOutputFormat链中查找最适合的muxer,并把这个muxer的指针用oformat保存,下面看看格式匹配的代码:

[cpp]  view plain  copy
  1. AVOutputFormat *av_guess_format(const char *short_name, const char *filename,  
  2.                                 const char *mime_type)  
  3. {  
  4.     AVOutputFormat *fmt = NULL, *fmt_found;  
  5.     int score_max, score;  
  6.   
  7.     /* specific test for image sequences */  
  8. #if CONFIG_IMAGE2_MUXER  
  9.     if (!short_name && filename &&  
  10.         av_filename_number_test(filename) &&  
  11.         ff_guess_image2_codec(filename) != AV_CODEC_ID_NONE) {  
  12.         return av_guess_format("image2", NULL, NULL);  
  13.     }  
  14. #endif  
  15.     /* Find the proper file type. */  
  16.     fmt_found = NULL;  
  17.     score_max = 0;  
  18.     while ((fmt = av_oformat_next(fmt))) {  
  19.         score = 0;  
  20.         if (fmt->name && short_name && av_match_name(short_name, fmt->name))  
  21.             score += 100;  
  22.         if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))  
  23.             score += 10;  
  24.         if (filename && fmt->extensions &&  
  25.             av_match_ext(filename, fmt->extensions)) {  
  26.             score += 5;  
  27.         }  
  28.         if (score > score_max) {  
  29.             score_max = score;  
  30.             fmt_found = fmt;  
  31.         }  
  32.     }  
  33.     return fmt_found;  
  34. }  
遍历整个链表,得分最高的就是最匹配的格式,name和传入的shotname(就是传入的格式)匹配得分100,mime_type匹配得分10,extensions和传入的文件名后缀匹配得分5,是不是很有意思。

再来看看avformat_write_header的代码:

[cpp]  view plain  copy
  1. int avformat_write_header(AVFormatContext *s, AVDictionary **options)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     if (ret = init_muxer(s, options))  
  6.         return ret;  
  7.   
  8.     if (s->oformat->write_header) {  
  9.         ret = s->oformat->write_header(s);  
  10.         if (ret >= 0 && s->pb && s->pb->error < 0)  
  11.             ret = s->pb->error;  
  12.         if (ret < 0)  
  13.             return ret;  
  14.         if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)  
  15.             avio_flush(s->pb);  
  16.     }  
  17.   
  18.     if ((ret = init_pts(s)) < 0)  
  19.         return ret;  
  20.   
  21.     if (s->avoid_negative_ts < 0) {  
  22.         av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);  
  23.         if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {  
  24.             s->avoid_negative_ts = 0;  
  25.         } else  
  26.             s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;  
  27.     }  
  28.   
  29.     return 0;  
  30. }  
会调用oformat的 write_header。

再看 av_write_frame 的代码:

[cpp]  view plain  copy
  1. int av_write_frame(AVFormatContext *s, AVPacket *pkt)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = check_packet(s, pkt);  
  6.     if (ret < 0)  
  7.         return ret;  
  8.   
  9.     if (!pkt) {  
  10.         if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {  
  11.             ret = s->oformat->write_packet(s, NULL);  
  12.             if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)  
  13.                 avio_flush(s->pb);  
  14.             if (ret >= 0 && s->pb && s->pb->error < 0)  
  15.                 ret = s->pb->error;  
  16.             return ret;  
  17.         }  
  18.         return 1;  
  19.     }  
  20.   
  21.     ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt);  
  22.   
  23.     if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))  
  24.         return ret;  
  25.   
  26.     ret = write_packet(s, pkt);  
  27.     if (ret >= 0 && s->pb && s->pb->error < 0)  
  28.         ret = s->pb->error;  
  29.   
  30.     if (ret >= 0)  
  31.         s->streams[pkt->stream_index]->nb_frames++;  
  32.     return ret;  
  33. }  

会调用oformat的 write_packet。

最后看看av_write_trailer的代码:

[cpp]  view plain  copy
  1. int av_write_trailer(AVFormatContext *s)  
  2. {  
  3.     int ret, i;  
  4.   
  5.     for (;; ) {  
  6.         AVPacket pkt;  
  7.         ret = interleave_packet(s, &pkt, NULL, 1);  
  8.         if (ret < 0)  
  9.             goto fail;  
  10.         if (!ret)  
  11.             break;  
  12.   
  13.         ret = write_packet(s, &pkt);  
  14.         if (ret >= 0)  
  15.             s->streams[pkt.stream_index]->nb_frames++;  
  16.   
  17.         av_free_packet(&pkt);  
  18.   
  19.         if (ret < 0)  
  20.             goto fail;  
  21.         if(s->pb && s->pb->error)  
  22.             goto fail;  
  23.     }  
  24.   
  25. fail:  
  26.     if (s->oformat->write_trailer)  
  27.         if (ret >= 0) {  
  28.         ret = s->oformat->write_trailer(s);  
  29.         } else {  
  30.             s->oformat->write_trailer(s);  
  31.         }  
  32.   
  33.     if (s->pb)  
  34.        avio_flush(s->pb);  
  35.     if (ret == 0)  
  36.        ret = s->pb ? s->pb->error : 0;  
  37.     for (i = 0; i < s->nb_streams; i++) {  
  38.         av_freep(&s->streams[i]->priv_data);  
  39.         av_freep(&s->streams[i]->index_entries);  
  40.     }  
  41.     if (s->oformat->priv_class)  
  42.         av_opt_free(s->priv_data);  
  43.     av_freep(&s->priv_data);  
  44.     return ret;  
  45. }  

会调用oformat的 write_trailer。


所以说,只要你按照ffmpeg的格式去定义muxer并加入链表,其他的都不用管了。

 最后验证一下你的结果,运行程序随便转一个结果出来,把后缀.mk改为.yuv,用yuv播放器放一下,ok了,大功告成。下一篇介绍如何把这个muxer加入ffmpeg源码,使ffmpeg命令行可以用这个muxer。









链接:http://blog.csdn.net/dancing_night/article/details/46328837







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值