WPF预览多个摄像头视频数据

本文介绍如何使用WPF开发应用程序预览多个摄像头的视频数据。文章详细探讨了使用FFMPEG获取视频数据,并通过尝试SDL显示及最终选择WPF Image控件的方法。包括解决将AV_PIX_FMT_RGB24格式数据转换为BitmapImage的问题。

需求:

使用WPF开发应用程序来预览多个摄像头视频数据。


设计思路:

1)通过FFMPEG获取摄像头视频数据。

2)通过SDL来显示视频图像。


技术问题点:

SDL只能在窗体显示,要么弹出新窗体,要么在程序UI相关控件(比如PictureBox控件,也可以是其他Static类控件)显示。显然,获取一个视频设备弹出一个新窗体,不符合需求。那只能把SDL显示窗体嵌入到UI控件里。

要想把SDL嵌入UI控件,就必须要把该UI控件的句柄传给SDL的SDL_CreateWindowFrom()函数。

但是,WPF的UI控件没有句柄这概念,需要在WPF应用程序嵌入Win Form的控件。先创建WindowsFormsHost控件,就可以插入Win Form控件了,代码示例:

                <WindowsFormsHost Canvas.Left="10" Canvas.Top="70" Width="230" Height="180" Background="Black">
                    <wf:PictureBox />
                </WindowsFormsHost>

但是,生成的效果如下:


我们发现,在滚动时WindowsFormsHost控件始终都是置顶显示,查询类似问题,答复是“wpf嵌套Win Form组件必定置顶,无解,wpf控件无句柄”。

那显然此需求不能使用SDL来显示视频图像,只好想办法直接使用WPF的Image控件。

新的问题来了,要在Image控件显示,就需要把视频数据转换成Image控件能识别的BitmapImage。

网上有Bitmap转换BitmapImage的方法(参考:http://blog.sina.com.cn/s/blog_6e6941e10100n2yn.html),其代码如下:

        public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
        {
            Bitmap bitmapSource = new Bitmap(bitmap.Width, bitmap.Height);
            bitmapSource.MakeTransparent();
            int i, j;
            for (i = 0; i < bitmap.Width; i++)
                for (j = 0; j < bitmap.Height; j++)
                {

                    Color pixelColor = bitmap.GetPixel(i, j);
                    Color newColor = Color.FromArgb(pixelColor.R, pixelColor.G, pixelColor.B);
                    if (newColor == Color.FromArgb(255, 0, 0, 0))
                    {
                        newColor = Color.FromArgb(255, 255, 255, 255);
                    }
                    bitmapSource.SetPixel(i, j, newColor);
                }
            MemoryStream ms = new MemoryStream();
            bitmapSource.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
            bitmapImage.EndInit();
            return bitmapImage;
        }
但是,我们从FFMPEG直接得到的视频数据是某种格式的数据,比如AV_PIX_FMT_RGB24格式。我们没有必要把AV_PIX_FMT_RGB24格式数据先转换到Bitmap,直接转换到BitmapImage即可,其代码如下:

        /// <summary>
        /// 格式为AV_PIX_FMT_RGB24的视频数据,转换成WPF Image控件所能识别的BitmapImage类的数据
        /// 
        /// </summary>
        /// <param name="data">AV_PIX_FMT_RGB24的视频数据</param>
        /// <param name="imgWidth">显示图像宽度</param>
        /// <param name="imgHeight">显示图像高度</param>
        /// <returns></returns>
        public static BitmapImage VideoDataToBitmapImage(byte []data, int imgWidth, int imgHeight)
        {
            Bitmap bitmapSource = new Bitmap(imgWidth, imgHeight);
            bitmapSource.MakeTransparent();
            int i, j;
#if true         
            for (i = 0; i < imgWidth; i++)
                for (j = 0; j < imgHeight; j++)
                {
                    Color newColor = Color.FromArgb(
                        data[j * imgWidth * 3 + i * 3 + 0],
                        data[j * imgWidth * 3 + i * 3 + 1],
                        data[j * imgWidth * 3 + i * 3 + 2]);
                    if (newColor == Color.FromArgb(255, 0, 0, 0))
                    {
                        newColor = Color.FromArgb(255, 255, 255, 255);
                    }
                    bitmapSource.SetPixel(i, j, newColor);
                }
#else
            for (i = 0; i < imgHeight; i++)
                for (j = 0; j < imgWidth; j++)
                {
                    Color newColor = Color.FromArgb(
                        data[i * imgWidth * 3 + j * 3 + 0],
                        data[i * imgWidth * 3 + j * 3 + 1],
                        data[i * imgWidth * 3 + j * 3 + 2]);
                    if (newColor == Color.FromArgb(255, 0, 0, 0))
                    {
                        newColor = Color.FromArgb(255, 255, 255, 255);
                    }
                    bitmapSource.SetPixel(j, i, newColor);
                }
#endif
            MemoryStream ms = new MemoryStream();
            bitmapSource.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
            bitmapImage.EndInit();
            return bitmapImage;
        }
上述代码,我调试了两天,才能正常显示图像,差点开始怀疑人生……上述函数重点是如何给每个像素点设置颜色,即处理SetPixel()函数。直到意识到这一点才得到正确的算法:我参考的代码的填充流程是一列一列的填充,但是获取的视频数据是一行一行的顺序。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值