丢帧是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;
这样就不丢帧了,延时也正常了。
但刚开始时,仍然要丢几帧。
不过仔细听起来,这种做法,声音总是一顿一顿的,不连续。
没有办法,还是得用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的拖累。