对讲开发关键技术总结
1,对讲部署环境
对讲分为前端(设备端)和后端(PC端)。
前后端要根据制定的数据通信协议,执行各种功能,包括:
1) 设备端发起呼叫
2) PC端接听呼叫或拒接呼叫
3) 设备端挂断
4) PC端挂断
5) 语音数据的获取、收发、播放
1.1设备端
l 设备端有按钮可以发起对讲呼叫。对讲中按按钮,则可以结束对讲。
l 设备端有麦克风和喇叭,分别用于拾取语音和播放语音。
l 设备端有控制板,将麦克风拾取的语音发送给PC端,同时接收PC端发来的语音进行播放。
l 语音数据采用定长包传输,如500或1000个字节为一个包。
l 对讲对语音的质量要求不高,麦克风可以采用16K采样率、单声道、8位数据存储。
1.2 PC端
l PC端为一个运行于windows系统的程序
l PC端监听设备呼叫,可以接听或拒接。对讲中,可以主动结束对讲。
l PC端有麦克风和喇叭,分别用于拾取语音和播放语音。
l PC端将麦克风拾取的语音发送给设备端,同时接收设备端发来的语音进行播放。
l 语音采样采用16K采样率、单声道、8位数据存储。
2,PC端关键技术
PC端程序开发的关键技术如下:
1) 麦克风语音数据流的读取和发送
2) 设备端语音数据流的接收和播放
3) windows消息传递
以下分别说明。
2.1 麦克风语音数据流的读取和发送
采用TSoundCaptureStream拾取PC麦克风数据
在CaptureFilledBuffer中,发送PC端语音到设备
FCapture: TSoundCaptureStream; FCapture.Stop; FCapture.BufferLength := VP_DATA_SIZE; FCapture.OnFilledBuffer := CaptureFilledBuffer; FCapture.CaptureFormat := 16; //16K SamplesPerSec, 8 BitsPerSample, 1 Channels
2.2 设备端语音数据流的接收和播放
1) 采用directSound播放接收到的语音数据流。
2) 采用TWaveStream作为设备语音播放缓冲区。
FormVoice:= TFormVoice.Create(nil); MemoryStream := TMemoryStream.Create; MemoryStream.SetSize(VP_DATA_SIZE); FWaveStreamIn := TWaveStream.Create(MemoryStream); FWaveStreamIn.SetPCMFormat(16000,8,1); FWaveStreamIn.Size := VP_DATA_SIZE; FWaveStreamIn.Open(True); Audio := TAudioStream.Create(FormVoice.DXSound.DSound); Audio.WaveStream := FWaveStreamIn; Audio.Looped := True; Audio.AutoUpdate := True; Audio.BufferLength := VP_DATA_SIZE;
注意:以上Audio.Looped := True;这一句是关键。如果流式播放不设置Loop属性为True,则PC端播放的语音会出现连续性的噪音。
参见:(19条消息) DirectSound开发指南_newrtc的博客-CSDN博客
3) 接收到设备发来的语音包时,将数据写入缓冲区,并播放。
FWaveStreamIn.Position := 0; FWaveStreamIn.Writebuffer(vBuff[8], VP_DATA_SIZE); Audio.Play;
2.3 windows消息传递
PC端程序有时免不了要进行postMessage操作,但如果对讲功能是封装在DLL中的话,消息传递的Handle需要自己创建。
1) 在工程文件中调用CreateHandle
library YYY; uses SysUtils, Classes, Forms, 。。。 exports XXX ; begin Application.Initialize; if Application.Handle = 0 then begin Application.CreateHandle; end; Application.Run; end.
2) 在单元类中创建消息队列、发送和接收消息
var DllWndHandle: HWND = 0; constructor TOutIntf.Create(CameraIP:PChar); begin DllWndHandle := AllocateHWnd(DllWndProc); end; destructor TOutIntf.destroy; begin if DllWndHandle <> 0 then begin DeallocateHWnd(DllWndHandle); DllWndHandle := 0; end; inherited; end; //发送消息给DLL自己 PostMessage(DllWndHandle, WM_USER, 0, 0); //处理消息 procedure TOutIntf.DllWndProc(var Message: TMessage); begin if (Message.Msg = WM_USER) then begin //do something end else Message.Result := DefWindowProc(DllWndHandle, Message.Msg, Message.WParam, Message.LParam); end;
需要注意的是,上述第一步如果不做的话,编译并无错误,但是程序运行将异常。