wpf 语音通话_WPF+WCF一步一步打造音频聊天室(三):语音聊天

前一篇文章中实现了文字聊天和共享白板的功能,这篇文章中,我将在前一篇文章的基础上实现语音聊天的功能。语音聊天要比文字聊天和共享白板难度要大一点。

实现的大概的流程为:

1、一个聊天室成员向另外一个成员发起语音聊天请求

2、这个请求将被送至WCF服务端,WCF的双工通知被邀请人。

3、被邀请人接到通知,他可以选择接受或者拒绝语音聊天的请求。

4、如果拒绝,将通知请求者拒绝语音聊天

5、如果同意,邀请者和被邀请者的客户端将进行语音聊天,此时客户端会开启一个播放声音和接受声音的线程。这里用到了一个开源的wave类库,在http://www.lumisoft.ee/lswww/download/downloads/Examples/可以下载。声音的通信使用到了UDPClient 类。这个类使用 UDP 与网络服务通讯。UDP 的优点是简单易用,并且能够同时向多个地址广播消息。UdpClient 类提供了一些简单的方法,用于在阻止同步模式下发送和接收无连接 UDP 数据报。因为 UDP 是无连接传输协议,所以不需要在发送和接收数据前建立远程主机连接。但您可以选择使用下面两种方法之一来建立默认远程主机:

使用远程主机名和端口号作为参数创建UdpClient 类的实例。

创建 UdpClient 类的实例,然后调用 Connect 方法。

可以使用在UdpClient 中提供的任何一种发送方法将数据发送到远程设备。使用 Receive 方法可以从远程主机接收数据。

这篇文章使用了Receive 方法从客户端接受数据。然后通过WCF中存储的IP地址,通过Send方法将其发送给客户端。

下面我将在前一篇文章的基础上实现这个语音聊天的功能。首先在客户端添加声音管理的类CallManager,这个类使用到了开源的wave类库,代码如下:

public class CallManager

{

private WaveIn _waveIn;

private WaveOut _waveOut;

private IPEndPoint _serverEndPoint;

private Thread _playSound;

private UdpClient _socket;

public CallManager(IPEndPoint serverEndpoint)

{

_serverEndPoint = serverEndpoint;

}

public void Start()

{

if (_waveIn != null || _waveOut != null)

{

throw new Exception("Call is allready started");

}

int waveInDevice = (Int32)Application.UserAppDataRegistry.GetValue("WaveIn", 0);

int waveOutDevice = (Int32)Application.UserAppDataRegistry.GetValue("WaveOut", 0);

_socket = new UdpClient(0); // opens a random available port on all interfaces

_waveIn = new WaveIn(WaveIn.Devices[waveInDevice], 8000, 16, 1, 400);

_waveIn.BufferFull += new BufferFullHandler(_waveIn_BufferFull);

_waveIn.Start();

_waveOut = new WaveOut(WaveOut.Devices[waveOutDevice], 8000, 16, 1);

_playSound = new Thread(new ThreadStart(playSound));

_playSound.IsBackground = true;

_playSound.Start();

}

private void playSound()

{

try

{

while (true)

{

lock (_socket)

{

if (_socket.Available != 0)

{

IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);

byte[] received = _socket.Receive(ref endpoint);

// todo: add codec

_waveOut.Play(received, 0, received.Length);

}

}

Thread.Sleep(1);

}

}

catch (ThreadAbortException)

{

}

catch

{

this.Stop();

}

}

void _waveIn_BufferFull(byte[] buffer)

{

lock (_socket)

{

//todo: add codec

_socket.Send(buffer, buffer.Length, _serverEndPoint);

}

}

public void Stop()

{

if (_waveIn != null)

{

_waveIn.Dispose();

}

if (_waveOut != null)

{

_waveOut.Dispose();

}

if (_playSound.IsAlive)

{

_playSound.Abort();

}

if (_socket != null)

{

_socket.Close();

_socket = null;

}

}

}

在服务端添加将接受到的声音,发送给接受者的类,使用到了UDPClient类:

public class UdpServer

{

private Thread _listenerThread;

private List _users = new List();

private UdpClient _udpSender = new UdpClient();

public IPAddress ServerAddress

{

get;

set;

}

public UdpClient UdpListener

{

get;

set;

}

public UdpServer()

{

try

{

ServerAddress = IPAddress.Parse("127.0.0.1");

}

catch

{

throw new Exception("Configuration not set propperly. View original source code");

}

}

public void Start()

{

UdpListener = new System.Net.Sockets.UdpClient(0);

_listenerThread = new Thread(new ThreadStart(listen));

_listenerThread.IsBackground = true;

_listenerThread.Start();

}

private void listen()

{

while (true)

{

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);

byte[] received = UdpListener.Receive(ref sender);

if (!_users.Contains(sender))

{

_users.Add(sender);

}

foreach (IPEndPoint endpoint in _users)

{

if (!endpoint.Equals(sender))

{

_udpSender.Send(received, received.Length, endpoint);

}

}

}

}

public void EndCall()

{

_listenerThread.Abort();

}

}

在WCF服务中添加两个方法:初始化语音通信和结束语音通信。

[OperationContract(IsOneWay = false)]

bool InitiateCall(string username);

[OperationContract(IsOneWay = true)]

void EndCall();

具体是实现代码:

public bool InitiateCall(string username)

{

ClientCallBack clientCaller = s_dictCallbackToUser[OperationContext.Current.GetCallbackChannel()];

ClientCallBack clientCalee = s_dictCallbackToUser.Values.Where(p => p.JoinChatUser.NickName == username).First();

if (clientCaller.Callee != null || clientCalee.Callee != null) // callee or caller is in another call

{

return false;

}

if (clientCaller == clientCalee)

{

return false;

}

if (clientCalee.Client.AcceptCall(clientCaller.JoinChatUser.NickName))

{

clientCaller.Callee = clientCalee.Client;

clientCalee.Callee = clientCaller.Client;

clientCaller.UdpCallServer = new UdpServer();

clientCaller.UdpCallServer.Start();

EmtpyDelegate separateThread = delegate()

{

IPEndPoint endpoint = new IPEndPoint(clientCaller.UdpCallServer.ServerAddress,

((IPEndPoint)clientCaller.UdpCallServer.UdpListener.Client.LocalEndPoint).Port);

clientCalee.Client.CallDetailes(endpoint, clientCaller.JoinChatUser.NickName, username);

clientCaller.Client.CallDetailes(endpoint, clientCaller.JoinChatUser.NickName, username);

foreach (var callback in s_dictCallbackToUser.Keys)

{

callback.NotifyMessage(String.Format("System:User \"{0}\" and user \"{1}\" have started a call",clientCaller.JoinChatUser.NickName, username));

}

};

separateThread.BeginInvoke(null, null);

return true;

}

else

{

return false;

}

}

public void EndCall()

{

ClientCallBack clientCaller = s_dictCallbackToUser[OperationContext.Current.GetCallbackChannel()];

ClientCallBack ClientCalee = s_dictCallbackToUser[clientCaller.Callee];

if (clientCaller.UdpCallServer != null)

{

clientCaller.UdpCallServer.EndCall();

}

if (ClientCalee.UdpCallServer != null)

{

ClientCalee.UdpCallServer.EndCall();

}

if (clientCaller.Callee != null)

{

foreach (var callback in s_dictCallbackToUser.Keys)

{

callback.NotifyMessage(String.Format("System:User \"{0}\" and user \"{1}\" have ended the call", clientCaller.JoinChatUser.NickName, ClientCalee.JoinChatUser.NickName));

}

clientCaller.Callee.EndCallClient();

clientCaller.Callee = null;

}

if (ClientCalee.Callee != null)

{

ClientCalee.Callee.EndCallClient();

ClientCalee.Callee = null;

}

}

还有部分做了修改的代码见附件代码。

下面看下演示的截图:

1、两个用户登录:

2、选择小花,点击按钮。麒麟向小花同学发起语音聊天:

3、小花同学接受通知,选择是:

4、弹出通话中的窗体,双发都可以选择结束通话:

5、结束通话之后,消息框中会广告消息

总结:

这个聊天程序主要都是用到了WCF的双工通信。没有用两台机子测试,我在我的笔记本上开了一个服务端和一个客户端,用了一个带耳麦的耳机,声音效果良好。

其实这个东西做完整还有很多细活,这个只是Demo,非常的粗糙。最近会很忙,期待将来的某一天能空去完善。如果实现了视频的功能我会写第四篇的。

相关文章:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值