ffmpeg源码简析(四)avcodec_find_encoder(),avcodec_open2(),avcodec_close()

来源:互联网 发布:linux路由器搭建局域网 编辑:程序博客网 时间:2024/05/17 12:46

avcodec_find_encoder()和avcodec_find_decoder()

avcodec_find_encoder()用于查找FFmpeg的编码器,avcodec_find_decoder()用于查找FFmpeg的解码器。
avcodec_find_encoder()的声明位于libavcodec\avcodec.h

avcodec_find_encoder()的源代码位于libavcodec\utils.c

    AVCodec *avcodec_find_encoder(enum AVCodecID id)      {          return find_encdec(id, 1);      }  

find_encdec()
find_encdec()的源代码位于libavcodec\utils.c,如下所示。

static AVCodec *first_avcodec;  static AVCodec *find_encdec(enum AVCodecID id, int encoder)  {      AVCodec *p, *experimental = NULL;      p = first_avcodec;      id= remap_deprecated_codec_id(id);      while (p) {          if ((encoder ? av_codec_is_encoder(p) : av_codec_is_decoder(p)) &&              p->id == id) {              if (p->capabilities & CODEC_CAP_EXPERIMENTAL && !experimental) {                  experimental = p;              } else                  return p;          }          p = p->next;      }      return experimental;  }

find_encdec()中有一个循环,该循环会遍历AVCodec结构的链表,逐一比较输入的ID和每一个编码器的ID,直到找到ID取值相等的编码器。
在这里有几点需要注意:
(1)first_avcodec是一个全局变量,存储AVCodec链表的第一个元素。
(2)remap_deprecated_codec_id()用于将一些过时的编码器ID映射到新的编码器ID。
(3)函数的第二个参数encoder用于确定查找编码器还是解码器。当该值为1的时候,用于查找编码器,此时会调用av_codec_is_encoder()判断AVCodec是否为编码器;当该值为0的时候,用于查找解码器,此时会调用av_codec_is_decoder()判断AVCodec是否为解码器。

av_codec_is_encoder()
av_codec_is_encoder()是一个判断AVCodec是否为编码器的函数。如果是编码器,返回非0值,否则返回0。

    int av_codec_is_encoder(const AVCodec *codec)      {          return codec && (codec->encode_sub || codec->encode2);      }  

从源代码可以看出,av_codec_is_encoder()判断了一下AVCodec是否包含了encode2()或者encode_sub()接口函数。

avcodec_open2()

该函数用于初始化一个视音频编解码器的AVCodecContext。avcodec_open2()的声明位于libavcodec\avcodec.h

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 

avctx:需要初始化的AVCodecContext。
codec:输入的AVCodec
options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置。

avcodec_open2()
avcodec_open2()的定义位于libavcodec\utils.c

avcodec_open2()的源代码量是非常长的,但是它的调用关系非常简单——它只调用了一个关键的函数,即AVCodec的init(),后文将会对这个函数进行分析。
我们可以简单梳理一下avcodec_open2()所做的工作,如下所列:

(1)为各种结构体分配内存(通过各种av_malloc()实现)。(2)将输入的AVDictionary形式的选项设置到AVCodecContext。(3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。(4)如果是编码器,检查输入参数是否符合编码器的要求(5)调用AVCodec的init()初始化具体的解码器。

AVCodec->init()
avcodec_open2()中最关键的一步就是调用AVCodec的init()方法初始化具体的编码器。AVCodec的init()是一个函数指针,指向具体编解码器中的初始化函数。

avcodec_close()

该函数用于关闭编码器。avcodec_close()函数的声明位于libavcodec\avcodec.h

int avcodec_close(AVCodecContext *avctx); 

avcodec_close()的定义位于libavcodec\utils.c,如下所示。

    av_cold int avcodec_close(AVCodecContext *avctx)      {          if (!avctx)              return 0;          if (avcodec_is_open(avctx)) {              FramePool *pool = avctx->internal->pool;              int i;              if (CONFIG_FRAME_THREAD_ENCODER &&                  avctx->internal->frame_thread_encoder && avctx->thread_count > 1) {                  ff_frame_thread_encoder_free(avctx);              }              if (HAVE_THREADS && avctx->internal->thread_ctx)                  ff_thread_free(avctx);              //关闭编解码器              if (avctx->codec && avctx->codec->close)                  avctx->codec->close(avctx);              avctx->coded_frame = NULL;              avctx->internal->byte_buffer_size = 0;              av_freep(&avctx->internal->byte_buffer);              av_frame_free(&avctx->internal->to_free);              for (i = 0; i < FF_ARRAY_ELEMS(pool->pools); i++)                  av_buffer_pool_uninit(&pool->pools[i]);              av_freep(&avctx->internal->pool);              if (avctx->hwaccel && avctx->hwaccel->uninit)                  avctx->hwaccel->uninit(avctx);              av_freep(&avctx->internal->hwaccel_priv_data);              av_freep(&avctx->internal);          }          if (avctx->priv_data && avctx->codec && avctx->codec->priv_class)              av_opt_free(avctx->priv_data);          av_opt_free(avctx);          av_freep(&avctx->priv_data);          if (av_codec_is_encoder(avctx->codec))              av_freep(&avctx->extradata);          avctx->codec = NULL;          avctx->active_thread_type = 0;          return 0;      }  

从avcodec_close()的定义可以看出,该函数释放AVCodecContext中有关的变量,并且调用了AVCodec的close()关闭了解码器。

AVCodec->close()
AVCodec的close()是一个函数指针,指向了特定编码器的关闭函数。在这里我们以libx264为例,看一下它对应的AVCodec的结构体的定义,如下所示。

AVCodec ff_libx264_encoder = {      .name             = "libx264",      .long_name        = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),      .type             = AVMEDIA_TYPE_VIDEO,      .id               = AV_CODEC_ID_H264,      .priv_data_size   = sizeof(X264Context),      .init             = X264_init,      .encode2          = X264_frame,      .close            = X264_close,      .capabilities     = CODEC_CAP_DELAY | CODEC_CAP_AUTO_THREADS,      .priv_class       = &x264_class,      .defaults         = x264_defaults,      .init_static_data = X264_init_static,  }; 

从ff_libx264_encoder的定义可以看出:close()函数对应的是X264_close()函数。继续看一下X264_close()函数的定义,如下所示。

    static av_cold int X264_close(AVCodecContext *avctx)      {          X264Context *x4 = avctx->priv_data;          av_freep(&avctx->extradata);          av_freep(&x4->sei);          //关闭编码器          if (x4->enc)              x264_encoder_close(x4->enc);          av_frame_free(&avctx->coded_frame);          return 0;      }  

从X264_close()的定义可以看出,该函数调用了libx264的x264_encoder_close()关闭了libx264编码器。

1 0