1. 程式人生 > >ffmpeg之rtsp分析流程

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