日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

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

發布時間:2024/9/19 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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,非常的粗糙。最近會很忙,期待將來的某一天能空去完善。如果實現了視頻的功能我會寫第四篇的。

相關文章:

總結

以上是生活随笔為你收集整理的wpf 语音通话_WPF+WCF一步一步打造音频聊天室(三):语音聊天的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。