最简单的基于FFMPEG的转码程序 —— 分析

模块:

    libavcodec    - 编码解码器
        libavdevice   - 输入输出设备的支持
        libavfilter   - 视音频滤镜支持
        libavformat   - 视音频等格式的解析
        libavutil     - 工具库
        libpostproc   - 后期效果处理
        libswscale    - 图像颜色、尺寸转换

1. 主函数分析:

大致流程:

    调用注册函数;

   open_input_file打开输入文件;

   open_output_file打开输出文件;

   init_filters()  过滤器初始化;

   完成  avformatcontext----avpacket----avstream----avcodeccontext----avcodec  解析包逐步得到编解码类型    ,其中av_read_frame读取数据到packet;

    读取的包处理:

      (1)若含有过滤器:

             时间戳设置,调用avcodec_decode_video2 或  avcodec_decode_audio4解码;

             解码成功后,设置时间戳,调用filter_encode_write_frame编码

      (2) 不含过滤器:  修改时基,将packet直接放入输出文件

 更新过滤器filter_encode_write_frame(NULL, i);   更新编码器flush_encoder(i)

1.1 代码详解

  1. int_tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     int ret;  
  4.     AVPacketpacket;  
  5.     AVFrame *frame= NULL;  
  6.     enum AVMediaType type;  
  7.     unsigned intstream_index;  
  8.     unsigned int i;  
  9.     int got_frame;  
  10.     int (*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket*);    //函数指针
  11.     if (argc != 3) {  
  12.        av_log(NULL, AV_LOG_ERROR, "Usage: %s<input file> <output file>\n", argv[0]);     //用法说明
  13.         return 1;  
  14.     }  
  15.    av_register_all();                //注册函数
  16.    avfilter_register_all();  
  17.     if ((ret = open_input_file(argv[1])) < 0)  
  18.         goto end;  
  19.     if ((ret = open_output_file(argv[2])) < 0)  
  20.         goto end;  
  21.     if ((ret = init_filters()) < 0)  
  22.         goto end;                                     //至此,都是输入参数检查,  end:
  23.     /* read all packets */  
  24.     while (1) {  
  25. /*      avformatcontext----avpacket----avstream----avcodeccontext----avcodec  解析包逐步得到编解码类型                */
  26.         if ((ret= av_read_frame(ifmt_ctx, &packet)) < 0)   /*int av_read_frame(AVFormatContext *s, AVPacket *pkt); 读取码流中的音频若干帧或者视频一帧,s 是输入的AVFormatContext,pkt是输出的AVPacket。这里ifmt_ctx是一个AVFormatContext  */
  27.             break;  
  28.        stream_index = packet.stream_index;  // int   stream_index:标识该AVPacket所属的视频/音频流
  29.         type =ifmt_ctx->streams[packet.stream_index]->codec->codec_type;  /* AVFormatContextAVStream **streams:视音频流;  AVStream: AVCodecContext *codec:指向该视频/音频流的AVCodecContext;       AVCodecContext:   enum AVMediaType codec_type:编解码器的类型(视频,音频...)*/
  30.        av_log(NULL, AV_LOG_DEBUG, "Demuxergave frame of stream_index %u\n",  
  31.                stream_index);  
  32.         if (filter_ctx[stream_index].filter_graph) {                         //  static FilteringContext *filter_ctx  完整的过滤器结构体,包含AVFilterGraph  *filter_graph过滤器图
  33.            av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n");  
  34.             frame =av_frame_alloc();        //   av_frame_alloc()  初始化 AVFrame
  35.             if (!frame) {  
  36.                 ret = AVERROR(ENOMEM);  
  37.                 break;  
  38.             }  
  39.          /* int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd);是计算 "a * b / c" 的值并分五种方式来取整.

                      用在FFmpeg中, 则是将以 "时钟基c" 表示的 数值a 转换成以 "时钟基b" 来表示。 */

  40.            packet.dts = av_rescale_q_rnd(packet.dts,      //   AVPacketnt64_t  dts解码时间戳
  41.                    ifmt_ctx->streams[stream_index]->time_base,  
  42.                    ifmt_ctx->streams[stream_index]->codec->time_base,  
  43.                     (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  44.            packet.pts = av_rescale_q_rnd(packet.pts,    //AVPacketint64_t pts显示时间戳
  45.                    ifmt_ctx->streams[stream_index]->time_base,  
  46.                    ifmt_ctx->streams[stream_index]->codec->time_base,  
  47.                    (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  48.            dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :  
  49.                avcodec_decode_audio4; //avcodec_decodec_video2()视频解码函数,avcodec_decode_audio4 音频解码函数
  50.           /*   int avcodec_decode_video2( AVCodecContext* avctx, AVFrame* picture,int* got_pitcure_ptr,const AVPacket* avpkt)
  51.              avctx:解码器    picture:保存输出的视频帧  got_picture_ptr: 0表示没有可以解码的, avpkt: 包含输入buffer的AVPacket
  52.   */
  53.            ret =dec_func(ifmt_ctx->streams[stream_index]->codec, frame,  
  54.                    &got_frame, &packet);  
  55.             if (ret < 0) {                             解码失败
  56.                av_frame_free(&frame);  
  57.                av_log(NULL, AV_LOG_ERROR, "Decodingfailed\n");  
  58.                 break;  
  59.             }  
  60.             if (got_frame) {                  //解码成功
  61.                //
    int64_t av_frame_get_best_effort_timestamp ( const AVFrame frame )  
  62.                   
  63.                 frame->pts = av_frame_get_best_effort_timestamp(frame);  
  64.                 ret= filter_encode_write_frame(frame, stream_index);        //filter_encode_write_frame():编码一个AVFrame
  65.                av_frame_free(&frame);  
  66.                 if (ret< 0)  
  67.                    goto end;  
  68.             } else {  
  69.                av_frame_free(&frame);  
  70.             }  
  71.         } else {  
  72.            /*     remux this frame without reencoding   重新复用frame,没有重新编码的情况下    *
  73.            packet.dts = av_rescale_q_rnd(packet.dts,  
  74.                    ifmt_ctx->streams[stream_index]->time_base,  
  75.                    ofmt_ctx->streams[stream_index]->time_base,  
  76.                     (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  77.            packet.pts = av_rescale_q_rnd(packet.pts,  
  78.                    ifmt_ctx->streams[stream_index]->time_base,  
  79.                    ofmt_ctx->streams[stream_index]->time_base,  
  80.                     (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
  81.             ret =av_interleaved_write_frame(ofmt_ctx, &packet);  //av_interleaved_write_frame写入一个AVPacket到输出文件
  82.             if (ret < 0)  
  83.                 goto end;  
  84.         }  
  85.        av_free_packet(&packet);  
  86.     }  
  87.    /* flush filters and encoders 刷新过滤器和编码器 
  88.     for (i = 0; i < ifmt_ctx->nb_streams; i++) {  
  89.        /*刷新过滤器flush filter   
  90.         if (!filter_ctx[i].filter_graph)  
  91.             continue;  
  92.         ret =filter_encode_write_frame(NULL, i);  
  93.         if (ret < 0) {  
  94.            av_log(NULL, AV_LOG_ERROR, "Flushingfilter failed\n");  
  95.             goto end;  
  96.         }  
  97.         /* 刷新编码器 flush  encoder  flush_encoder():输入文件读取完毕后,输出编码器中剩余的AVPacket
  98.         ret = flush_encoder(i);  
  99.         if (ret < 0) {  
  100.            av_log(NULL, AV_LOG_ERROR, "Flushingencoder failed\n");  
  101.             goto end;  
  102.         }  
  103.     }  
  104.    av_write_trailer(ofmt_ctx);        //输出文件尾
  105. end:  
  106.  /* 释放个个结构体   packet,frame----codec----filter----ifmt_ctx,ofmt_ctx    */
  107.    av_free_packet(&packet);         //av_free_packet,   释放AVPacket对象
  108.    av_frame_free(&frame);           //av_frame_free,   释放AVFrame对象
  109.     for (i = 0; i < ifmt_ctx->nb_streams; i++) {  
  110.        avcodec_close(ifmt_ctx->streams[i]->codec);  
  111.         if (ofmt_ctx && ofmt_ctx->nb_streams >i && ofmt_ctx->streams[i] &&ofmt_ctx->streams[i]->codec)  
  112.            avcodec_close(ofmt_ctx->streams[i]->codec);  
  113.         if(filter_ctx && filter_ctx[i].filter_graph)  
  114.            avfilter_graph_free(&filter_ctx[i].filter_graph);  
  115.     }  
  116.    av_free(filter_ctx);  
  117.    avformat_close_input(&ifmt_ctx);  
  118.     if (ofmt_ctx &&!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))  
  119.         avio_close(ofmt_ctx->pb);  
  120.    avformat_free_context(ofmt_ctx);  
  121.     if (ret < 0)  
  122.        av_log(NULL, AV_LOG_ERROR, "Erroroccurred\n");  
  123.     return (ret? 1:0);  

1.2   int _tmain(int argc, _TCHAR* argv[])

   用过C的人都知道每一个C的程序都会有一个main(),但有时看别人写的程序发现主函数不是int main(),而是int _tmain(),而且头文件也不是<iostream.h>而是<stdafx.h>,会困惑吧?首先,这个_tmain() 是为了支持unicode所使用的main一个别名而已,既然是别名,应该有宏定义过的,在哪里定义的呢?就在那个让你困惑 的<stdafx.h>里,有这么两行
  #include <stdio.h>
  #include <tchar.h>
我们可以在头文件<tchar.h>里找到_tmain的宏定义
  #define _tmain main
所以,经过预编译以后, _tmain就变成main了

//_TCHAR类型是宽字符型字符串,和我们一般常用的字符串不同,它是32位或者更 高的操作系统中所使用的类型.

 

2. 头部

(1). extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编 译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。 这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库, 需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

  这个功能主要用在下面的情况:

    1)、C++代码调用C语言代码

    2)、在C++的头文件中使用

    3)、在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++,这样的情况下也会有用到

(2).   基本的过滤器使用流程是:

      解码后的画面--->buffer过滤器---->其他过滤器---->buffersink过滤器--->处理完的画面

所有的过滤器形成了过滤器链,一定要的两个过滤器是buffer过滤器和buffersink过滤器,前者的作用是将解码后的画面加载到过滤器链中,后者的作用是将处理好的画面从过滤器链中读取出来.

     过滤器相关的结构体:

      AVFilterGraph: 管理所有的过滤器图像

      AVFilterContext: 过滤器上下文

      AVFilter: 过滤器

    1. #include "stdafx.h" 
    2. extern "C"  
    3. {  
    4. #include "libavcodec/avcodec.h"  
    5. #include "libavformat/avformat.h"  
    6. #include "libavfilter/avfiltergraph.h"  
    7. #include "libavfilter/avcodec.h"  
    8. #include "libavfilter/buffersink.h"  
    9. #include "libavfilter/buffersrc.h"  
    10. #include "libavutil/avutil.h"  
    11. #include "libavutil/opt.h"  
    12. #include "libavutil/pixdesc.h"  
    13. };  
    14. /* 三个重要的结构体:  AVFormatContext *ifmt_ctx  ,AVFormatContext *ofmt_ctx , FilteringContext *filter_ctx
    15. */

    16. static AVFormatContext *ifmt_ctx;  
    17. static AVFormatContext *ofmt_ctx;  
    18. typedef struct FilteringContext{  
    19.     AVFilterContext*buffersink_ctx;  
    20.     AVFilterContext*buffersrc_ctx;  
    21.     AVFilterGraph*filter_graph;  
    22. } FilteringContext;  
    23. static FilteringContext *filter_ctx; 

 

3. static int open_input_file(const char *filename)函数———打开输入文件,获取流信息,打开解码器,打印输入输出信息

(该函数在ffmpeg文件 transcoding.c)

说明:open_input_file():打开输入文件,并初始化相关的结构体,

(1).打开媒体的的过程开始于avformat_open_input函数

  int avformat_open_input(AVFormatContext** ps, const char* filename,AVInputFormat* fmt,AVDictionary** options):

作用: 打开输入流,读取头部;   codecs没有打开,最后关闭流需要使用avformat_close_input();  正确执行返回0

参数:  ps: 指向用户提供的AVFormatContext(由avformat_alloc_context分配);可以指向NULL(当AVFormatContext由该函数分配,并写入到ps中)

     filename:   打开输入流的名字      fmt:如果非null,即特定的输入形式,否则自动检测格式      option:其他

 

(2). 获取相关信息 avformat_find_stream_info

  int avformat_find_stream_info(AVFormatContext* ic, AVDictionary** options)

作用:读取媒体文件的包,得到流信息

参数: ic:处理的媒体文件,即输入的 AVFormatContext ,  option 可选项;   正常执行后返回值大于等于0。

 

(3).打开解码器avcodec_open2

     int avcodec_open2(AVCodecContext* avctx, const AVCodec * codec, AVDictionary** options),正确执行返回0

作用:初始化AVCodecContext使得使用给定的编解码器AVCodec

说明:(1). 使用函数avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder()ti提供编解码器  ;   (2).该函数不是线程安全的??? ; (3).通常在解码程序之前调用,比如avcodec_decode_video2()

 

(4).av_dump_format

  void  av_dump_format (AVFormatContext* ic,   int index, const char* url,  int is_output)

作用:打印输入输出格式的详细信息,比如持续时间duration,比特率 bitrate,流 streams,容器 container, 程序programs,元数据 metadata, side data, 编解码   器codec ,时基 time base.

参数: ic:要分析的context,    index:流索引,    url:打印的URL,比如源文件或者目标文件, is_output:选择指定的context是input(0) or output(1)

 

  1. static int open_input_file(const char *filename)  
  2. {  
  3.     int ret;  
  4.     unsigned int i;  
  5.     ifmt_ctx =NULL;  
  6. /*   avformat_open_input 打开输入流    失败报错 */
  7.     if ((ret = avformat_open_input(&ifmt_ctx,filename, NULL, NULL)) < 0) {  
  8.        av_log(NULL, AV_LOG_ERROR, "Cannot openinput file\n");  
  9.         return ret;  
  10.     }  
  11. /*   avformat_open_input 获取流信息   失败报错 */
  12.     if ((ret = avformat_find_stream_info(ifmt_ctx, NULL))< 0) {  
  13.        av_log(NULL, AV_LOG_ERROR, "Cannot findstream information\n");  
  14.         return ret;  
  15.     }  
  16.     for (i = 0; i < ifmt_ctx->nb_streams; i++) {  
  17.         AVStream*stream;  
  18.        AVCodecContext *codec_ctx;  
  19.         /*   AVFormatContext : ifmt_ctx--- AVStream: stream---AVCodecContext: codec_ctx    */
  20.         stream =ifmt_ctx->streams[i];  
  21.         codec_ctx =stream->codec;  
  22.         /*Reencode video & audio and remux subtitles(字幕) etc   */
  23.         if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO  
  24.                 ||codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {  
  25.              /* 打开解码器 open  decoder   */
  26.             ret =avcodec_open2(codec_ctx,  
  27.                    avcodec_find_decoder(codec_ctx->codec_id), NULL);  
  28.             if (ret < 0) {  
  29.                av_log(NULL, AV_LOG_ERROR, "Failed toopen decoder for stream #%u\n", i);  
  30.                 return ret;  
  31.             }  
  32.         }  
  33.     }  
  34.    av_dump_format(ifmt_ctx, 0, filename, 0);     //打印数据信息
  35.     return 0;  

 

4.satic int open_output_file(const char *filename)———打开输出文件;输入输出的AVStream,AVFormatContex;视频音频分别设置输出数据,其他的直接拷贝设置; 设置编码器;  打印信息到目标文件 ; 写入头文件

(该函数即ffmpeg文件中 transcoding.c)

(1).avformat_alloc_output_context2

  int  avformat_alloc_output_context2(AVFormatContext** ctx,AVOutputFormat* oformat, const char* format_name, const char* filename)

作用:分配一个用于输出格式的AVFormatContext,可以使用avformat_free_context() 释放;  正确返回大于等于0

参数:ctx:函数成功后创建的AVFormatContext结构体;

   oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由          FFmpeg猜测输出格式。使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。

   format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。

     filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。

(2).avformat_new_stream

  AVStream* avformat_new_stream(AVFormatContext* s,  const AVCodec*  c)

作用:在给定的context中增加一个新的流,

 

(3).avcodec_copy_context

  int  avcodec_copy_context(AVCodecContext*  dest,   const AVCodecContext* src)

 作用:拷贝源AVCodecContext的设置到目的AVCodecContext

 

(4).avformat_write_header

  int  avformat_write_header(AVFormatContext *  s, AVDictionary** options) 

作用:分配流的私有数据,并将流的头部写入到输出文件;   正确返回0

 

  1. static int open_output_file(const char *filename)  
  2. {  
  3.     AVStream*out_stream;  
  4.     AVStream*in_stream;  
  5.     AVCodecContext*dec_ctx, *enc_ctx;  
  6.     AVCodec*encoder;  
  7.     int ret;  
  8.     unsigned int i;  
  9.     ofmt_ctx =NULL;  
  10.    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);           //创建输出 AVFormatContext格式的ofmt_ctx,
  11.     if (!ofmt_ctx) {  
  12.        av_log(NULL, AV_LOG_ERROR, "Could notcreate output context\n");  
  13.         return AVERROR_UNKNOWN;  
  14.     }  
  15.     for (i = 0; i < ifmt_ctx->nb_streams; i++) {  
  16.         out_stream= avformat_new_stream(ofmt_ctx, NULL);                   //创建一条流
  17.         if (!out_stream) {  
  18.            av_log(NULL, AV_LOG_ERROR, "Failedallocating output stream\n");  
  19.             return AVERROR_UNKNOWN;  
  20.         }  
  21.         in_stream =ifmt_ctx->streams[i];           
  22.         dec_ctx =in_stream->codec;                  //dec_ctx:  输入AVFormatContext中的AVCodecContext
  23.         enc_ctx =out_stream->codec;               //enc_ctx:  输出AVFormatContext中的AVCodecContext
  24.         if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO  
  25.                 ||dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {  
  26.             /*in this example, we choose transcoding to same codec    这里使用相同的编解码器    */
  27.             encoder= avcodec_find_encoder(dec_ctx->codec_id);  
  28.             /* In this example, we transcode to same properties(picture size, 
  29.             * sample rate etc.). These properties can be changed for output 
  30.             * streams easily using filters */  
  31.             if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {       //视频:设置输出的高,宽,纵横比,pix_fms ,时基
  32.                enc_ctx->height = dec_ctx->height;                
  33.                enc_ctx->width = dec_ctx->width;  
  34.                enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;  
  35.                 /* take first format from list of supported formats */  
  36.                enc_ctx->pix_fmt = encoder->pix_fmts[0];  
  37.                 /* video time_base can be set to whatever is handy andsupported by encoder */  
  38.                enc_ctx->time_base = dec_ctx->time_base;  
  39.             } else {                                                                   //音频:设置输出的采样率,声道,声道数,,sample_fms...
  40.                enc_ctx->sample_rate = dec_ctx->sample_rate;  
  41.                enc_ctx->channel_layout = dec_ctx->channel_layout;  
  42.                enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);  
  43.                 /* take first format from list of supported formats */  
  44.                enc_ctx->sample_fmt = encoder->sample_fmts[0];  
  45.                 AVRationaltime_base={1, enc_ctx->sample_rate}; // AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)
  46.                enc_ctx->time_base = time_base;  
  47.             }  
  48.             /* Third parameter can be used to pass settings to encoder*/  
  49.             ret =avcodec_open2(enc_ctx, encoder, NULL);          // 为enc_ctx(输出AVFormatContext中的AVCodecContext)   初始化编码器
  50.             if (ret < 0) {  
  51.                av_log(NULL, AV_LOG_ERROR, "Cannot openvideo encoder for stream #%u\n", i);  
  52.                 return ret;  
  53.             }  
  54.         } else if(dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {  
  55.            av_log(NULL, AV_LOG_FATAL, "Elementarystream #%d is of unknown type, cannot proceed\n", i);  
  56.             return AVERROR_INVALIDDATA;  
  57.         } else {  
  58.            /*假设该流一定要 if this stream must be remuxed   */ 
  59.             ret =avcodec_copy_context(ofmt_ctx->streams[i]->codec,      //拷贝设置,即复用??
  60.                    ifmt_ctx->streams[i]->codec);            
  61.             if (ret < 0) {  
  62.                av_log(NULL, AV_LOG_ERROR, "Copyingstream context failed\n");  
  63.                 return ret;  
  64.             }  
  65.         }  
  66.         if (ofmt_ctx->oformat->flags &AVFMT_GLOBALHEADER)  
  67.            enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;  
  68.     }  
  69.    av_dump_format(ofmt_ctx, 0, filename, 1);     //打印ofmt_ctx的信息到文件filename
  70.     if (!(ofmt_ctx->oformat->flags &AVFMT_NOFILE)) {  
  71.         ret =avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);  
  72.         if (ret < 0) {  
  73.            av_log(NULL, AV_LOG_ERROR, "Could notopen output file '%s'", filename);  
  74.             return ret;  
  75.         }  
  76.     }  
  77.     /* init muxer, write output file header */  
  78.     ret =avformat_write_header(ofmt_ctx, NULL);       //写入头文件  
  79.     if (ret < 0) {  
  80.         av_log(NULL,AV_LOG_ERROR, "Error occurred when openingoutput file\n");  
  81.         return ret;  
  82.     }  
  83.     return 0;  

 

 

5.intinit_filter函数———— 初始化结构体; 视频音频分别  获取buffer过滤器,buffersink过滤器,并且创建过滤器,存储视频音频信息;  创建过滤器端; 连接参数传递的过滤器到过滤器图中,配置过滤器图的链路,  最后生成FilteringContext

(该函数即ffmpeg文件中 transcoding.c)

     解码后的画面--->buffer过滤器---->其他过滤器---->buffersink过滤器--->处理完的画面

1).所有的过滤器形成了过滤器链,一定要的两个过滤器是buffer过滤器和buffersink过滤器,前者的作用是将解码后的画面加载到过滤器链中,后者  的作用是将处理好的画面从过滤器链中读取出来.

 

2). 过滤器相关的结构体:   AVFilterGraph: 管理所有的过滤器图像;   AVFilterContext: 过滤器上下文;    AVFilter过滤器;

3).本程序中的定义:

    typedef struct FilteringContext {
        AVFilterContext *buffersink_ctx;
        AVFilterContext *buffersrc_ctx;
        AVFilterGraph *filter_graph;
    } FilteringContext;
    static FilteringContext *filter_ctx;

          AVFilterInOut

4). AVFilterInOut 结构体

作用:过滤器链的输入输出链

This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(), where it is used to communicate open (unlinked) inputs and outputs from and to the caller. This struct specifies, per each not connected pad contained in the graph, the filter context and the pad index required for establishing a link.

 

 

 

 

(1).avfilter_inout_alloc()——分配一个AVFilterInOut; 使用 avfilter_inout_free() 释放

 

 

 

 

 

(2).avfilter_graph_alloc——分配一个AVFilterGraph

 

(3).avfilter_get_by_name——获取过滤器

 

(4).snprintf(),

    为函数原型int snprintf(char *str, size_t size, const char *format, ...),将可变个参数(...)按照format格式化成字符串,然后将其复制到str中

  1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
  2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0'),返回值为格式化后的 字符串的长度。
    char a[20];
    i = snprintf(a, 9, "%012d", 12345);
    printf("i = %d, a = %s", i, a);
  输出为:i = 12, a = 00000001
 

(5).avfilter_graph_create_filter——创建过滤器并且添加到存在的AVFilterGraph

(6). av_opt_set_bin   ——用于设置参数

你可能感兴趣的:(ffmpeg)