一、引言
通过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接收端的接收端口是否跟推流端指定的端口匹配。