一 拿flv/rtmp为例,时间戳用4字节表示,最大范围约是42亿。
二 遇到问题,在rtmp推流过程中,有些服务器会在推流断开之后,hold住连接一段时间。
这样一来就会存在续时间戳的问题,有的服务器没有管时间戳,直接透传的。这样一来就会有个问题,
rtmp传输的时候,传输的是相对时间戳,回退的时间戳会被计算成负值。当然也有服务器通过发送绝对时间戳来避开这个问题。
在实际项目中,遇到这样的问题,使用公司自研的rtmp服务器,偶尔会出现时间戳超过了32bit表示的最大范围,这就奇怪了。
跟了一下ffmpeg的代码发现,ffmpeg有时间戳回绕判断逻辑,
static int64_t wrap_timestamp(const AVStream *st, int64_t timestamp)
{
if (st->internal->pts_wrap_behavior != AV_PTS_WRAP_IGNORE && st->pts_wrap_bits < 64 &&
st->internal->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
if (st->internal->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET &&
timestamp < st->internal->pts_wrap_reference) // 当前时间戳小于reference时间戳。
return timestamp + (1ULL << st->pts_wrap_bits);
else if (st->internal->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET &&
timestamp >= st->internal->pts_wrap_reference)
return timestamp - (1ULL << st->pts_wrap_bits);
}
return timestamp;
}
这个reference时间戳是什么呢?
// reference time stamp should be 60 s before first time stamp
pts_wrap_reference = ref - av_rescale(60, st->time_base.den, st->time_base.num);
是首帧时间戳 - 60s。 这样一来,如果推流的时候,时间戳不是从0开始的,就容易出现这种情况。假设第一次推流时间戳从100s开始,断开后马上重推,
时间戳从0开始,这样就满足条件了。 ffmpeg就会认为是时间戳超过32bit 能表示的最大范围,回绕了。
于是在计算的时间戳的时候就会加上32bit能表示的最大值。
因此: 流媒体服务器如果想hold住连接,建议还是做时间戳续上的逻辑,要不然容易引来很多问题。 要不就推流断开的时候,直接把源销毁,重新推流的时候,再重新创建就能避免上述问题。