您的位置:首页 > 其它

ffmpeg之rtsp分析流程

2016-07-12 17:35 429 查看
1,首先从ffplay.c main()

is = stream_open(input_filename, file_iformat);

2,在straem_open里面

is->read_tid = SDL_CreateThread(read_thread, is);

3,read_thread

err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);

4,avformat_open_input

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

真正的好戏是从init_input();开始的。

我们的目标是想要知道怎样和服务器进行RTSP报文交互的,所以第一个要从输入的网址里面解析知道这要走RTSP协议

 

探测过程:

 static int init_input(AVFormatContext *s, const char *filename,

                      AVDictionary **options)

{

    int ret;

    AVProbeData pd = { filename, NULL, 0 };

    int score = AVPROBE_SCORE_RETRY;

    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;

    }

    if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||

        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))

        return score;//实际上rtsp在这个地方就返回了。

    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_buffer2(s->pb, &s->iformat, filename,

                                 s, 0, s->format_probesize);

}

其实在这个地方以AVFMT_NOFILE分成两类来解析: 

如果播放的是本地文件,那会有个实实在在的文件,而rtsp网址,没有任何文件存在的概念。

首先看rtspdec.c

AVInputFormat  ff_rtsp_demuxer = {

    .name           = "rtsp",

    .long_name      = NULL_IF_CONFIG_SMALL("RTSP input"),

    .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,

};

再看file.c和http

URLProtocol ff_pipe_protocol = {

    .name                = "pipe",

    .url_open            = pipe_open,

    .url_read            = file_read,

    .url_write           = file_write,

    .url_get_file_handle = file_get_handle,

    .url_check           = file_check,

    .priv_data_size      = sizeof(FileContext),

    .priv_data_class     = &pipe_class,

};

URLProtocol ff_httpproxy_protocol = {

    .name                = "httpproxy",

    .url_open            = http_proxy_open,

    .url_read            = http_buf_read,

    .url_write           = http_proxy_write,

    .url_close           = http_proxy_close,

    .url_get_file_handle = http_get_file_handle,

    .priv_data_size      = sizeof(HTTPContext),

    .flags               = URL_PROTOCOL_FLAG_NETWORK,

};

以上先暂时不管,有个印象就好了,我们来分析他是怎么探测的。

AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,

                                      int *score_ret)

{

    AVProbeData lpd = *pd;

    AVInputFormat *fmt1 = NULL, *fmt;

    int score, nodat = 0, score_max = 0;

    const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];

******省略*****

    fmt = NULL;

    while ((fmt1 = av_iformat_next(fmt1))) {

        if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))

            continue; //  file和http就因为这个continue了。而从上面的ff_rtsp_demuxer.flag 可以看出可以进行下一步

        score = 0;

        if (fmt1->read_probe) {

            score = fmt1->read_probe(&lpd);//rtsp的返回score=AVPROBE_SCORE_MAX,有很多虽然到了这一步但是返回时还是0

            if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {

                if      (nodat == 0) score = FFMAX(score, 1);

                else if (nodat == 1) score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);

                else                 score = FFMAX(score, AVPROBE_SCORE_EXTENSION);

            }

        } 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))

            score = FFMAX(score, AVPROBE_SCORE_MIME);

        if (score > score_max) {

            score_max = score;

            fmt       = fmt1;

        } else if (score == score_max)//如果是0 的话,在这一步fmt还是等于null,也就是整个探测没有任何意义了,就会执行avio_open2函数了。

            fmt = NULL;

    }

    if (nodat == 1)

        score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);

    *score_ret = score_max;

    return fmt;

}

由于rtsp在av_probe_input_format3探测成功,所以现在直接返回到avformat_open_input函数,然后执行到

 if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)

        if ((ret = s->iformat->read_header(s)) < 0)  //由于探测成个这个时候iformat=ff_rtsp_demuxer 

            goto fail;

所以现在就进入

static int rtsp_read_header(AVFormatContext *s)

{

   ***********    省略*********

    if (rt->rtsp_flags & RTSP_FLAG_LISTEN) {

        ret = rtsp_listen(s);

        if (ret)

            return ret;

    } else {

        ret = ff_rtsp_connect(s);  //在这里调用:

 

        

        if (ret)

            return ret;

 

    }

  ***********    省略*********

    return 0;

}

 ret = ff_rtsp_connect(s);  //在这里调用:

1,ffurl_connect  ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL); 

2,  err = ff_rtsp_setup_input_streams(s, reply);//describe报文

3,err = ff_rtsp_make_setup_request(s, host, port, lower_transport,

                                 rt->server_type == RTSP_SERVER_REAL ?

                                     real_challenge : NULL);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: