在海思hi3516C上面做流媒体rtsp server,总是存在延时很大并且VLC播放丢帧

丢帧是VLC报出来的,如下:

我将帧率设置为15,而且是CIF分辨率,局域网传输,不应该有丢帧啊!?我XX

查了一整天,各种办法,最后我发现是海思SDK送出来PTS有问题:

pts=pstStream->pstPack[i].u64PTS*90/1000;      //ms*90

干脆换成我自己计算PTS:

gVideoPath[ch].VencOutQue[wp].u64PTS=gVideoPath[ch].seq*gVencIn[ch].pts_tick;

这样就不丢帧了,延时也正常了。

但刚开始时,仍然要丢几帧。

扫描二维码关注公众号,回复: 2678882 查看本文章

不过仔细听起来,这种做法,声音总是一顿一顿的,不连续。

没有办法,还是得用SDK提供出来timestamp。而且,越播延迟越大,但第一路流1080P就不会,这TMD究竟是什么原因啊?

我在海思底层MPP上面配置了三个通道(1080P/720P/QVGA):

   VpssGrp = 0;   VpssChn = 3;

   VencGrp = 0;   VencChn = 0;             //for HD recording

   s32Ret = _COMM_VENC_Start(VencGrp, VencChn, pVideo+0);

   s32Ret = SAMPLE_COMM_VENC_BindVpss(VencGrp, VpssGrp, VpssChn);

   VpssGrp = 0;        VpssChn = 4;

   VencGrp = 1;    VencChn = 1;           //for preview

   s32Ret = _COMM_VENC_Start(VencGrp, VencChn, pVideo+1);

   s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);

   VpssGrp = 0;        VpssChn = 5;

   VencGrp = 2;    VencChn = 2;    //for CIF clip recording

   s32Ret = _COMM_VENC_Start(VencGrp, VencChn, (pVideo+2));

现在的现象就是1080P这个通道是正常的,延迟在2s左右,丢帧也少。720P/QVGA就不行了,延时达到6秒以上,丢帧严重。

下面只有两个办法:

1、 换成wis-streamer。

2、 录像为mp4文件播放,看是否有问题。

我把VpssChn改了一下,不再关联3/4/5,而是关联0,这样的话,延时的问题基本上算是解决了。但是,丢帧的原因始终没有找到。

我去掉audio后,Video就不再丢帧了,看来,这是音视频不同步引发的血案啊!

Hi3518平台流媒体服务器的音视频同步问题

PTS:Presentation Time Stamp
DTS:Decoding TimeStamp
PCR:Program ClockReference
STC:System TimeClock

我也正在研究这一块.说解码才得到的都是不对的.其实是从LIVE555那里得到的.afterGettingFrame里面有一个参数是presentationTime.通过RTSP得到的流的PTS都是由它计算出来的.不过它的单位是us.还是90000.这个要值得注意.ffmpeg解码后只是校准了PTS.PTS真正的源头是RTSP的发送方.只有发送方才有能力告诉你数据采集的PTS从而帮助你进行同步.

RTP规范里,RTP包的TIMESTAMP字段应该指示图像的显示时间,而且时间单位是tick.像视频的话单位就是90K,那一段25fps的视频每增长一帧,时间戳就应该增加3600.但是同样是通过RTSP播实时流,ffplay和vlc对于时间戳的处理是不一样的.如果在ffplay里.它直接把rtp包里的timestamp当作是解码前的pts传入avcodec_decode_video2().ffmpeg进行重排序(如果没有B帧,一般是原样复制到解码后的AVFrame里),然后将它乘以1/90K还原成us的单位,然后直接用来usleep();而vlc的rtsp传输模块用的是live555,live555在我看来多此一举了,它对上层屏蔽了RTP包里的TIMESTAMP.而是直接帮你还原好了.单位就是us.也就是说,afterGettingFrame()函数的pts单位是us.可以直接拿来用usleep了。

// 1s = 90000 time scale , 一帧就应该是  90000/video_frame_rate 个timescale
static uint32_t video_frame_rate = 30;
static uint32_t video_pts_increment = 90000 / video_frame_rate;   //用一秒钟除以帧率,得到每一帧应该耗时是多少,单位是 timescale单位
static uint64_t video_pts = 0;
pts初始值可以是任意的,我一般定为0,后面每一帧加上增量就可以了。
音频pts的计算方法同上,只不过不是通过帧率,而是通过采样率。
uint32_t audio_pts_increment = (90000 * audio_samples_per_frame) / audio_sample_rate;
audio_samples_per_frame这个值对aac和mp3是不同的,aac固定为1024,mp3固定为1152。

>>

用了你的计算方法,音视频可以用VLC同时播放,但是音频过一段时间后就变慢了。
而且越来延时越大,最后音频就没有了。请问知道是什么原因吗?而且这是变帧率,根本不应该这样来计算。

A:RFC3984 规定采用 90000 Hz 的时钟,因此如果编码帧频是 30,那么时间戳间隔就该是 90000 / 30 = 3000。audio_timebase =av_q2d(fmtctx->streams[audio_index]->time_base);
video_timebase = av_q2d(fmtctx->streams[video_index]->time_base);
last_video_pts = pts * video_timebase;
last_audio_pts = pts * audio_timebase;
timebase就是单位
以audio为基准同步video(算法是如何?),因为audio是固定帧率的,而video是变帧率。只要设置好了 ao 的参数,如sample rate, channels, sample size等, audio驱动就能以正确的速度播放,所以只要程序里write不出大问题的话,这种同步是非常有效的。

一 固定帧率

1. 视频时间戳

     pts = inc++*(1000/fps);  其中inc是一个静态的,初始值为0,每次打完时间戳inc加1.

    在ffmpeg,中的代码

    pkt.pts=m_nVideoTimeStamp++ * (m_VCtx->time_base.num * 1000 /m_VCtx->time_base.den);

 2. 音频时间戳

    pts = inc++ *(frame_size * 1000 / sample_rate)

   在ffmpeg中的代码为

   pkt.pts=m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。

。正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质

对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

背景知识:

(一个AAC原始帧包含一段时间内1024个采样及相关数据)
分析:

1 AAC

音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)

一帧 1024个 sample。采样率 Samplerate 44100KHz,每秒44100个sample, 所以 根据公式   音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率

当前AAC一帧的播放时间是= 1024*1000000/44100=22.32ms(单位为ms)

2 MP3

 mp3 每帧均为1152个字节, 则:

frame_duration = 1152 * 1000000 /sample_rate

例如:sample_rate= 44100HZ时, 计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来。

二 可变帧率

有很多的采集卡,摄像头,在做采集的时候,明明设置的25FPS,但实际采集数据回调过来,发现并不是40毫秒的间隔,而是50,60,甚至100不等的时间间隔。

这就给编码后打时间戳带来很大的困难。

在libav里,我们的默认编码参数都是:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps;

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1;

这样在编码后的时间戳以1递增,只适合于固定帧率。

我们来改一下:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps * 1000;

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1* 1000;

这样就把时间戳的scale变成了毫秒,就可以以毫秒为单位进行计算了,如下:

tAvPacket.pts = ((s64)u32TimeStamp * (s64)s32Fps);

u32TimeStamp是从开始记录的时间差值,以毫秒为单位;s32Fps是帧率。

对于音频,mp4文件默认是采样率为tick的,时间戳计算为:

tAvPacket.pts = (AvEncoderAudioInSizeGet(hHandle) * ( (s64)(u32TimeStamp)) / (AvEncoderAudioInSizeGet(hHandle) * 1000 / ptAvEncoder->ptAvStreamAudio->codec->sample_rate);

AvEncoderAudioInSizeGet(hHandle) 每次编码器需要的PCM数据长度。

u32TimeStamp是从开始记录的时间差值,以毫秒为单位。

 ptAvEncoder->ptAvStreamAudio->codec->sample_rate PCM采样率,代表一秒的数据量。

因为乘以了1000,所以也化成了毫秒单位。

一般有三种办法
1.以音频为主进行同步
2.以视频为主进行同步
3.以一个外部参考时钟为同步
你可以参考下ffmpeg下的ffplay来做,还有
http://www.dranger.com/ffmpeg/tutorial05.html 和
http://www.dranger.com/ffmpeg/tutorial04.html 
具体如何实现还是要靠自己研究清楚代码后来解决的.

同步的方法:

1、 audio pts直接用video的pts,这样的话,播放继续,Video受audio拖累而丢包严重。

2、 video pts直接用audio pts,结果Video播放不动,audio可以继续。

3、 audio pts直接填0,也就是不要,这样播放是最为流畅的,但video偶尔有丢帧,不知道是否受audio的拖累。

猜你喜欢

转载自blog.csdn.net/xswy1/article/details/81290310