前面三篇文章中,我实现了音频聊天室的部分功能,包括:文字聊天,共享白板,语音聊天。这篇文章我将叙述一下视频会话实现的技术要点。
在Silerlight4中已经集成了摄像和采集声音的功能,但是在WPF4中却没有直接可以用的的控件,由此也可以看出,由桌面程序走向web程序的大趋势。如果你想用Silverlight实现类似的音频聊天室,下面我列出一些资料供你参考。
1、 Your First Step to the Silverlight Voice/Video Chatting Client/Server
2、Accessing Web Camera and Microphone
3、Record The Audio Into A Wave File
4、Playback The Wave File in Silverlight
6、convert encode and decode silverlight
上面是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://www.cnblogs.com/zhuqil/archive/2010/06/13/ZqlChart-4.html,如需转载请自行联系原作者