FFmpeg获取RTP流信息时报错:Could not find codec parameters for stream 0 (Video: h264, none)

一、引言

通过FFmpeg命令获取SDP描述的RTP流的信息:

ffmpeg -protocol_whitelist "file,rtp,udp" -i XXX.sdp

正常情况下,可以打印RTP流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率等信息:

但是某些时候,这些信息可能打印不出来,本该显示上述信息的地方显示了“none”,并报错:“Could not find codec parameters for stream 0 (Video: h264, none): unspecified size Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options”:

 本文讲述导致上述报错的可能原因。

二、导致报错的可能原因

先说结论:导致报错的一个可能原因是FFmpeg内部调用的poll函数超时了。

FFmpeg源码内部会默认RTP基于UDP传输,并在udp_read_packet函数里面通过poll函数监视文件描述符(UDP套接字)是否可读。如果poll函数超时多次,即较长时间内UDP套接字没有任何事件发生,poll函数会返回0,此时变量n的值为0,udp_read_packet函数返回AVERROR(ETIMEDOUT),该值为一个负数:

static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
                           uint8_t *buf, int buf_size, int64_t wait_end)
{
//...
    for (;;) {
    //...
        n = poll(p, rt->max_p, POLLING_TIME);
        if (n > 0) {
         //...
        }
        else if (n == 0 && rt->stimeout > 0 && --runs <= 0) {
            return AVERROR(ETIMEDOUT);
        } else if (n < 0 && errno != EINTR)
            return AVERROR(errno);
    }
    //...
    }
//...
}

由于read_packet函数内部调用了udp_read_packet函数,所以这时候read_packet函数也会返回一个负数:

static int read_packet(AVFormatContext *s,
                       RTSPStream **rtsp_st, RTSPStream *first_queue_st,
                       int64_t wait_end)
{
//...
    len = udp_read_packet(s, rtsp_st, rt->recvbuf, RECVBUF_SIZE, wait_end);
//...
    return len;
//...
}

ff_rtsp_fetch_packet函数中调用了read_packet函数。read_packet函数返回负数时,ff_rtsp_fetch_packet函数中变量len的值会小于0,此时会直接执行:return len返回,不会执行ff_rtp_parse_packet函数。所以这个时候pkt-size等于0(正常情况下如果执行ff_rtp_parse_packet函数成功,pkt-size的值会大于0):

int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt)
{
//...
    len = read_packet(s, &rtsp_st, first_queue_st, wait_end);
//...
    if (len < 0)
        return len;
    if (rt->transport == RTSP_TRANSPORT_RDT) {
        ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len);
    } else if (rt->transport == RTSP_TRANSPORT_RTP) {
        ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len);
    //...
    }
//...
}

所以parse_packet函数中,局部变量size的值为0:

static int parse_packet(AVFormatContext *s, AVPacket *pkt,
                        int stream_index, int flush)
{
//...
        int size = pkt->size;
//...
        len = av_parser_parse2(sti->parser, sti->avctx,
                               &out_pkt->data, &out_pkt->size, data, size,
                               pkt->pts, pkt->dts, pkt->pos);
//...
}

parse_packet函数的底层又调用了parse_nal_units函数。buf_size的值(该值等于上述局部变量size的值)为0,导致parse_nal_units函数内部会直接return 0,从而导致没有给FFmpeg内部存贮视频分辨率的变量(s->witch和s->height)赋值:

static inline int parse_nal_units(AVCodecParserContext *s,
                                  AVCodecContext *avctx,
                                  const uint8_t * const buf, int buf_size)
{
//...
    if (!buf_size)
        return 0;
//...
            s->coded_width  = 16 * sps->mb_width;
            s->coded_height = 16 * sps->mb_height;
            s->width        = s->coded_width  - (sps->crop_right + sps->crop_left);
            s->height       = s->coded_height - (sps->crop_top   + sps->crop_bottom);
            if (s->width <= 0 || s->height <= 0) {
                s->width  = s->coded_width;
                s->height = s->coded_height;
            }
//...
}

之后avformat_find_stream_info函数内部会调用has_codec_parameters函数检测音视频编解码参数是否正确:

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
//...
        if (!has_codec_parameters(st, &errmsg)) {
            char buf[256];
            avcodec_string(buf, sizeof(buf), sti->avctx, 0);
            av_log(ic, AV_LOG_WARNING,
                   "Could not find codec parameters for stream %d (%s): %s\n"
                   "Consider increasing the value for the 'analyzeduration' (%"PRId64") and 'probesize' (%"PRId64") options\n",
                   i, buf, errmsg, ic->max_analyze_duration, ic->probesize);
        } 
//...
}

由于视频分辨率的宽度没有被赋值,值为0,所以has_codec_parameters函数中会执行:FAIL("unspecified size")。has_codec_parameters函数返回0:

static int has_codec_parameters(const AVStream *st, const char **errmsg_ptr)
{
//...
#define FAIL(errmsg) do {                                         \
        if (errmsg_ptr)                                           \
            *errmsg_ptr = errmsg;                                 \
        return 0;                                                 \
    } while (0)
//...
        if (!avctx->width)
            FAIL("unspecified size");
//...
}

这就导致了avformat_find_stream_info函数中打印错误日志:

"Could not find codec parameters for stream %d (%s): %s\n"

 "Consider increasing the value for the 'analyzeduration' (%"PRId64") and 'probesize' (%"PRId64") options\n":

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
//...
        if (!has_codec_parameters(st, &errmsg)) {
            char buf[256];
            avcodec_string(buf, sizeof(buf), sti->avctx, 0);
            av_log(ic, AV_LOG_WARNING,
                   "Could not find codec parameters for stream %d (%s): %s\n"
                   "Consider increasing the value for the 'analyzeduration' (%"PRId64") and 'probesize' (%"PRId64") options\n",
                   i, buf, errmsg, ic->max_analyze_duration, ic->probesize);
        } 
//...
}

三、总结

导致报错:“Could not find codec parameters for stream 0 (Video: h264, none): unspecified size Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options”的一个可能原因是FFmpeg内部调用的poll函数超时了。这个时候可以检查:

1.RTP推流端是否有在推流,抓包是否能抓到RTP视频数据。

2.如果抓包无法抓到RTP视频数据,检查是否有关闭防火墙,或是否开放了相应的端口。

3.检查RTP接收端的接收端口是否跟推流端指定的端口匹配。