在托管应用程序中使用 DirectShow 组件获取视频文件截图和 FourCC 信息 最近需要完成一个对 MP4 视频文件进行截图的功能。在网上查了很多资料,结果都是通过调用 MPlayer 或者 ffmpeg 这些外部应用程序实现视频文件截图功能的。这种方法比较简单,代码量也比较少。但是因为使用了是第三方产品,所以显得很不专业。而且 MPlayer 和 ffmpeg 很不稳定,截出来的图像总是有黑屏或者花屏的问题。 后来安装了 Platform SDK for Windows Server 2003 R2,发现 IBasicVideo2 的 GetCurrentImage 方法可以对视频截图。但是 Windows 本身既不支持 AVC(H.264) 视频解码,也不支持 AAC 音频解码。所以又花了点时间找解码器,最后终于实现了在托管应用程序中使用 DirectShow 组件获取视频文件截图功能。 1.引用 DirectShow 组件 启动 Microsoft Visual Studio 2008,创建一个控制台应用程序。在控制台应用程序中引用 C:/Windows/System32/quartz.dll (ActiveMovie control type library) C:/Windows/System32/qedit.dll (Dexter 1.0 Type Library) 这两个 COM 组件和 System.Drawing 程序集。 2.获取视频文件截图 2.1 定义 IBasicVideo2 接口 IBasicVideo2 的 GetCurrentImage 方法是视频截图功能所必须的,但 QuartzTypeLib.IBasicVideo2 是 IBasicVideo2 Object,而不是 IBasicVideo2 Interface。根据 SDK 中的描述 QuartzTypeLib.IBasicVideo2 的 GetCurrentImage 方法是不支持在托管应用程序中调用的,所以必须编写代码定义 IBasicVideo2 Interface 才行。 创建一个 IBasicVideo2.cs 文件: namespace ConsoleApplication1 { using System; using System.Runtime.InteropServices; [ComImport] [ComVisible(true)] [Guid("329bb360-f6ea-11d1-9038-00a0c9697298")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IBasicVideo2 { [PreserveSig] int AvgTimePerFrame([Out] double pAvgTimePerFrame); [PreserveSig] int BitRate([Out] int pBitRate); [PreserveSig] int BitErrorRate([Out] int pBitRate); [PreserveSig] int VideoWidth([Out] int pVideoWidth); [PreserveSig] int VideoHeight([Out] int pVideoHeight); [PreserveSig] int put_SourceLeft(int SourceLeft); [PreserveSig] int get_SourceLeft([Out] int pSourceLeft); [PreserveSig] int put_SourceWidth(int SourceWidth); [PreserveSig] int get_SourceWidth([Out] int pSourceWidth); [PreserveSig] int put_SourceTop(int SourceTop); [PreserveSig] int get_SourceTop([Out] int pSourceTop); [PreserveSig] int put_SourceHeight(int SourceHeight); [PreserveSig] int get_SourceHeight([Out] int pSourceHeight); [PreserveSig] int put_DestinationLeft(int DestinationLeft); [PreserveSig] int get_DestinationLeft([Out] int pDestinationLeft); [PreserveSig] int put_DestinationWidth(int DestinationWidth); [PreserveSig] int get_DestinationWidth([Out] int pDestinationWidth); [PreserveSig] int put_DestinationTop(int DestinationTop); [PreserveSig] int get_DestinationTop([Out] int pDestinationTop); [PreserveSig] int put_DestinationHeight(int DestinationHeight); [PreserveSig] int get_DestinationHeight([Out] int pDestinationHeight); [PreserveSig] int SetSourcePosition(int left, int top, int width, int height); [PreserveSig] int GetSourcePosition([Out] int left, [Out] int top, [Out] int width, [Out] int height); [PreserveSig] int SetDefaultSourcePosition(); [PreserveSig] int SetDestinationPosition(int left, int top, int width, int height); [PreserveSig] int GetDestinationPosition([Out] int left, [Out] int top, [Out] int width, [Out] int height); [PreserveSig] int SetDefaultDestinationPosition(); [PreserveSig] int GetVideoSize([Out] int pWidth, [Out] int pHeight); [PreserveSig] int GetVideoPaletteEntries(int StartIndex, int Entries, [Out] int pRetrieved, IntPtr pPalette); [PreserveSig] int GetCurrentImage(ref int pBufferSize, IntPtr pDIBImage); [PreserveSig] int IsUsingDefaultSource(); [PreserveSig] int IsUsingDefaultDestination(); [PreserveSig] int GetPreferredAspectRatio([Out] int plAspectX, [Out] int plAspectY); } } 2.2 使用 GetCurrentImage 方法获取视频图像 可以使用 GraphEdit 对视频文件进行测试,只要是能在 GraphEdit 中正常播放就可以使用下面的代码进行截图。GraphEdit 工具包含在 Platform SDK for Windows Server 2003 R2 中,安装之后你可以在 C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Bin/graphedt.exe 找到它。 Program.cs 文件: namespace ConsoleApplication1 { using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using QuartzTypeLib; class Program { static void Main(string[] args) { if (args.Length == 2) { GetThumbnail(args[0], args[1]); } } static void GetThumbnail(string videoFileName, string bitmapFileName) { // 初始化 FilgraphManagerClass 类的新实例 FilgraphManagerClass filgraph = new FilgraphManagerClass(); // 载入视频文件 try { // 尝试载入视频文件 filgraph.RenderFile(videoFileName); } catch { // 载入视频文件失败,使用 GraphEdit 检查解码器是否可以正常工作 return; } // 将开始时间定位到总时长的一半,之后将截取此位置的图像 filgraph.CurrentPosition = filgraph.Duration / 2; // 视频的原始宽度 int width = filgraph.SourceWidth; // 视频的原始高度 int height = filgraph.SourceHeight; // 所需的内存中的字节数 // BITMAPINFOHEADER.biSize + 4 * BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight int pBufferSize = 40 + 4 * width * height; // 将 FilgraphManager 转换成 IBasicVideo2.cs 文件中定义的 IBasicVideo2 接口 IBasicVideo2 video = (IBasicVideo2)filgraph; // 从进程的非托管内存中分配内存 IntPtr pDIBImage = Marshal.AllocHGlobal(pBufferSize); // 获取新分配的内存的 IntPtr video.GetCurrentImage(ref pBufferSize, pDIBImage); // 相邻扫描行开始处之间字节偏移量 int stride = -4 * width; // 颜色数据的格式 PixelFormat format = PixelFormat.Format32bppRgb; // 包含像素数据的字节数组的指针 IntPtr scan0 = (IntPtr)(((int)pDIBImage) + (pBufferSize - (4 * width))); // 用指定的大小、像素格式和像素数据初始化 Bitmap 类的新实例 Bitmap bmp = new Bitmap(width, height, stride, format, scan0); // 将图像保存到指定的文件。 bmp.Save(bitmapFileName); // 释放以前使用 AllocHGlobal 从进程的非托管内存中分配的内存 Marshal.FreeHGlobal(pDIBImage); // 释放运行时可调用包装及原始 COM 对象 while (Marshal.ReleaseComObject(filgraph) > 0); } } } 2.3 添加对 MP4 视频文件的支持 MP4Splitter.ax 是一个 MP4 分离器,可以从 http://downloads.sourceforge.net/mpc-hc/ 获取到。 CoreAVCDecoder.ax 是一个 AVC(H.264) 视频解码器,从 http://www.coreavc.com/ 下载安装包,安装之后可以在安装目录找到。 CoreAAC.ax 是一个 AAC 音频解码器,可以从 http://www.codecs.com/ 获取到。 将这3个文件复制到 C:/Windows/System32/ 目录,使用 regsvr32 命令注册之后就能在 GraphEdit 中播放 MP4 视频文件了。 注意:只有在有许可的情况下才能注册 CoreAVCDecoder.ax 组件。 3.获取 FourCC 信息 微软在 FOURCC Codes 这篇文档中阐述了如何将 GUID 类型的 Subtype 转换成 FourCC 四字符代码。 static string GetFourCC(string videoFileName) { string fourCC = null; // 初始化 MediaDetClass 类的新实例 MediaDetClass mediaDet = new MediaDetClass(); // 指派视频文件 mediaDet.Filename = videoFileName; // 获取流数量 int streamsNumber = mediaDet.OutputStreams; // 循环读取流信息 for (int i = 0; i < streamsNumber; i++) { // 设置要操作的流 mediaDet.CurrentStream = i; // 获取流的媒体类型 _AMMediaType streamMediaType = mediaDet.StreamMediaType; // 获取媒体类型的主类型 Guid majorType = streamMediaType.majortype; // 获取媒体类型的主类型字节数组 byte[] majorTypeBytes = majorType.ToByteArray(); // 获取媒体类型的主类型的 FourCC 信息,检查流是否是视频类型 if (string.Concat( (char)majorTypeBytes[0], (char)majorTypeBytes[1], (char)majorTypeBytes[2], (char)majorTypeBytes[3] ) == "vids") { // 获取媒体类型的子类型 Guid subType = streamMediaType.subtype; // 获取媒体类型的子类型字节数组 byte[] subTypeBytes = subType.ToByteArray(); // 获取媒体类型的子类型的 FourCC 信息 fourCC = string.Concat( (char)majorTypeBytes[0], (char)majorTypeBytes[1], (char)majorTypeBytes[2], (char)majorTypeBytes[3] ); // 跳出循环 break; } } // 释放运行时可调用包装及原始 COM 对象 while (Marshal.ReleaseComObject(mediaDet) > 0) ; // 返回媒体文件的 FourCC return fourCC; } 使用上面的代码对一个 WMV 视频文件进行操作时,你得到的 FourCC 会是 WVC1 或者 WMV3。但是对 MP4 视频操作时得到的却是 YV12,而不是 AVC1。这显然不是你所期望的。一个可以替代的解决方案是使用 MediaInfo 这个开源项目所提供的类库。你可以从 http://downloads.sourceforge.net/mediainfo/ 下载。当然,这个类库是可以在托管应用程序中调用的。

C#中手动引用COM组建的例子相关推荐

  1. [转载] Java中如何引用另一个类里的集合_Java工程师面试题整理

    参考链接: 在Java中将预定义的类名用作类或变量名 花了一星期把学过的都整理一遍 尽量易懂,从基础到框架 最新版大厂面经汇总出炉,持续更新中 汇总完了上传网盘,设计到后端架构师的一切知识 如果没更新 ...

  2. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子  例1 用Lambda表达式实现Runnable接口 Java代码   //Before Java 8: new Thread(new Runnable() ...

  3. C++中const引用和非const引用的使用注意

    今天学习时突然有疑惑,C++有了指针为何还要设计引用呢?后来看到一篇博客豁然开朗:为什么 C++ 有指针了还要引用? 总结一下: 代码更加简洁好看了 由于引用必须被初始化,并且之后也无法重新绑定其他对 ...

  4. python中的引用、浅拷贝和深拷贝

    在python中,有一句话:"一切皆为对象,一切皆为对象的引用",所以 只要记住这句话就很容易清楚python中的引用.浅拷贝和深拷贝了. 1. 引用 python中的引用是经常使 ...

  5. c++中的引用和python中的引用_对比 C++ 和 Python,谈谈指针与引用

    作者 | 樱雨楼 引言 指针(Pointer)是 C.C++ 以及 Java.Go 等语言的一个非常核心且重要的概念,而引用(Reference)是在指针的基础上构建出的一个同样重要的概念. 指针对于 ...

  6. java 软引用_Java中弱引用和软引用的区别以及虚引用和强引用介绍

    知道弱引用和软引用的概念与如何使用它们是两码事,引用类在垃圾回收工作的过程中有重要作用.我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型 ...

  7. Java学习笔记二十六:Java多态中的引用类型转换

    Java多态中的引用类型转换 引用类型转换: 1.向上类型转换(隐式/自动类型转换),是小类型到大类型的转换: 2.向下类型转换(强制类型转换),是大类型到小类型的转换: 3.instanceof运算 ...

  8. Perl 教学 Perl5中的引用(指针)

    一.引用简介    引用就是指针,可以指向变量.数组.哈希表(也叫关联数组)甚至子程序.Pascal或C程序员应该对引用(即指针)的概念很熟悉,引用就是某值的地址,对其的使用则取决于程序员和语言的规定 ...

  9. 浅析C#中foreach引用变量

    昨天做老师的网站作业.要对一些对象做添加修改删除处理.别的倒没什么,删除时出现了点问题似的. 因为是从一个类的集合中删除掉一个元素.这样就要遍历整个集合,而foreach正是为遍历准备的新玩意.自然而 ...

  10. 理解Javascritp中的引用

    Author: bugall Wechat: bugallF Email: 769088641@qq.com Github: https://github.com/bugall 一: 函数中的引用传递 ...

最新文章

  1. JAVA中对日期格式的处理
  2. pygtk笔记--2.1:布局容器,VBox、Hbox、Alignment
  3. python基本库系列一:Request
  4. 洛谷 2820 局域网
  5. ES6-16 WeakMap与WeakSet、proxy与reflect
  6. python可以调用windows资源吗_如何在Windows上用Python调用WinRar?还有问题吗
  7. dataTables插件使用
  8. 巨省显存的重计算技巧在TF、Keras中的正确打开方式
  9. 破解centos7root口令
  10. 426rmb to php,PHP 转换数字为大写人民币之二
  11. python四种可变类型_python中可变类型与不可变类型 + 类型转换
  12. 计算机考试准考证去哪下载
  13. java gb28181网关_国标GB28181协议对接网关
  14. EEPROM、FLASH、NOR FLASH、NAND FLASH 区别、关系总结
  15. html 确定取消dialog,弹出一个带确认和取消的dialog实例
  16. “汉堡+奶昔”怎么就成了精致生活的热门标签?
  17. 重估维信金科:担保压身,负重难行
  18. 微信小程序九宫格抽奖大转盘
  19. 解决import cv2找不到指定模块问题
  20. 银心科技与黑萤科技达成战略合作,联合构建区块链数据库存储生态至高点

热门文章

  1. Python实现图形学DDA算法
  2. 大量监控视频如何存储?
  3. html加载gif动画效果,html – 使用CSS动画加载图像VS使用GIF图像加载
  4. 解读主流CDN厂商的节点数据
  5. 《Nature-Inspired Metaheuristic Algorithms》——蝙蝠算法 Bat Algorithm
  6. html+css入门(参考b站黑马
  7. 伴你装系统系列(上篇):Windows10
  8. 【数据结构-栈】C语言实现顺序栈基本操作
  9. java实习周记_计算机java开发实习周记20篇
  10. 腾讯视频QLV格式转换为MP4格式