您的位置:首页 > 理论基础 > 计算机网络

初探基于TCP的服务器/客户端结构的聊天系统(四)视频聊天的实现

2013-08-15 22:01 816 查看
    视频聊天,肯定首先要捕获视频。这个实现倒是很顺利,我是用别人写好的Widget类实现的。好东西,附上链接http://download.csdn.net/download/zlhforever2/4503796,没分的我可以传给你。很容易用,作者是继承的QWidget类。这个就不贴源代码了。

    接下来就遇到一个很让人头疼的问题。我估算了一下数据量,如果采用RGB888格式的也就是RGB24格式,那么在320*240大小的画面的情况下,一帧原始数据的大小就是320*240*3 = 230400字节。那么加入1秒钟传输10帧原始数据,那么就要2304000字节,也就是2M多,那么就需要2304000 * 8 = 18432000bit/s的传输速率,也就是大概需要20M带宽才能满足这路视频信号!!就国内这网速,想都不用想。那就只能采用一定的压缩算法了。

    那么用什么压缩算法呢?视频的工业标准应该是MPEG-4(我不是学视频编码的,说的不对的还望大家见谅)。压缩算法,更准确的说是压缩库也有很多,X264、XVID、DIVX等等。我用的是XVID。这东西我看了一天多,才发现有多蛋疼。竟然没有一个像样的官方文档!不过也确实,光那些多如牛毛的参数都列举不完,如果不是有点理论基础的,还真难玩的转。网上貌似只有一个中文的文档,http://wenku.baidu.com/view/d20e5b0e7cd184254b35351c.html说的倒是挺清楚,但是连个像样的范例都没举。让我去看源代码中sample,又没那么大耐性。耐着性子看完了那个文档,比着他说的套路写下来,发现死活不管用,调了半天,也不知道到底哪个参数没设置好。这还真郁闷。算了,看别人写的吧。还真是好人多,发现网上有个哥们写的http://blog.csdn.net/markman101/article/details/5713034,不得不说,还是好人多啊!我果断给他回复,好人一生平安!真是好东西,谁用谁知道,连接口都写好了。

    还真不得不说,这个视频实现,大部分代码还都是抄别人的

。不过,拿来注意也没什么不好。毕竟时间有限,容不得我去把每一个模块都自己一一实现了。剩下的就是用别人的代码实现自己的功能了。

    我统计了一下,用他的压缩设置,我的视频压缩率在77~78之间,这样算下来,我只要300K左右的带宽就可以支持一路视频信号,还算比较理想。

    接下来还有一个问题,传输协议的问题。TCP实时性不好,UDP可靠性不好,并且容易出现乱序。比较标准的选择应该是RTP协议。关于这些协议的特性,可以百度百度,详细了解一下。RTP协议可以自己实现,当然,懒人自然是懒办法,用别人的库呗。我用的是JRTP库。

   

    在发送端,先从设备中提取原始的帧数据,然后用编码器编码,每个帧压缩完有几K,但是RTP包支持的最大大小是1000多个字节(可能是为了保持实时性),所以就需要拆分每个帧。我是这样做的,每个包的负载的第一个字节标示是否是一个帧的结束,如果是,则标为0,否则标为1。发送部分代码如下:

int bitToSend = videoSender->bitLen;
int bitHaveSend = 0;
unsigned char buff[1024];
while(bitToSend > 1000)
{
buff[0] = 1;
memcpy(buff+1, videoSender->bitstream + bitHaveSend, 1000);
videoSession.SendPacket(buff, 1001, 0,false,0);
bitToSend -= 1000;
bitHaveSend += 1000;
}
buff[0] = 0;
memcpy(buff+1, videoSender->bitstream + bitHaveSend, bitToSend);
int status = videoSession.SendPacket(buff, 1001, 0,false,0);


    在接收端就循环接收,当收到包的负载的第一个字节是0的时候,就将已接收到的所有数据发送给解码器,然后解码器解码并显示出来。

    关于不定长数据,还要考虑循环缓冲区接收的问题。

if(writePos + pack->GetPayloadLength() - 1 > IN_BITSTREAM_SIZE )
{
memcpy(recvBuff + writePos, tempBuff + 1, IN_BITSTREAM_SIZE - writePos);
memcpy(recvBuff, tempBuff + 1 + IN_BITSTREAM_SIZE - writePos, pack->GetPayloadLength() - 1 - IN_BITSTREAM_SIZE + writePos);
writePos = (writePos + pack->GetPayloadLength() -1) % IN_BITSTREAM_SIZE;
}
else
{
memcpy(recvBuff + writePos, tempBuff + 1, pack->GetPayloadLength() - 1);
writePos +=  pack->GetPayloadLength() - 1;
}


     这就是整个视频聊天的大概流程。说的有点笼统。但是自己写的代码又太烂了,不能模块化到直接拿出来就能供大家使用。我只能说说我的经验以及我用到的比较好的资源。希望我提到的这些资源和思路能帮到大家。

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐