wpf中非继承FrameworkElement的控件,是无法在wpf的界面上展现的,必须用WindowsFormsHost来嵌套,比如一些第三方的ocx,而这其中我个人经常遇到的最郁闷的事情就是在wpf中播放特殊格式的视频,本人做视频监控方面的应用,面对各种乱七八糟的视频格式,有些必须厂商的库才能播放,可以想象把这些视频在wpf上播放多难受,一切因为wpf的控件是无句柄的,而用了WindowsFormsHost,控件又会置顶,视频菜单和视频叠加无法实现.
最近看到一个调用VLC播放器的C#开源代码,其中有Wpf的控件,看完代码发现,他实际是把视频的每一帧读取出来,然后再建一个共享内存,把视频数据转为InteropBitmap,InteropBitmap使开发人员能够提高在互操作方案中由 WPF 承载的非 WPF UIs 的呈现性能,可以直接赋值给Image.Source,这样通过Image播放每帧画面,就让视频动起来,当然项目中是借了vlc的解码功能能,如果我们要解自己的视频格式,就需要自己来实现解码转换为InteropBitmap的构成了,要是够强可以直接改VLC的解码器,VLC也是开源的.
借鉴开源代码中的思路和方法,打算实现自己的播放器,一般第三方解码库会提供:句柄播放视频,播放控制,截图等功能,我不打算完全实现一个wpf中的播放器,那样不实际,因为有太多厂商的接口要处理,只打算把句柄播放视频这里改成Image来展现,为了实现这个功能,先了解一些必备知识。
内存映射API
//创建内存映射 [DllImport("kernel32", SetLastError = true)] public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, PageAccess flProtect, int dwMaximumSizeLow, int dwMaximumSizeHigh, string lpName); //获取内存映射地址 [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccess dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); //释放内存映射 [DllImport("kernel32", SetLastError = true)] public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); //释放句柄 [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr handle);
Imaging如何从非托管内存获取位图,使用Imaging.CreateBitmapSourceFromMemorySection方法
// // 摘要: // 基于所提供的非托管内存位置,返回托管的 System.Windows.Media.Imaging.BitmapSource。 // // 参数: // section: // 内存部分的指针。 // // pixelWidth: // 用于指定位图宽度(以像素为单位)的整数。 // // pixelHeight: // 用于指定位图高度(以像素为单位)的整数。 // // format: // 枚举的一个值。 // // stride: // 位图的跨距。 // // offset: // 图像从其开始的内存流字节偏移量。 // // 返回结果: // 创建的 System.Windows.Media.Imaging.BitmapSource。 [SecurityCritical] public static BitmapSource CreateBitmapSourceFromMemorySection(IntPtr section, int pixelWidth, int pixelHeight, PixelFormat format, int stride, int offset);
CreateBitmapSourceFromMemorySection出来的类型显的转换为InteropBitmap,定义依赖属性绑定到image控件就可以了;
接下来就是怎么把视频数据一桢桢从内存中映射出来,这个需要和你的视频解码提供商有关系了,他解出来的每帧图片是什么格式,一般都那几种PixelFormats.Bgr32、PixelFormats.Pbgra32等,根据格式大小等图片信息,用CreateBitmapSourceFromMemorySection把它从内存中读取出来,内存隐射做的就是一帧帧的把内存隐射出来给CreateBitmapSourceFromMemorySection来处理,处理完释放掉,在继续下一帧,对方要是提供回调函数最好,没有回调就要自己去对这个视频内存做处理,视频组件都会带着些接口的。