rtsp流媒体播放器----ffmpeg相关代码走读(一)

来源:互联网 发布:apache pulsar 编辑:程序博客网 时间:2024/05/16 08:54

rtsp流媒体播放器—-ffmpeg相关代码走读(一)

本文介绍avformat_open_input和init_input两个函数

直接贴代码如下

avformat_open_input 函数

int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options){    AVFormatContext *s = *ps;    int ret = 0;    AVDictionary *tmp = NULL;    ID3v2ExtraMeta *id3v2_extra_meta = NULL;    if (!s && !(s = avformat_alloc_context()))        return AVERROR(ENOMEM);    if (!s->av_class){              // 如果用户自己申请了s那么就必须同时申请s->av_class        av_log(0, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");        return AVERROR(EINVAL);    }    if (fmt)        s->iformat = fmt;    if (options)        av_dict_copy(&tmp, *options, 0);    if ((ret = av_opt_set_dict(s, &tmp)) < 0)        goto fail;    // 此函数打开查找需要rtsp输入流    if ((ret = init_input(s, filename, &tmp)) < 0)        goto fail;    /* check filename in case an image number is expected */    if (s->iformat->flags & AVFMT_NEEDNUMBER) {        if (!av_filename_number_test(filename)) {            ret = AVERROR(EINVAL);            goto fail;        }    }    s->duration = s->start_time = AV_NOPTS_VALUE;    av_strlcpy(s->filename, filename, sizeof(s->filename));    /* allocate private data */    if (s->iformat->priv_data_size > 0) {       // rtsp 播放器此处对应的iformat就是ff_rtsp_demuxer        if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {            ret = AVERROR(ENOMEM);            goto fail;        }        if (s->iformat->priv_class) {           // 这里的 priv_class 就是 rtsp_demuxer_class            *(const AVClass**)s->priv_data = s->iformat->priv_class;            av_opt_set_defaults(s->priv_data);            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)                goto fail;        }    }    /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */    if (s->pb)        ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);    // 执行rtsp拉流动作    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)        if ((ret = s->iformat->read_header(s)) < 0)            goto fail;    if (id3v2_extra_meta &&        (ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)        goto fail;    ff_id3v2_free_extra_meta(&id3v2_extra_meta);    // 将获取到的帧数据添加到队列里面去,其实就是个list    queue_attached_pictures(s);    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset)        s->data_offset = avio_tell(s->pb);    // 最大buffer数据量大小    s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;    if (options) {        av_dict_free(options);        *options = tmp;    }    *ps = s;    return 0;fail:    ff_id3v2_free_extra_meta(&id3v2_extra_meta);    av_dict_free(&tmp);    if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))        avio_close(s->pb);    avformat_free_context(s);    *ps = NULL;    return ret;}

1,s初始化值为NULLfmtoptions我们这里用的NULL

2,默认的入参*ps应该是 NULL, 此处申请实际对象s的内存

    if (!s && !(s = avformat_alloc_context()))        return AVERROR(ENOMEM);

3,此处查找需要的demuxer

其实就是将对象s对应的字段iformat设置为ff_rtsp_demuxer,该变量定义在rtspdec.c文件中。

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

4,申请对象s的私有数据s->priv_data

播放器此处对应的iformat就是ff_rtsp_demuxer,所以s->iformat->priv_data_size值其实就是ff_rtsp_demuxer.priv_data_size
同理可以知道这里的priv_class就是rtsp_demuxer_class

    /* allocate private data */    if (s->iformat->priv_data_size > 0) {               if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {            ret = AVERROR(ENOMEM);            goto fail;        }        if (s->iformat->priv_class) {           // 这里的 priv_class 就是 rtsp_demuxer_class            *(const AVClass**)s->priv_data = s->iformat->priv_class;            av_opt_set_defaults(s->priv_data);            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)                goto fail;        }    }

5,执行rtsp拉流动作

这里其实调用的是rtsp_read_header.,后面重点分析。

    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)        if ((ret = s->iformat->read_header(s)) < 0)            goto fail;

6,将获取到的帧数据添加到队列里面去

其实就是个list

    queue_attached_pictures(s);

7,最后成功返回0

init_input函数

该函数定义如下:

static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options){    int ret;    AVProbeData pd = {filename, NULL, 0};    if (s->pb) {        s->flags |= AVFMT_FLAG_CUSTOM_IO;        if (!s->iformat)            return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, s->probesize);        else if (s->iformat->flags & AVFMT_NOFILE)            av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "                                      "will be ignored with AVFMT_NOFILE format.\n");        return 0;    }    if ( (s->iformat && s->iformat->flags & AVFMT_NOFILE) ||        (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))        return 0;    if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,                          &s->interrupt_callback, options)) < 0)        return ret;    if (s->iformat)        return 0;    return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, s->probesize);}

查表确定当前输入格式

av_probe_input_format这个函数返回了查找到的输入格式,ffmpeg会遍历所有注册的demuxer,最精确匹配的将被返回。

    if ( (s->iformat && s->iformat->flags & AVFMT_NOFILE) ||        (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))        return 0;

对象ff_rtsp_demuxer等定义于rtspdec.c文件中

static const AVClass rtsp_demuxer_class = {    .class_name     = "RTSP demuxer",    .item_name      = av_default_item_name,    .option         = ff_rtsp_options,    .version        = LIBAVUTIL_VERSION_INT,};AVInputFormat ff_rtsp_demuxer = {    .name           = "rtsp",    .long_name      = NULL_IF_CONFIG_SMALL("RTSP input format"),    .priv_data_size = sizeof(RTSPState),    .read_probe     = rtsp_probe,    .read_header    = rtsp_read_header,    .read_packet    = rtsp_read_packet,    .read_close     = rtsp_read_close,    .read_seek      = rtsp_read_seek,    .flags          = AVFMT_NOFILE,    .read_play      = rtsp_read_play,    .read_pause     = rtsp_read_pause,    .priv_class     = &rtsp_demuxer_class,};
0 0