3.4.2语音回放
相对录音而言,播放就简单多了,同样用的一个CWinThread对象CAudioPlay来实现,部分实现代码:
LRESULT CAudioPlay::OnWriteSoundData(WPARAM wParam, LPARAM lParam) { //
TRACE(\
MMRESULT mmResult = FALSE; char *p=NULL;
int length=(int) wParam;
if(Playing==FALSE) return FALSE; if(length<=0)
return FALSE;
WAVEHDR *lpHdr=new WAVEHDR; if(!lpHdr)
return FALSE;
p=new char [length]; if(!p) {delete lpHdr; return FALSE;}
ZeroMemory(lpHdr,sizeof(WAVEHDR)); ZeroMemory(p,length);
CopyMemory(p,(char*)lParam,length); lpHdr->lpData=p;
lpHdr->dwBufferLength = length;
mmResult = ::waveOutPrepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR)); //为回放设备准备内存块 if(mmResult){ delete lpHdr;delete p; return mmResult;}
mmResult = ::waveOutWrite(m_hPlay, lpHdr, sizeof(WAVEHDR));//写数据(放音) if(mmResult){delete lpHdr;delete p; return mmResult; }
m_Count++;
return MMSYSERR_NOERROR; }
3.5音视频发送与接收
视频采集采用AVICap从视频采集卡捕获视频图像,得到的是位图形式的视频帧,然后用Divx编码器进行压缩,压缩以后形成以帧为格式的Mpeg4流。通过Winsock实现压缩后的视频数据在局域网中的实时传输,接收完的数据交给Divx解码器,以帧的格式解压,最后实现视频显示。所以提出以帧为单位发送视频数据流。为了在接收端能够方便地提取出一帧,提出如表1所示的格式组建帧。完整的一帧由5个字段组成,各个字段的意义如下:帧开始标志:标志着一帧地开始,占用4个字节的空间;帧大小:表示整个帧的大小,包括5个字段的大小,占用4个字节的空间;帧编号:表示帧的顺序编号,占用4个字节的空间;帧类型:标志此帧是否是关键帧,占用1个字节的空间;帧数据:存放压缩后一帧的完整数据。
3.5.1音视频发送与接收模块流程图
音视频发送与接收模块流程图如图8所示。
图8 音视频发送与接收模块流程图
3.5.1音视频发送与接收代码
不妨假设创建的线程名为sendThread,发送代码如下: while(1){
isOK=true; //准备就绪
SuspendThread(sendThread); //挂起线程
isOK=false; //线程正在发送数据 int length=frameLength; //待发数据长度 if(length<50000) {//判断数据是否正常 int n=0;
int sendCount=0; while(length>0) {
n=send(sock,(char*)imageBuf+sendCount,length,0); //发送数据, //imageBuf是指针,指向待发数据帧
if(n==SOCKET_ERROR) //网络出现异常,则退出线程 break; length-=n; sendCount+=n;}} }
接收端创建一个线程专门用来执行数据接收。不妨假设线程名为recThread,核心代码实现如下:
while(temp!=SOCKET_ERROR){
if(!isStart) {//帧数据是否开始,true表示开始 if(endNum>3) //endNum纪录当前接收未处理的数据 endNum=0;
temp=recv(clisock,(char*)(recBuf+endNum),1000,0);//从缓冲区读取数据 startPos=serchStr(temp+endNum); //查找帧开始标志 if(startPos!=-1) { isStart=true;
endNum=temp+endNum-startPos-4;
memcpy(imageBuf,recBuf+startPos+4,endNum); //保存帧数据} else{
memcpy(recBuf,recBuf+temp+endNum-3,3);//保存最后三个字节的数据 endNum=3;} }
else{
if(endNum<4) {//判定紧跟开始标志的数据,如果小于4表示不能获得帧大小 temp=recv(clisock,(char*)(recBuf),1000,0); //读入数据 memcpy(imageBuf+endNum,recBuf,temp);//保存数据 endNum+=temp; if(endNum<4) continue;
frameSize= *((int*)imageBuf);//获得帧大小
if(frameSize<500 || frameSize>50000) {//异常处理(帧大小非法) isStart = false; //丢弃数据重新查找帧开始标志 endNum = 0; continue;}
frameSize-=endNum+4;} else{
while(frameSize>0&&temp!=SOCKET_ERROR) {//获得完整帧的剩余数据temp=recv(clisock,(char*)(imageBuf+endNum),frameSize,0); endNum+=temp; frameSize-=temp;}
if(frameSize<=0) {//帧结束置位,解压 isStart=false; endNum=0;
deCompress();//判断数据的有效性,调用ICDecompress进行解压}}} }

