0
点赞
收藏
分享

微信扫一扫

ShiftMediaProject项目介绍—ffplay工具 init_input函数剖析以及应用

皮皮球场 05-13 21:00 阅读 10

代码

//如果需要,打开文件探测解复用器

static int init_input(AVFormatContext *s, const char *filename,
                      AVDictionary **options)
{
    int ret;
    AVProbeData pd = { filename, NULL, 0 };
    int score = AVPROBE_SCORE_RETRY;   //以RTSP URL为例,探测URL的码流格式,s->pb为空,所以不会进入下面的函数体
   if (s->pb) {
        s->flags |= AVFMT_FLAG_CUSTOM_IO;
        if (!s->iformat)
            return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                         s, 0, s->format_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;
    }

//关键点,如果s->iformat没有指定解复用器,就是调用av_find_input_format,根据名称,找到解复用器,就会迭代所有的解复用器,找到分数最高的

// AVFMT_NOFILE主要是针对某些libformat或者libavdevice解复用器,例如显示器等特殊的设备,所以如果指定了解复用器,直接返回,并且不会进入 //av_probe_input_format2迭代解复用器,当前以RTSP URL 为例,RTSP中的flags也是具备了AVFMT_NOFILE属性

/*做了精简

AVInputFormat ff_rtsp_demuxer = {
    .name           = "rtsp",
    .flags          = AVFMT_NOFILE,
};

所以一旦设置了     AVInputFormat* pAVInputFormat = av_find_input_format("rtsp");
     s->iformat = pAVInputFormat;

(s->iformat && s->iformat->flags & AVFMT_NOFILE) 就是为真,不会走到av_probe_input_format2,迭代所有的解复用器
*/
    if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
        return score;    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
        return ret;    if (s->iformat)
        return 0;
    return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                 s, 0, s->format_probesize);
}

av_probe_input_format2函数继续走读

ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max)
{
    int score_ret;
    ff_const59 AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);
    if (score_ret > *score_max) {
        *score_max = score_ret;
        return fmt;
    } else
        return NULL;
}

av_probe_input_format3中av_demuxer_iterate就是迭代解复用器

ff_const59 AVInputFormat *av_probe_input_format3(ff_const59 AVProbeData *pd, int is_opened,
                                      int *score_ret)
{
    AVProbeData lpd = *pd;
    const AVInputFormat *fmt1 = NULL;
    ff_const59 AVInputFormat *fmt = NULL;
    int score, score_max = 0;
    void *i = 0;
    const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
    enum nodat {
        NO_ID3,
        ID3_ALMOST_GREATER_PROBE,
        ID3_GREATER_PROBE,
        ID3_GREATER_MAX_PROBE,
    } nodat = NO_ID3;

    if (!lpd.buf)
        lpd.buf = (unsigned char *) zerobuffer;

    if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
        int id3len = ff_id3v2_tag_len(lpd.buf);
        if (lpd.buf_size > id3len + 16) {
            if (lpd.buf_size < 2LL*id3len + 16)
                nodat = ID3_ALMOST_GREATER_PROBE;
            lpd.buf      += id3len;
            lpd.buf_size -= id3len;
        } else if (id3len >= PROBE_BUF_MAX) {
            nodat = ID3_GREATER_MAX_PROBE;
        } else
            nodat = ID3_GREATER_PROBE;
    }

    while ((fmt1 = av_demuxer_iterate(&i))) {
        if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
            continue;
        score = 0;
        if (fmt1->read_probe) {
            score = fmt1->read_probe(&lpd);
            if (score)
                av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
            if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
                switch (nodat) {
                case NO_ID3:
                    score = FFMAX(score, 1);
                    break;
                case ID3_GREATER_PROBE:
                case ID3_ALMOST_GREATER_PROBE:
                    score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
                    break;
                case ID3_GREATER_MAX_PROBE:
                    score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
                    break;
                }
            }
        } else if (fmt1->extensions) {
            if (av_match_ext(lpd.filename, fmt1->extensions))
                score = AVPROBE_SCORE_EXTENSION;
        }
        if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
            if (AVPROBE_SCORE_MIME > score) {
                av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
                score = AVPROBE_SCORE_MIME;
            }
        }
        if (score > score_max) {
            score_max = score;
            fmt       = (AVInputFormat*)fmt1;
        } else if (score == score_max)
            fmt = NULL;
    }
    if (nodat == ID3_GREATER_PROBE)
        score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
    *score_ret = score_max;

    return fmt;
}


总结

从上面的代码走读,可以看到该函数由avformat_open_input调用,根据传递的文件名称,迭代所有的解复用器,去确定解复用器的类型,其中如果可能,会打开文件,读取数据,进行判断。


应用

如果不想迭代所有的解复用器,快速定位解复用器,当然前提是直到输入文件对应的解复用器,例如知道输入文件名称是:rtsp://admin:admin123456@192.168.18.201:554/cam/realmonitor?channel=1&subtype=0

就可以调用如下函数,优化点播速度

AVInputFormat* pAVInputFormat = av_find_input_format("rtsp");
is->iformat = pAVInputFormat;
avformat_open_input(&ic, is->filename, is->iformat, &format_opts);

 

举报

相关推荐

0 条评论