Avformat_open_input函数的分析之--HTTP篇

本文主要分析ffmpeg的avformat_open_input函数在处理HTTP协议时的工作流程。从接口参数解析开始,逐步讲解init_input、ffio_open_whitelist、ffurl_open_whitelist等关键函数的实现,特别是http_open函数及其内部的http_open_cnx_internal函数,涉及TCP连接的建立、DNS解析和HTTP请求的发送。整个过程揭示了ffmpeg如何处理网络数据,对理解ffmpeg的网络流处理有重要帮助。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间在做直播的优化,主要是优化首屏时间,因为直播播放器大部分都会采用ffmpeg来处理,所以就会用到avformat_open_input这个函数,这也是首屏耗时比较多的一个地方,这里我主要跟踪一下http的请求以及rtmp的请求,源码都是开源的,这里主要是记录下来以备自己查询,本篇文章主要是是以ijkplayer源码为基础分析的。

avformat_open_input这个函数的作用是打开文件的链接,如果是网络连接,还会发起网络请求,并一直等待网络数据的返回,然后读取视频流的数据。接下来进行详细的分析。

1.接口参数的解析

首先看函数的声明

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

    该函数的主要作用是填充好AVFormatContext **ps这个结构体。AVFormatContext这个结构体里面的参数比较多,这里就不一一列举了,详细可以参考avformat.h这个头文件,具体用到啥到时再详细说明。

  • const char *filename

    文件的全部路径,比如http://flv-meipai.8686c.com/meipai-live/58e03ffd20a05d7a1410d08c.flv

  • AVInputFormat *fmt

    AVInputFormat 的结构体也比较复杂,主要是封装媒体数据封装类型的结构体,比如flv, mpegts, mp4等。在这里可以传入空,如果为空,后面就会从网络数据中读出。当然如果我们知道文件的类型,先用av_find_input_format("flv")初始化出对应的结构体,这里我们用的是flv,先初始化好这个结构体,对于直播来说,会比较节约时间。

  • AVDictionary **options

    struct AVDictionary {
      int count;
      AVDictionaryEntry *elems;
    };
    typedef struct AVDictionaryEntry {
      char *key;
      char *value;
    } AVDictionaryEntry;

    字典类型的可选参数,可以向ffmpeg中传入指定的参数的值。比如我们这里传入了 av_dict_set_int(&ffp->format_opts, "fpsprobesize", 0, 0); 表示fpsprobesize对应的参数值为0,当然还可以传入更多值,具体可以参考options_table.h这个头文件。

2.函数的关键函数实现

avformat_open_input的具体实现在libavformat/utils.c文件。

init_input函数

第一次调用avformat_open_input函数时,传入的ps是属于初始化状态,很多部分可以忽略,直接跳到以下部分

if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;

init_input函数的声明如下

/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
                      AVDictionary **options)

函数的主要功能如注释一样,打开一个文件链接,并尽可能解析出该文件的格式。它里面关键的调用是

if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | 
                      s->avio_flags, options)) < 0)
        return ret;

io_open函数是一个回调函数。一般情况下是采用的默认函数io_open_default,具体的赋值是在libavformat/option.c文件中,调用过程如下:

avformat_alloc_context->avformat_get_context_defaults->(s->io_open  = io_open_default;)
其中io_open_default的函数实现
static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                           const char *url, int flags, AVDictionary **options)
{
    printf("io_open_default called\n");
avformat_open_input 函数FFmpeg 中的一个重要函数,用于打开输入媒体文件,创建一个 AVFormatContext 结构体,并且为每个流分配一个 AVStream 结构体。该函数的使用方法如下: 1. 初始化 AVFormatContext 结构体 首先需要初始化一个 AVFormatContext 结构体,可以使用 avformat_alloc_context 函数来分配内存空间。 2. 打开输入文件 调用 avformat_open_input 函数来打开输入媒体文件,并将文件的信息存储在 AVFormatContext 结构体中。该函数的原型如下: ```c int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options); ``` 其中,参数 ps 是一个指向 AVFormatContext 指针的指针,url 是输入媒体文件的路径,fmt 是一个 AVInputFormat 结构体指针,用于指定输入媒体文件的格式,如果设置为 NULL,则会自动检测输入文件的格式。options 是一个 AVDictionary 结构体指针,用于设置一些额外的选项,例如设置输入缓冲区大小等。 3. 检查 AVFormatContext 结构体 调用 avformat_find_stream_info 函数来检查 AVFormatContext 结构体,并获取媒体文件的一些基本信息,例如流的数量、每个流的编码格式等等。该函数的原型如下: ```c int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); ``` 其中,参数 ic 是一个指向 AVFormatContext 结构体的指针,options 是一个 AVDictionary 结构体指针,用于设置一些额外的选项。 4. 获取流的信息 遍历 AVFormatContext 结构体中的每个 AVStream 结构体,获取每个流的详细信息,例如编码格式、码率、时长等等。 下面是一个简单的示例代码,演示了如何使用 avformat_open_input 函数打开一个媒体文件,并获取每个流的信息: ```c #include <libavformat/avformat.h> int main(int argc, char* argv[]) { AVFormatContext* formatContext = NULL; int ret = avformat_open_input(&formatContext, "input.mp4", NULL, NULL); if (ret < 0) { printf("Failed to open input file!\n"); return -1; } ret = avformat_find_stream_info(formatContext, NULL); if (ret < 0) { printf("Failed to find stream info!\n"); return -1; } for (int i = 0; i < formatContext->nb_streams; i++) { AVCodecParameters* codecParam = formatContext->streams[i]->codecpar; printf("stream %d: codec_id=%d, codec_name=%s, bitrate=%lld, duration=%lld\n", i, codecParam->codec_id, avcodec_get_name(codecParam->codec_id), formatContext->bit_rate, formatContext->duration); } avformat_close_input(&formatContext); return 0; } ``` 在这个示例代码中,我们使用 avformat_open_input 函数打开了一个名为 input.mp4 的媒体文件,然后遍历了 AVFormatContext 结构体中的每个流,并打印出每个流的编码格式、码率和时长等信息。 需要注意的是,avformat_open_input 函数只是打开了输入媒体文件,并创建了一个 AVFormatContext 结构体,但并没有开始解码媒体文件。要想解码媒体文件,需要使用其他的函数,例如 av_read_frame 函数来读取媒体文件的每一帧数据。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值