Library core部分。
第四步:一个语音会议的demo
最后我们来做一个局域网的语音会议的demo,也算做个入门结业。
我们的语音会议的demo设计为这样:没有服务器,会议中每一方都可以listen来自局域网的声音,也可以往自己的列表中添加目标位置,向它(们)发送你的语音。
这样要完成一个三人的对话,我们需要三方都打开listen,并且添加另外两个人的地址,发送自己的声音。
我们开两个独立的线程,一个负责在端口12000接受来自局域网的声音,一个负责往发送列表中的目的地发送本地语音。
之前我们的例子中只是在本地做了一个wav文件的播放,我们需要解决另外几个问题:得到声卡输入、如何向目的地发送RTP、如何接收RTP。
第一个问题比较好解决,我们仿照例子中声卡播放使用的对象,找到了这个类:MIPWAVInput,把它放在某个chain的最开始处,我们就可以得到声音的输入了。
代码类似于下面:
- MIPWinMMInput sndCardInput;
-
- bool returnValue;
-
- int samplingRate = 8000;
- int numChannels = 1;
- returnValue=sndCardInput.open(samplingRate,numChannels,interval,buffertime,false);
在初始化chain时候:
- chain.setChainStart(&sndCardInput);
就可以了。
RTP的发送和接收主要是对于RTPsession的参数设置。接收端代码我们设置端口为监听端口,发送端SetPortbase(portBase)为发送端口,而在RTPSession::AddDestination函数中端口使用目的地的监听端口;
我们的发送语音的线程大致是这样的:
- DWORD WINAPI WorkerThread(LPVOID lpParameter)
- {
- IpAddrList *ipl=(IpAddrList*)lpParameter;
-
- WSADATA dat;
- WSAStartup(MAKEWORD(2,2),&dat);
-
- MIPTime interval(0.020);
- MIPTime buffertime(10);
- MIPAverageTimer timer(interval);
- MIPWAVInput sndFileInput;
- MIPSamplingRateConverter sampConv;
- MIPSampleEncoder sampEnc;
- MIPULawEncoder uLawEnc;
- MIPRTPULawEncoder rtpEnc;
- MIPRTPComponent rtpComp;
-
-
- MIPWinMMInput sndCardInput;
- MIPWinMMOutput sndCardOutput;
- MyChain chain("MPVC");
- RTPSession rtpSession;
- bool returnValue;
-
- int samplingRate = 8000;
- int numChannels = 1;
-
-
- returnValue=sndCardInput.open(samplingRate,numChannels,interval,buffertime,false);
- returnValue = sampEnc.init(MIPRAWAUDIOMESSAGE_TYPE_S16);
- returnValue = uLawEnc.init();
- returnValue = rtpEnc.init();
-
- RTPUDPv4TransmissionParams transmissionParams;
- RTPSessionParams sessionParams;
- int portBase = BASEPORT_SEND;
- int status;
-
- transmissionParams.SetPortbase(portBase);
- sessionParams.SetOwnTimestampUnit(1.0/((double)samplingRate));
- sessionParams.SetMaximumPacketSize(64000);
-
- status = rtpSession.Create(sessionParams,&transmissionParams);
-
- for(int i=0;i<ipl->count;i++)
-
- {
- status = rtpSession.AddDestination(RTPIPv4Address(ntohl(inet_addr(ipl->ipaddr[i]))
- ,BASEPORT_LISTEN));
- }
-
- returnValue = rtpComp.init(&rtpSession);
- returnValue = chain.setChainStart(&sndCardInput);
- returnValue = chain.addConnection(&sndCardInput, &sampEnc);
- returnValue = chain.addConnection(&sampEnc, &uLawEnc);
- returnValue = chain.addConnection(&uLawEnc, &rtpEnc);
- returnValue = chain.addConnection(&rtpEnc, &rtpComp);
- returnValue = chain.start();
-
- while(stop_f)
- {
- Sleep(SLEEP_TIME);
- }
-
- rtpSession.BYEDestroy(RTPTime(10,0),0,0);
- returnValue = chain.stop();
- rtpSession.Destroy();
-
- WSACleanup();
- return 0;
- }
listen端的代码类似下面:
- DWORD WINAPI WorkerThread_Listen(LPVOID lpParameter)
- {
- WSADATA dat;
- WSAStartup(MAKEWORD(2,2),&dat);
-
- MIPTime interval(0.020);
- MIPTime buffertime(10);
- MIPAverageTimer timer(interval);
- MIPWAVInput sndFileInput;
- MIPSamplingRateConverter sampConv, sampConv2;
- MIPSampleEncoder sampEnc, sampEnc2, sampEnc3;
- MIPULawEncoder uLawEnc;
- MIPRTPULawEncoder rtpEnc;
- MIPRTPComponent rtpComp;
- MIPRTPDecoder rtpDec;
- MIPRTPULawDecoder rtpULawDec;
- MIPULawDecoder uLawDec;
- MIPAudioMixer mixer;
-
- MIPWinMMOutput sndCardOutput;
- MyChain chain("MPVClisten");
- RTPSession rtpSession;
- bool returnValue;
-
- int samplingRate = 8000;
- int numChannels = 1;
-
-
- RTPUDPv4TransmissionParams transmissionParams;
- RTPSessionParams sessionParams;
-
- int portBase = BASEPORT_LISTEN;
- int status;
-
- transmissionParams.SetPortbase(portBase);
- sessionParams.SetOwnTimestampUnit(1.0/((double)samplingRate));
- sessionParams.SetMaximumPacketSize(64000);
- status = rtpSession.Create(sessionParams,&transmissionParams);
-
- returnValue = rtpComp.init(&rtpSession);
- returnValue = rtpDec.init(true, 0, &rtpSession);
- returnValue = rtpDec.setPacketDecoder(0,&rtpULawDec);
- returnValue = uLawDec.init();
- returnValue = sampEnc2.init(MIPRAWAUDIOMESSAGE_TYPE_FLOAT);
- returnValue = sampConv2.init(samplingRate, numChannels);
- returnValue = mixer.init(samplingRate, numChannels, interval);
- returnValue = sndCardOutput.open(samplingRate, numChannels, interval);
- returnValue = sampEnc3.init(MIPRAWAUDIOMESSAGE_TYPE_S16LE);
- returnValue = chain.setChainStart(&timer);
- returnValue = chain.addConnection(&timer, &rtpComp);
- returnValue = chain.addConnection(&rtpComp, &rtpDec);
- returnValue = chain.addConnection(&rtpDec, &uLawDec, true);
- returnValue = chain.addConnection(&uLawDec, &sampEnc2, true);
- returnValue = chain.addConnection(&sampEnc2, &sampConv2, true);
- returnValue = chain.addConnection(&sampConv2, &mixer, true);
- returnValue = chain.addConnection(&mixer, &sampEnc3);
- returnValue = chain.addConnection(&sampEnc3, &sndCardOutput);
-
-
- returnValue = chain.start();
-
- while(lstop_f)
- {
- Sleep(SLEEP_TIME);
- }
-
- rtpSession.BYEDestroy(RTPTime(10,0),0,0);
- returnValue = chain.stop();
- rtpSession.Destroy();
-
- WSACleanup();
- return 0;
- }
其他的一些UI方面的事情大家随意吧...
你要是在你的局域网里面已经已经成功实现了一个语音会议demo...呵呵,恭喜你。我的文章就先写到这里。更高级的...等我会了就写。
大家如果在上面这章有什么问题,可以看看 源代码src\sessions目录下的mipaudiosession.cpp,里面有MIPAudioSession类的实现,它对我们理解这个chain很有帮助。
接下来,我要去搞视频了,妈呀~前几天搞了搞QT...现在比较头疼4版本和3版本之间的一些隔阂。
(arden1019) |