前面三篇文章中,我實現了音頻聊天室的部分功能,包括:文字聊天,共享白板,語音聊天。這篇文章我將敘述一下視頻會話實現的技 術要點。
在Silerlight4中已經集成了攝像和采集聲音的功能,但是在WPF4中卻沒有直接可以用的的控件,由此也可以看出,由桌面程序走向web 程序的大趨勢。如果你想用Silverlight實現類似的音頻聊天室,下面我列出一些資料供你參考。
1、 Your First Step to the Silverlight Voice/Video Chatting Client/Server:
http://www.codeproject.com/KB/silverlight/SilverlightVoiceVideoChat.aspx
2、Accessing Web Camera and Microphone:
http://www.silverlight.net/learn/videos/all/access-web-camera-microphone/
3、Record The Audio Into A Wave File:
http://cspeex.codeplex.com/
4、Playback The Wave File in Silverlight:
http://blogs.msdn.com/gillesk/archive/2009/03/23/playing-back-wave-files-in-silverlight.aspx
5、Using the G.711 Codec:
http://www.codeproject.com/KB/security/g711audio.aspx
6、convert encode and decode silverlight:
http://kodierer.blogspot.com/2009/11/convert-encode-and-decode-silverlight.html
上面是Silverlight實現的方案和資料。這篇文章是用WPF+WCF去實現的。列出Silerlight是方便大家有個對照。
視頻會話實現的方式和語音通話實現的方式是一樣的。他們之間不一樣的地方在於,一個是通過麥克風獲取數據,一個是通過攝像頭獲 取數據。下面我用WF4畫了一個流程圖(這個流程圖只是為了說明問題,沒有用到程序裡面)。
實現
前面 說到了,WPF中沒有像Silerlight一樣集成了攝像的功能,在WPF中又如何去實現攝像呢?這也是首先要解決的問題,我經過一番google, 在Codeplex上找到了一個開源WPF的Webcam控件。地址是:WebCam control for WPF。
添加一個窗體,在這個窗體上使用這個控件 ,布局如下。
注意:左邊是本機的視頻,右邊是對方的視頻。修改窗體的構造函數;
private IPEndPoint _serverEndPoint;
private UdpClient _socket;
public WebcamPlayerForm(IPEndPoint serverEndpoint, string caller, string callee)
{
}
與語音聊天一樣,數據傳遞我使 用了UdpClient,我感覺UdpClient簡單好用。_serverEndPoint是WCF服務的地址,_socket用於視頻數據傳遞。在客戶端我使用了兩個 System.Windows.Threading.DispatcherTimer,本來打算直接使用兩個線程,發現一些莫名奇妙的線程問題。兩個DispatcherTimer,一個 用來啟動接受來自WCF服務的視頻數據,一個用來將自己的視頻數據發送到WCF服務。代碼如下:
System.Windows.Threading.DispatcherTimer myDispatcherTimer = new System.Windows.Threading.DispatcherTimer();
myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
myDispatcherTimer.Tick += new EventHandler(captureVideo);
myDispatcherTimer.Start();
System.Windows.Threading.DispatcherTimer myDispatcherTimer2 = new System.Windows.Threading.DispatcherTimer();
myDispatcherTimer2.Interval = new TimeSpan(0, 0, 0, 0, 1000);
myDispatcherTimer2.Tick += new EventHandler(playVideo);
myDispatcherTimer2.Start();
captureVideo用於從攝像頭捕獲數據通過UdpClient發送到WCF服務中,代碼如下。
private void captureVideo(object sender, EventArgs e)
{
try
{
byte[] bytes;
if (webcamPlayer.CurrentBitmap != null)
{
bytes = ConvertImageSourceToByteArray (webcamPlayer.CurrentBitmap);//webcamPlayer.CurrentBitmap
_socket.Send(bytes, bytes.Length, _serverEndPoint);
}
}
catch (Exception) { }
}
上面的captureVideo方法將視頻數據先轉發到WCF服務,在由WCF服務轉發給對方,在WCF服務中有一個UdpClient接受數據,方法是 listen(),它的代碼如下:
private void listen()
{
try
{
while (true)
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
//if (sender.ToString() != "0.0.0.0: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);
}
}
//}
}
}
catch (Exception e)
{
}
}
WCF服務中的listen()方法將接收到的數據發送給對方的客戶端,在客戶端有playVideo方法來接收和並播放來自WCF的視頻數據,代碼 如下:
private void playVideo(object sender, EventArgs e)
{
try
{
lock (_socket)
{
if (_socket.Available != 0)
{
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
byte[] received = _socket.Receive(ref endpoint);
image1.Source = ConvertByteArrayToImageSource(received);
}
}
}
catch
{
}
}
由於從攝像頭獲取的數據格式是ImageSource,我們需要將它轉換成byte[]傳輸,轉換的代碼如下:
/// <summary>
/// Converts an <see cref="ImageSource"/> to an array of bytes.
/// </summary>
/// <param name="image"><see cref="ImageSource"/> to convert.</param>
/// <returns>Array of bytes.</returns>
public byte[] ConvertImageSourceToByteArray( ImageSource image)
{
// Declare variables
byte[] result = null;
// Use a memory stream to convert
using (MemoryStream memoryStream = new MemoryStream())
{
// Get right encoder
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
// Get right frame
if (image is BitmapSource)
{
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)image));
}
// Now set some encoder values
encoder.QualityLevel = 100;
encoder.Save(memoryStream);
// Now convert
result = memoryStream.ToArray();
}
// Return result
return result;
}
從WCF服務收到到的數據是byte[] 格式,我們需要將其轉換成ImageSource,代碼如下:
/// <summary>
/// Converts an array of bytes to a <see cref="ImageSource"/>.
/// </summary>
/// <param name="bytes">Bytes to convert.</param>
/// <returns><see cref="ImageSource"/>.</returns>
public ImageSource ConvertByteArrayToImageSource(byte[] bytes)
{
// Declare variables
ImageSource result = null;
// Validate input
if (bytes.Length == 0) return null;
// Create memory stream - it seems that if you clean up or dispose
// the memory stream, you cannot display the image any longer
MemoryStream memoryStream = new MemoryStream(bytes);
// Assign to bitmap image
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
// Assign bitmap to image source
result = bitmapImage;
// Return result
return result;
}
在前面文章的的基礎上完成這些操作,我們就可以實現視頻會話的功能。
效果:
1、選擇跟小花視頻:
2、小花接受到請求:
3、視頻中:
上圖是我在一台電腦上演示的,所以只有一邊顯示數據。但是,我用兩台筆記本測試過,效果也還不錯。
總結:
主要用到的技術有;WCF、WPF、UDPClient。還使用了一個開源的控件WebCam control for WPF。這個程序調試了我一天的時間。
由於博客的空間不夠用,過段時間整理好,會將代碼上傳到Codeplex上。
出處:http://zhuqil.cnblogs.com