如何在两台机器之间实现实时语音传送(局域网中),有源码最好,给点思路也行(200分)

  • 主题发起人 主题发起人 yxyyyy
  • 开始时间 开始时间
Y

yxyyyy

Unregistered / Unconfirmed
GUEST, unregistred user!
如何在两台机器之间实现实时语音传送(局域网中),有源码最好,给点思路也行
 
要实现点对点语音通信,原理非常简单,只要针对一个点实现话音的实时采集、处理、播放,同时能进行可靠的传送和接收,这样两点一连便可通话。
在点对点网络传输方面,选择面向连接的TCP协议较好,TCP传输协议自动处理分组丢失和交付失序问题,这样我们不用为这些问题操心,只需很好地利
用这个连接,对于话音的实时采集、处理、回放,采用Windows MDK的低层音频服务比较合适,因为低层音频服务中的回调机制为我们提供了很大的方便。
Windows低层波形音频数据块结构 WAVEHDR,其声明如下:
type struct{
LPSTR lpData;
//指向锁定的数据缓冲区的指针
DWORD dwBufferLength;
//数据缓冲区的大小
DWORD dwByteRecorded;
//录音时指明缓冲区中的数据量
DWORD dwUser;
//用户数据
DWORD dwFlag;
//提供缓冲区信息的标志
DWORD dwLoops;
//循环播放的次数
struct wavehdr_tag * lpNext;
//保留
DWORD reserved;
//保留
} WAVEHDR;


实现步骤:
1.初始化操作
①用waveInGetNumDevs()和waveOutGetNumDevs()查看当前系统波形音频输入、输出设备;
②按11025Hz,16Bit,单声道,22K/S的格式设置WAVEFORMATEX结构的成员变量,也可以改为其他WAVE格式;
③用waveInOpen(...) 和waveOutOpen(...)分别调用WAVE_FORMAT_QUERY参数查看波形输入设备是否支持所设定的格式;
④再次用waveInOpen(...) 和waveOutOpen(...)分别调用CALLBACK_WINDOW参数打开波形输入设备;
⑤分别给音频数据块和音频数据缓冲区分配、锁定全局内存;
⑥初始化音频数据块结构各成员变量,主要是将每个缓冲区指针赋给对应数据块结构中的缓冲区指针变量lpData;调用waveInPrepareHeader(...)和
waveInAddBuffer(...)将音频数据块赋给输入设备驱动程序;
⑦调用waveInStart(...)函数开始录音。
2.消息操作
录音开始后,每当有采样数据填满数据块后,设备驱动程序就会发消息MM_WIM_DATA给用户窗口,相应的消息回调函数OnMmWimData(...)对数据块中的
采样数据进行处理,然后就可以发送给输出设备进行回放,每当一个音频数据块播放完毕,设备驱动程序又会发出消息MM_WOM_DONE,相应的消息回调
函数 OnMmWomDone(...)记录音频数据并经必要准备后重新发送给输入设备,以准备接收后续的采样数据。这样,最初为输入设备准备的音频数据块就
在消息的控制下,在输入、输出设备间循环使用,无需人为控制实现了实时采集、处理和播放。

当结束通话时要关闭音频输入设备,这时音频设备驱动程序会发送MM_WIM_CLOSE消息,可在相应的消息函数OnMmWimClose(..)中清除赋给输入、输出设备的音频数据块。


 
二、基于TCP协议的点对点话音传输
对于声音的传送和接收主要是采用面向连接的TCP协议,并用Windows Socket进行网络编程实现,但首先要将发送和接收的函数接口放在 OnMmWimData(...)函数中,这样才能做到采集数据块填满后被发送,接收的数据收到后被播放。 Windows Socket对于从事过网络编程的人
来说应该不陌生,因为我们要实现点对点通信,所以得把客户和服务器模式融合为一种模式,让服务器可以做客户,客户也可以做服务器,从而使双方都有呼叫对方和接受对方呼叫的能力,这只需增加一个监听Socket就行了。一旦呼叫连接建立成功,便在两个点之间建立了
一个数据流,即使双方不讲话,每个点也在不停地收、发数据,一方有话音自然就随着这个数据流传给了对方,所以关键的问题就是怎样读取话音数据流,因为TCP提供的流式服务是不保证边界的,当发送方想一次发送 4000个字节时,调用语句Send(sBuffer,4000),并不能
保证一定能发送出4000个字节;同样,接收方准备一次接收发过来的数据,调用语句Receive(rBuffer,4000),也不能保证一定能接收4000个字节,因此实际一次发送和接收的字节数会是1到4000中的任何一个值,最坏的情况是只有1个字节。相反,如果用Send函数连续发送少
量数据,比如一次发送400个字节,连续发送10次,接收方用Receive函数可能一次就把这4000个字节都接收下来了,而为了实现播放,我们希望调用一次发送函数就能把缓冲区大小的话音数据发送出去,调用一次接收函数就能把对方一次发送的话音数据准确接收下来,以便
进行播放,所以一种比较简单实用的办法,就是利用TCP协议发送数据时为每个数据包加个标志头: 这个标志头包含长型(4字节)的话音数据量值和一个标志字符串,程序中可为这两项标志相应设置偏移量,同时也为话音数据设置偏移量,通过重载OnReceive(...)进行接
收。开始接收前,偏移量都置0,接收开始后先检测是否收到了4 个字节的话音数据量大小值和字符串标志,如果没有收到,则通过偏移量来控制将它们准确收到,之后校验字符串标志的正确性,如果两项标志都正确收到了,则按收到的数据量大小值进行真正的话音数据接
收,如果在OnReceive函数中一次调用Receive(...)接收没有达到这个值,则采用非阻塞模式,调用AsyncSelect(...)函数继续接收,直至全部收到,这样重载 OnReceive(...)接收函数后就可以一次接收对方发过来的数据;同理,我们重载CAsyncSocket 的OnSend函数,也可
以实现一次发完一个缓冲区中的数据。这样只要将收发函数的接口放在OnMmWimData(...)函数中,使收和发都产生恒定速率的数据流,从而实现了网络话音的传输和回放。
 
可以用acm实现.
 
流程:
采集(ACM)-压缩(MP3,G.723...)-传输(TCP/UDP...)
 
查以前的贴子, 讨论过多次了
 
关注:
jingwei@goldeastpaper.com.cn
 
有这么烦吗?
参观一下DirectPlay8.0的例子,有源代码(C++)
 
最好采用DirectSound,如果用WaveIn 和WaveOut的话在声音回放时如果回放缓冲区
设的太小(小于100ms)程序容易会阻塞在WaveOut中。
 
[8D]http://www.banasoft.com/AVPhone.htm
 
接受答案了.
 
后退
顶部