2010年6月21日星期一

阅读和使用LIVE555的一些简单记录

行2 012

注意,此图和本文无关。图为以前拍的汉诺威动物园的企鹅。要了解正文请继续阅读。

最近继续看了看live555的代码,进行了一些具体的实验。
对live555的部分特性有了一定的了解。
稍微看了网上的一些关于live555的文章,千篇一律。
大部分都是转载自一个人的博客。
很少人进行一些实验或进行一些比较深入的代码阅读,然后写出一些其他的经验之谈。
我想应该是很多人都进行了代码阅读,理解了代码,但是就是懒的写一些东西吧。
这里自夸一下自己,哈,hanyionet真是大好人,大闲人。
这里我尽我自己一份小小的力,给大家写一些小小的经验之谈。如果有时间的话,我将写一些更多的文章关于live555。

以前在网上看到一个论坛有人问:
使用live555的testMpeg1or2VideoReceiver在其FrameSink的afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) 接受的buffer是否是代表一个帧。
大概是这个问题。

当时看到没有太在意。
因为没有搞这部分。
刚刚做了一些实验。
根据实验的结果。我可以得出:
live555如果采用 A test program that reads a MPEG-1 or 2 Video Elementary Stream file就是testMpeg1or2VideoStreamer.cpp发送rtp包,那么得到的rtp包包含的就不是一个完整的帧。

如果不是完整的一帧那么我们该如何进行“正确”的解码呢。
例如我们使用的是FFmpeg的avcodec_decode_video()进行解码。
那么如果要正确解码,就必须将一个完整的帧的buffer放入这个解码API中去。
如果没有正确的一帧,我们直接将buffer放入avcodec_decode_video()进行解码,会有什么结果呢。
我们就会得到抖动的画面,带着马赛克,或者画面模糊等等。
这说明了,我们没有正确的解码。

接下来的问题是,我们该如何得到完整的一帧呢。
因为我们现在得到的是一些buffer(它为unsigned char*)。
很多人会想到要使用avcodec_decode_video()进行解码然后得到的例如AVFrame *_decoded_frame这样的_decoded_frame的指针。
通过类似_decoded_frame->pict_type == FF_I_TYPE 或FF_P_TYPE,进行判断。
但是我进行的实验证明这是不可以的,也就是说无法正确地判断这些buffer是否为同一帧的buffer。
我使用的avcodec_find_decoder( CODEC_ID_MPEG2VIDEO );
可能其他的codec可以通过_decoded_frame->pict_type == FF_I_TYPE 或FF_P_TYPE,进行判断。
但是在CODEC_ID_MPEG2VIDEO或CODEC_ID_MPEG1VIDEO的情况下不行。

经过一点思考。
我发现使用live555中afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) 的presentationTime进行判断就可以判断出是得到的buffer是否是同一帧的。
具体实现如下,我只是提供一点思路。
具体实现请自己搞定。
请不要问我如何编。
很简单,请想想吧,请不要写信要源代码。

一点思路:

可以将每次进入afterGettingFrame1的presentationTime进行判断,如果是相同的
那么将相同的buffer存入一个buffer中去,也就是两个buffer连接在一起,不知道我是否表述的清晰。
其实就是使用memcpy这个函数。
当presntationTime不同时候,将以前存的buffer(已经是完整一帧)传给avcodec_decode_video()就可以正确地解一帧了。

当然了,这里在提一下,我们最好设两个thread,一个thread进行live555的接受,另一个thread进行解码使用FFmpeg或其他。
两个thread如果读取同一变量,请设定mutex进行锁。

在进行一些延伸的思考,为什么可以使用presentationTime判断是否为同一帧呢。
如果读过live555的http://www.live555.com/liveMedia/doxygen/html/DeviceSource_8cpp-source.html
liveMedia/DeviceSource.cpp的朋友就知道了,非常非常不负责任的简单地说,如果我们想自己进行编码然后再播放那么就要使用DeviceSource这个类,自己进行一些拓展,DeviceSource是在服务器端,进行rtp包发送前的处理。
DeviceSource里的deliverFrame()函数的用途就是将编码后产生的buffer连同想对应的presentationTime一起“发送出去”,如果熟悉FFmpeg可以使用avcodec_encode_video()进行编码。
看到了吧,在这里就可以找到了presentationTime和一帧里的buffer之间的联系。
当然了,我的这个解释很不负责任,极度简单。
只是通过一个例子进行说明。
有兴趣的朋友可以通过进行更进一步的代码阅读了解presentationTime和一帧里的buffer之间的关系。

好了,随手写一些东西,比较不见条理性。希望大家见谅。
希望大家在需要的时候,能搜索到这篇文章,并得到一点帮助。

注意,以上的解释和记录比较不负责任,极度简单,因为没有进行更系统的代码阅读和分析,不能保证以上所诉完全正确。

没有评论: