且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

WPF+WCF一步一步打造音频聊天室(四):视频会话

更新时间:2022-08-25 12:35:29

  前面三篇文章中,我实现了音频聊天室的部分功能,包括:文字聊天,共享白板,语音聊天。这篇文章我将叙述一下视频会话实现的技术要点。

     在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

   5、Using the G.711 Codec

   6、convert encode and decode silverlight

    上面是Silverlight实现的方案和资料。这篇文章是用WPF+WCF去实现的。列出Silerlight是方便大家有个对照。

    视频会话实现的方式和语音通话实现的方式是一样的。他们之间不一样的地方在于,一个是通过麦克风获取数据,一个是通过摄像头获取数据。下面我用WF4画了一个流程图(这个流程图只是为了说明问题,没有用到程序里面)。WPF+WCF一步一步打造音频聊天室(四):视频会话

实现

    前面说到了,WPF中没有像Silerlight一样集成了摄像的功能,在WPF中又如何去实现摄像呢?这也是首先要解决的问题,我经过一番google,在Codeplex上找到了一个开源WPF的Webcam控件。地址是:WebCam control for WPF

    添加一个窗体,在这个窗体上使用这个控件,布局如下。

WPF+WCF一步一步打造音频聊天室(四):视频会话

    注意:左边是本机的视频,右边是对方的视频。修改窗体的构造函数;

        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、小花接受到请求:

WPF+WCF一步一步打造音频聊天室(四):视频会话

3、视频中:

WPF+WCF一步一步打造音频聊天室(四):视频会话

 

   上图是我在一台电脑上演示的,所以只有一边显示数据。但是,我用两台笔记本测试过,效果也还不错。

总结:

    主要用到的技术有;WCF、WPF、UDPClient。还使用了一个开源的控件WebCam control for WPF。这个程序调试了我一天的时间。

    由于博客的空间不够用,过段时间整理好,会将代码上传到Codeplex上。






本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2010/06/13/ZqlChart-4.html,如需转载请自行联系原作者