• avcodec_open2()分析


    该函数用于初始化一个视音频编解码器的AVCodecContext。
    int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    各个参数的含义:
    avctx:需要初始化的AVCodecContext。
    codec:输入的AVCodec
    options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置。
    函数调用关系图
    avcodec_open2()
    avcodec_open2()的源代码量是非常长的,但是它的调用关系非常简单——它只调用了一个关键的函数,即AVCodec的init(),后文将会对这个函数进行分析。
    我们可以简单梳理一下avcodec_open2()所做的工作,如下所列:
    (1)为各种结构体分配内存(通过各种av_malloc()实现)。
    (2)将输入的AVDictionary形式的选项设置到AVCodecContext。
    (3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。
    (4)如果是编码器,检查输入参数是否符合编码器的要求
    (5)调用AVCodec的init()初始化具体的解码器。
    我们分析一下第4步和第5步。
    检查输入参数是否符合编码器要求
    在这里简单分析一下第4步,即“检查输入参数是否符合编码器的要求”。这一步中检查了很多的参数,在这里我们随便选一个参数pix_fmts(像素格式)看一下,如下所示。
    1. //检查像素格式
    2. if (avctx->codec->pix_fmts) {
    3. for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
    4. if (avctx->pix_fmt == avctx->codec->pix_fmts[i])
    5. break;
    6. if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE
    7. && !((avctx->codec_id == AV_CODEC_ID_MJPEG || avctx->codec_id == AV_CODEC_ID_LJPEG)
    8. && avctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL)) {
    9. char buf[128];
    10. snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt);
    11. av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported ",
    12. (char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf));
    13. ret = AVERROR(EINVAL);
    14. goto free_and_end;
    15. }
    16. if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P ||
    17. avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P ||
    18. avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P ||
    19. avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P ||
    20. avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P)
    21. avctx->color_range = AVCOL_RANGE_JPEG;
    22. }
    可以看出,该代码首先进入了一个for()循环,将AVCodecContext中设定的pix_fmt与编码器AVCodec中的pix_fmts数组中的元素逐一比较。
    先简单介绍一下AVCodec中的pix_fmts数组。AVCodec中的pix_fmts数组存储了该种编码器支持的像素格式,并且规定以AV_PIX_FMT_NONE(AV_PIX_FMT_NONE取值为-1)为结尾。例如,libx264的pix_fmts数组的定义位于libavcodeclibx264.c,如下所示。
    1. staticconstenum AVPixelFormat pix_fmts_8bit[] = {
    2. AV_PIX_FMT_YUV420P,
    3. AV_PIX_FMT_YUVJ420P,
    4. AV_PIX_FMT_YUV422P,
    5. AV_PIX_FMT_YUVJ422P,
    6. AV_PIX_FMT_YUV444P,
    7. AV_PIX_FMT_YUVJ444P,
    8. AV_PIX_FMT_NV12,
    9. AV_PIX_FMT_NV16,
    10. AV_PIX_FMT_NONE
    11. };
    从pix_fmts_8bit的定义可以看出libx264主要支持的是以YUV为主的像素格式。
    现在回到“检查输入pix_fmt是否符合编码器的要求”的那段代码。如果for()循环从AVCodec->pix_fmts数组中找到了符合AVCodecContext->pix_fmt的像素格式,或者完成了AVCodec->pix_fmts数组的遍历,都会跳出循环。如果发现AVCodec->pix_fmts数组中索引为i的元素是AV_PIX_FMT_NONE(即最后一个元素,取值为-1)的时候,就认为没有找到合适的像素格式,并且最终提示错误信息。
    AVCodec->init()
    avcodec_open2()中最关键的一步就是调用AVCodec的init()方法初始化具体的编码器。AVCodec的init()是一个函数指针,指向具体编解码器中的初始化函数。这里我们以libx264为例,看一下它对应的AVCodec的定义。libx264对应的AVCodec的定义位于libavcodeclibx264.c,如下所示。
    1. AVCodec ff_libx264_encoder = {
    2. .name = "libx264",
    3. .long_name = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    4. .type = AVMEDIA_TYPE_VIDEO,
    5. .id = AV_CODEC_ID_H264,
    6. .priv_data_size = sizeof(X264Context),
    7. .init = X264_init,
    8. .encode2 = X264_frame,
    9. .close = X264_close,
    10. .capabilities = CODEC_CAP_DELAY | CODEC_CAP_AUTO_THREADS,
    11. .priv_class = &x264_class,
    12. .defaults = x264_defaults,
    13. .init_static_data = X264_init_static,
    14. };
    可以看出在ff_libx264_encoder中init()指向X264_init()。X264_init()的定义同样位于libavcodeclibx264.c,
    X264_init()的代码以后研究X264的时候再进行细节的分析,在这里简单记录一下它做的两项工作:
    (1)设置X264Context的参数。X264Context主要完成了libx264和FFmpeg对接的功能。可以看出代码主要在设置一个params结构体变量,该变量的类型即是x264中存储参数的结构体x264_param_t。
    (2)调用libx264的API进行编码器的初始化工作。例如调用x264_param_default()设置默认参数,调用x264_param_apply_profile()设置profile,调用x264_encoder_open()打开编码器等等。
    X264Context的定义,位于libavcodeclibx264.c,
  • 相关阅读:
    windows 共享文件夹 给 mac
    给mac配置adb 路径
    关于android 加载https网页的问题
    http tcp udp ip 间的关系
    手机服务器微架构设计和实现专题
    添加ssh key
    本人对于线程池的理解和实践
    使用Android Butterknife
    记一次失败的笔试(华为研发工程师-汽水瓶笔试题)
    简易坦克大战python版
  • 原文地址:https://www.cnblogs.com/elesos/p/10042622.html
Copyright © 2020-2023  润新知