关于vlc中是如何同步live库得到的rtp audio,video流?

ear5cm 2010-11-09 11:17:15
各位大牛,我最近做个实验,关于从wowza服务器发出的rtsp流. 是 audio为 mpa, video为 h264.

在客户端 ,用vlc 以tcp来连接,基本上可以看到 audio,video是同步的,虽然有时候会卡,但 顺畅之后还是保持同步.
不过,在 log消息里,将等级设为2,可以看到 很多 pts late,audio buffer,什么 drop buffer ,upsampling等消息.感觉,
vlc为了同步,去除了很多 不理想的数据?
请问,这个vlc这是通过什么算法,来 判断 哪些audio或者video数据不合理,从而选择丢弃,保持同步呢?

而我自己在自己实现的播放器代码里,也是调用了live库. 我自己的pts同步策略很简单.就是 将得到的 每个rtp数据包 所带的pts, 作为同步时间.当然要以第一个得到的数据pts为参考值,之后的与之相减. audio,video各自以 第一个作为参考值的.
但很明显,效果很不理想,播放之后,很快就不同步了. 声音总是慢慢的越来越之后 . 可见,我的同步算法太幼稚了.

我是假想 用live库从server上得到的pts应该,本身就是同步好了的.到我这边,我直接用就可以了. 可是,从现象来看,以及从 vlc的log来看, 似乎还需要做 pts的误差调整.

希望,哪位熟悉这个的,帮忙解解惑.应该怎么处理pts,才能维持好同步呢?
万分感谢~
...全文
574 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
dengzikun 2010-11-10
  • 打赏
  • 举报
回复
楼主的做法只能实现媒体内的同步,不能实现媒体间的同步.

你的 假想 用live库从server上得到的pts应该,本身就是同步好了的.
这没问题,但是你的不是实时流,所以媒体数据到客户端的时间是受到客户端播放速度
控制的,也就是抖动会比较大.你可以参考DSHOW的音视频同步的办法,音视频用一个参考时钟,
早到的做缓存,到期播放,晚到的立即播放或丢弃. 当然,做这一切的前提是时间戳正确.
可以参考DSHOW的CBaseRender.
tufaqing 2010-11-10
  • 打赏
  • 举报
回复
时间戳很关键,任何播放都是通过时间戳控制同步的,不能简单的处理,否则会不同步,如果偏差过大,缓冲区找不到要同时播放的数据,就会丢失比较快的包。
看这个情况,主要还是你的时间戳打得有问题。看一下你的音视频数据是怎么来的,一般有的自己带有时间戳。如果是抓的数据的话,可以以系统时间参考。
ear5cm 2010-11-10
  • 打赏
  • 举报
回复
鲲哥:
谢谢你的帮忙 。我继续一头雾水。

之前,我对live server提供的 mpa/mpv的流,就是直接用 pts 来同步的,效果还行。

现在的这个 wowza server提供的 h264/mpa的流,效果就不行了。你说的“媒体间同步需要RTCP的时间戳”这个怎么理解呢?

因为我们的系统decoder那边,基本都是用pts来同步的。而理论上,只需要我这边 提供给decoder的接口buffer提供数据,和相应的pts。 其他的 好像 不需要我来管 。

所以,我觉得我需要处理的还是 pts 这个时间戳的 修正上? (比较幼稚的想法是, 找个参照点,判断出当前时间戳 提前还是落后了,就对应 补上误差 。可是, 怎么来判断这个pts 提前还是落后, 不知,哎)

关于DSHOW,我完全没接触。。。
dengzikun 2010-11-10
  • 打赏
  • 举报
回复
你在A/V线程中分别使用A/V时间戳做定时播放,两个线程独立向前运行。
这样要做到A/V同步,需要两个条件:A/V时间戳正确,相应的A/V数据包同时到达,
现在你假设A/V时间戳是正确的,那么数据包同时到达是不可能保证的,这样,两个
线程的等待时间点会出现漂移,并且漂移会积累的很快,A/V很快就不同步了。

一个简单的解决办法就是,放弃时间戳,A/V分别按自己的帧率播放,当然,这个方法有局限性,
更适合实时流。

更好的办法可以参考DSHOW的时间戳。

这个问题的根源是因为RTP时间戳只能实现媒体内同步,媒体间同步需要RTCP的时间戳。你可以
参考MPEG2系统的媒体间同步方法,需要PCR在编解码器之间做时钟同步。
ear5cm 2010-11-10
  • 打赏
  • 举报
回复
感谢 tufaqing ,dengzikun ~!

To dengzikun :就像你说的 一切的前提是时间戳正确。 我现在就是假设的 我得到的 rtp包的 pts是正确的。用的rtpsource->curPacketRTPTimestamp()来取得的。 这些时间应该如何来 同步呢?理论上,我想,这些时间都是 server同步好,打在包里的 ,供client端来同步用。 能简单说下您的理解吗?谢谢~

To tufaqing :我是用时间戳来同步。时间戳也是rtp包里,解析得到的。我看 audio,video都是以各自随机的一个值作为 时间戳的起点值,然后 根据fps递增的。“你说不能简单的处理”我就是很头疼,不知道怎么来有效的处理,因为我简单的将这些值 来比对,并不对。所以,能否请您给点建议呢? 谢谢了
openrtxp是一个小型的rtsp/rtmp协议的rtxpserver, ANSI C语言开发,方便移植到嵌入式系统如IPCam. 功能list: (1)支持H264 + AAC编码格式封包成RTSP/RTMP,可以使用VLC/VLC网页控件播放,RTMP可以用VLC/flash player/kmplayer播放. 支持RTP over TCP/UDP. (2)RTSP/RTMP的URL可配置,缺省URL如下: VOD点播(文件回放):rtsp://192.168.2.62/playback/test.mp4 直播:rtsp://192.168.2.62/stream/0 RTMP的url和RTSP一致,只需要把"rtsp"修改为"rtmp" 图像延迟在250ms左右. (4)同时支持基于UDP/TCP的TS直播. (5)移植很方便,只需要编写约7个直播获取Audio/Video的函数即可. (6)支持DVR功能,通过简单的调用,即可实现把直播保存到硬盘中(支持.mp4/.ts格式) (7)所有代码为纯C代码,可移植性强。 移植很方便,只需要编写约7个直播获取Audio/Video的函数即可. int liveInit(); int liveUninit(); void* liveCreate(char* filename,void* prv,double* duration,unsigned int* video_codec,unsigned int* video_width,unsigned int* video_height,unsigned int* video_fps,unsigned int* video_bps,unsigned int* audio_codec,unsigned int* audio_samplerate,unsigned int* audio_channels,unsigned int* audio_bps); int liveGetHeaderPacket(void* hdl,int data_type,char* buf); int liveGetVideoPacket(void* hdl,char* buf,double* pts,int* sync); int liveGetAudioPacket(void* hdl,char* buf,double* pts); int liveDestroy(void* hdl); FAQ: 1.为什么要用ffmpeg和ssl? 答:ssl是rtmp协议要用到。ts,mp4文件都是由ffmpeg中的方法来实现的。如果去掉这两个lib,就支持不了rtmp,.ts,.mp4等功能。 libssl和libcrypto则是rtmp要使用的,修改configs.h 和Makefile就可以很容易地去掉。 playback/DVR/TS功能要用到ffmpeg lib的支持,修改configs.h和Makefile也很容易去掉ffmpeg,但这三项功能就实现不了。 去掉openssl和ffmpeg后,编译出来的image size在83KB左右。 2.如何支持底层的多通道?比如一个是1080P ch,一个是CIF ch. 答:通过live->idx = idx=atoi(filename);来区分不同的hw path。 3.如何支持多session或多个client连接? 答:来一个session,就调用liveCreate()来生成一个live实体。 liveCreate()的时候每次都是malloc()一个新的instance,那个instance包含一个h264enc指针和一个rd。 h264enc指针内部有一个wr,也就是说所有的都共享一个h264enc指针,但每个的rd是独立的。 第二次liveCreate()的时候,h264enc指针已经有了,就不需要再次建立h264enc指针了,直接引用就可以了(因此h264enc内部要做引用计数) 每多调用一次liveCreate(),h264enc->ref++。 每调用一次liveDestroy(),就h264enc->ref--,==0的时候就真正destroy h264enc。
Hardware Video Encoding on iPhone — RTSP Server example On iOS, the only way to use hardware acceleration when encoding video is to use AVAssetWriter, and that means writing the compressed video to file. If you want to stream that video over the network, for example, it needs to be read back out of the file. I’ve written an example application that demonstrates how to do this, as part of an RTSP server that streams H264 video from the iPhone or iPad camera to remote clients. The end-to-end latency, measured using a low-latency DirectShow client, is under a second. Latency with VLC and QuickTime playback is a few seconds, since these clients buffer somewhat more data at the client side. The whole example app is available in source form here under an attribution license. It’s a very basic app, but is fully functional. Build and run the app on an iPhone or iPad, then use Quicktime Player or VLC to play back the URL that is displayed in the app. Details, Details When the compressed video data is written to a MOV or MP4 file, it is written to an mdat atom and indexed in the moov atom. However, the moov atom is not written out until the file is closed, and without that index, the data in mdat is not easily accessible. There are no boundary markers or sub-atoms, just raw elementary stream. Moreover, the data in the mdat cannot be extracted or used without the data from the moov atom (specifically the lengthSize and SPS and PPS param sets). My example code takes the following approach to this problem: Only video is written using the AVAssetWriter instance, or it would be impossible to distinguish video from audio in the mdat atom. Initially, I create two AVAssetWriter instances. The first frame is written to both, and then one instance is closed. Once the moov atom has been written to that file, I parse the file and assume that the parameters apply to both instances, since the initial conditions were the same. Once I have the parameters, I use a dispatch_source object to trigger reads from the file whenever new data is written. The body of the mdat chunk consists of H264 NALUs, each preceded by a length field. Although the length of the mdat chunk is not known, we can safely assume that it will continue to the end of the file (until we finish the output file and the moov is added). For RTP delivery of the data, we group the NALUs into frames by parsing the NALU headers. Since there are no AUDs marking the frame boundaries, this requires looking at several different elements of the NALU header. Timestamps arrive with the uncompressed frames from the camera and are stored in a FIFO. These timestamps are applied to the compressed frames in the same order. Fortunately, the AVAssetWriter live encoder does not require re-ordering of frames. When the file gets too large, a new instance of AVAssetWriter is used, so that the old temporary file can be deleted. Transition code must then wait for the old instance to be closed so that the remaining NALUs can be read from the mdat atom without reading past the end of that atom into the subsequent metadata. Finally, the new file is opened and timestamps are adjusted. The resulting compressed output is seamless. A little experimentation suggests that we are able to read compressed frames from file about 500ms or so after they are captured, and these frames then arrive around 200ms after that at the client app. Rotation For modern graphics hardware, it is very straightforward to rotate an image when displaying it, and this is the method used by AVFoundation to handle rotation of the camera. The buffers are captured, encoded and written to file in landscape orientation. If the device is rotated to portrait mode, a transform matrix is written out to the file to indicate that the video should be rotated for playback. At the same time, the preview layer is also rotated to match the device orientation. This is efficient and works in most cases. However, there isn’t a way to pass this transform matrix to an RTP client, so the view on a remote player will not match the preview on the device if it is rotated away from the base camera orientation. The solution is to rotate the pixel buffers after receiving them from the capture output and before delivering them to the encoder. There is a cost to this processing, and this example code does not include this extra step.

2,543

社区成员

发帖
与我相关
我的任务
社区描述
专题开发/技术/项目 多媒体/流媒体开发
社区管理员
  • 多媒体/流媒体开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧