实时语音通信的实现
来源:互联网 发布:ios商城购物app源码 编辑:程序博客网 时间:2024/04/27 16:23
实时语音通信的实现
作者:解放军炮兵学院 十四队 孔康
下载源代码
引言
本人虽已学习VC++一年半载,仍觉捉襟见肘,好在有VCKBASE的帮忙,确实学到了不少东西,www.vckbase.com也成了我每次上民网必到之处(阁下有所不知,鄙人接受最为严格的管理,上民网是要申请的)。近日在做一个通信 方面的程序,实时的语音和视频通信当然是大家所喜欢的。本文将向您展示局域网环境下实时语音通信的的一个解决方案(视频这一块正在做,估计很快就能出炉),Winxp环境下测试效果良好,并且具有网络 拥塞处理机制,您不妨一看。
本文以第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢。雷同之处将不再赘述,主要做了以下发展:
例子程序运行画面:
下面且看我细细道来:
(一)首先定义了一个声音数据“块”
struct CAudioData{PBYTE lpdata; //指向语音数据,注意这里内存区域是动态申请释放的DWORD dwLength;//语音数据长度}接下来申明两个循环队列和相关指针。
//InBlocks,OutBlocks非别为两个常数CAudioData m_AudioDataIn[InBlocks],m_AudioDataOut[OutBlocks];int nAudioIn, nSend, //录入、发送指针 nAudioOut, nReceive;//接收、播放指针// 对于录音和放音都存在和网络的同步问题,主要靠这些指针进行协调
讨论:如图所示,几个指针的相互追逐,这种机制在处理网络拥塞上应该有普遍的应用意义
接收端:因为录、放音的采样频率设置为相等,故不可能出现 nReceive 在n AudioOut 之后,
即收到的声音文件太多,来不及播放的现象。
发送端:nAudioIn 绕一圈反追上 nSend,于是将数据接在当前块的尾部,以待发送
接收端:nAudioOut 追上 nReceive 后,发现没有数据可播放了,就“空转”。
综合以上情况,相关实现如下:
(二)声音的录制与播放
(1)录音处理
void CRecTestDlg::OnMM_WIM_DATA(UINT wParam,LONG lParam){ int nextBlock = (nAudioIn+1)% InBlocks;if(m_AudioDataIn[nextBlock].dwLength!=0)//下一“块”没发走{ //把PWAVEHDR(即pBUfferi)里的数据接到当前“块”的末尾 m_AudioDataIn[nAudioIn].lpdata = (PBYTE)realloc (m_AudioDataIn[nAudioIn].lpdata , (((PWAVEHDR) lParam)->dwBytesRecorded+m_AudioDataIn[nAudioIn].dwLength)) ;if (m_AudioDataIn[nAudioIn].lpdata == NULL){//...出错处理return ;} CopyMemory ((m_AudioDataIn[nAudioIn].lpdata+m_AudioDataIn[nAudioIn].dwLength), ((PWAVEHDR) lParam)->lpData, ((PWAVEHDR) lParam)->dwBytesRecorded) ;//(*destination,*resource,nLen);m_AudioDataIn[nAudioIn].dwLength +=((PWAVEHDR) lParam)->dwBytesRecorded; }else //把PWAVEHDR(即pBUfferi)里的数据拷贝到下一“块”中{nAudioIn = (nAudioIn+1)% InBlocks;m_AudioDataIn[nAudioIn].lpdata = (PBYTE)realloc(0,((PWAVEHDR) lParam)->dwBytesRecorded);CopyMemory(m_AudioDataIn[nAudioIn].lpdata, ((PWAVEHDR) lParam)->lpData,((PWAVEHDR) lParam)->dwBytesRecorded) ; m_AudioDataIn[nAudioIn].dwLength =((PWAVEHDR) lParam)->dwBytesRecorded;}// Send out a new bufferwaveInAddBuffer (hWaveIn, (PWAVEHDR) lParam, sizeof (WAVEHDR)) ;return ;}(2)放音处理
void CRecTestDlg::OnMM_WOM_DONE(UINT wParam,LONG lParam){ //释放播放完的缓冲区,并准备新的数据 free(m_AudioDataOut[nAudioOut].lpdata);m_AudioDataOut[nAudioOut].lpdata = reinterpret_cast<PBYTE>(malloc(1));m_AudioDataOut[nAudioOut].dwLength = 0; nAudioOut= (nAudioOut+1)%OutBlocks;((PWAVEHDR)lParam)->lpData = (LPTSTR)m_AudioDataOut[nAudioOut].lpdata ;((PWAVEHDR)lParam)->dwBufferLength = m_AudioDataOut[nAudioOut].dwLength ; waveOutPrepareHeader (hWaveOut,(PWAVEHDR)lParam,sizeof(WAVEHDR)); waveOutWrite(hWaveOut,(PWAVEHDR)lParam,sizeof(WAVEHDR)); return;}(三)套接字发送、接收线程
其实,经过刚才的讨论,现在这两个线程的运作很简单---只是循环地操作nReceive和nSend指针。首先发送(接收)声音块的长度,然后发送(接收)声音内容。注意:拿CSocket::Send(buffer,count)为例,其返回值(发送出去的字结数)只是1到count之间的某值,所以要添加检测机制,否则将出现错误,这也是socket编程必须注意的。本文是用一个循环,直到发送出去的字节总数等于“块”的长度才发送第二个数据块的信息。
例外这两个线程稍加改动即可实现多人的语音会议。
UINT Audio_Listen_Thread(LPVOID lParam){CRecTestDlg *pdlg = (CRecTestDlg*)lParam;CSocket m_Server;DWORD length;if(!m_Server.Create(4002))AfxMessageBox("Listen Socket create error"+pdlg->GetError(GetLastError()));if(!m_Server.Listen()) AfxMessageBox("m_server.Listen ERROR"+pdlg->GetError(GetLastError()));CSocket recSo;if(! m_Server.Accept(recSo))AfxMessageBox("m_server.Accept() error"+pdlg->GetError(GetLastError()));m_Server.Close();int ret ;while(1){ //开始循环接收声音文件,首先接收文件长度ret = recSo.Receive(&length,sizeof(DWORD));if(ret== SOCKET_ERROR )AfxMessageBox("服务器端接收声音文件长度出错,原因: "+pdlg->GetError(GetLastError()));if(ret!=sizeof(DWORD)){AfxMessageBox("接收文件头错误,将关闭该线程");recSo.Close();return -1;}//接下来开辟length长的内存空间pdlg->m_AudioDataOut[pdlg->nReceive].lpdata =(PBYTE)realloc (0,length);if (pdlg->m_AudioDataOut[pdlg->nReceive].lpdata == NULL){AfxMessageBox("erro memory_ReceiveAudio");recSo.Close();return -1;}else//内存申请成功,可以进行循环检测接受{DWORD dwReceived = 0,dwret;while(length>dwReceived){dwret = recSo.Receive((pdlg->m_AudioDataOut[pdlg->nReceive].lpdata+dwReceived),(length-dwReceived));dwReceived +=dwret;if(dwReceived ==length){pdlg->m_AudioDataOut[pdlg->nReceive].dwLength = length;break;}}}//本轮声音文件接收完毕pdlg->nReceive=(pdlg->nReceive+1)%OutBlocks;}recSo.Close();return 0;}UINT Audio_Send_Thread(LPVOID lParam){ CRecTestDlg *pdlg = (CRecTestDlg*)lParam;CSocket m_Client;m_Client.Create();if( m_Client.Connect("127.0.0.1",4002)){DWORD ret, length;int count=0;while(1)//循环使用指针nSend{length =pdlg->m_AudioDataIn[pdlg->nSend].dwLength;if(length !=0){ //首先发送块的长度if(((ret = m_Client.Send(&length,sizeof(DWORD))) != sizeof(DWORD))||(ret==SOCKET_ERROR)){ AfxMessageBox("声音文件头传输错误!"+pdlg->GetError(GetLastError()));pdlg->OnOK();break;}//其次发送块的内容,循环检测是否发送完毕DWORD dwSent = 0;//已经发送掉的字节数while(1)//==============================发送声音数据开始{ret = m_Client.Send((pdlg->m_AudioDataIn[pdlg->nSend].lpdata+dwSent), (length-dwSent));if(ret==SOCKET_ERROR)//检错{AfxMessageBox("声音文件传输错误!"+pdlg->GetError(GetLastError()));break;}else //发送未发送完的{dwSent += ret;if(dwSent ==length)//发送完毕,则释放当前“块”{ free(pdlg->m_AudioDataIn[pdlg->nSend].lpdata);pdlg->m_AudioDataIn[pdlg->nSend].dwLength = 0;break;}}} //======================================发送声音数据结束}pdlg->nSend = (pdlg->nSend +1)% InBlocks;}}else AfxMessageBox("Socket连接失败"+pdlg->GetError(GetLastError()));m_Client.Close();return 0;}存在的问题
Finally,Thank Candy Lee(my special friend) for her help.
- 实时语音通信的实现
- 实时语音通信的实现
- 实时语音通信的实现(VC++)
- Nanopc 使用mumble实现实时语音通信功能
- 基于WaveX低级音频函数的实时语音通信
- 基于WaveX低级音频函数的实时语音通信
- 基于WaveX低级音频函数的实时语音通信
- 基于WaveX低级音频函数的实时语音通信
- 网络语音实时通信音频下的缓冲区设计
- 基于WaveX低级音频函数的实时语音通信
- 基于WaveX低级音频函数的实时语音通信
- iOS音频编程之实时语音通信
- iOS音频编程之实时语音通信
- H5 WebSocket实现简单的实时通信
- php实现实时通信
- 使用微软的实时通信API实现客户端通信
- 三网融合情况下,实时语音通信技术的研究
- 三网融合情况下,实时语音通信技术解决之道
- 《Linux内核模块编程指南》(一)
- 主题:《Linux内核模块编程指南》(二)
- 权限管理 之一 四维权限管理模型 (转载)
- 主题:《Linux内核模块编程指南》(三)
- 主题:《Linux内核模块编程指南》(四)
- 实时语音通信的实现
- 主题:《Linux内核模块编程指南》(五)
- 主题:《Linux内核模块编程指南》(六)
- 权限管理 之二 权限管理与访问控制概要设计(转载)
- 基于VC视频采集的实现
- 主题:《Linux内核模块编程指南》(七)
- 分离权限管理与访问控制(转载)
- 浅谈权限管理的对象模型和实现
- 主题:《Linux内核模块编程指南》(七)