ffmpeg之rtsp分析流程

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



你可能感兴趣的:(ffmpeg)