C#中手动引用COM组建的例子
生活随笔
收集整理的這篇文章主要介紹了
C#中手动引用COM组建的例子
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在托管應(yīng)用程序中使用 DirectShow 組件獲取視頻文件截圖和 FourCC 信息 最近需要完成一個(gè)對 MP4 視頻文件進(jìn)行截圖的功能。在網(wǎng)上查了很多資料,結(jié)果都是通過調(diào)用 MPlayer 或者 ffmpeg 這些外部應(yīng)用程序?qū)崿F(xiàn)視頻文件截圖功能的。這種方法比較簡單,代碼量也比較少。但是因?yàn)槭褂昧耸堑谌疆a(chǎn)品,所以顯得很不專業(yè)。而且 MPlayer 和 ffmpeg 很不穩(wěn)定,截出來的圖像總是有黑屏或者花屏的問題。 后來安裝了 Platform SDK for Windows Server 2003 R2,發(fā)現(xiàn) IBasicVideo2 的 GetCurrentImage 方法可以對視頻截圖。但是 Windows 本身既不支持 AVC(H.264) 視頻解碼,也不支持 AAC 音頻解碼。所以又花了點(diǎn)時(shí)間找解碼器,最后終于實(shí)現(xiàn)了在托管應(yīng)用程序中使用 DirectShow 組件獲取視頻文件截圖功能。 1.引用 DirectShow 組件 啟動(dòng) Microsoft Visual Studio 2008,創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序。在控制臺(tái)應(yīng)用程序中引用 C:/Windows/System32/quartz.dll (ActiveMovie control type library) C:/Windows/System32/qedit.dll (Dexter 1.0 Type Library) 這兩個(gè) COM 組件和 System.Drawing 程序集。 2.獲取視頻文件截圖 2.1 定義 IBasicVideo2 接口 IBasicVideo2 的 GetCurrentImage 方法是視頻截圖功能所必須的,但 QuartzTypeLib.IBasicVideo2 是 IBasicVideo2 Object,而不是 IBasicVideo2 Interface。根據(jù) SDK 中的描述 QuartzTypeLib.IBasicVideo2 的 GetCurrentImage 方法是不支持在托管應(yīng)用程序中調(diào)用的,所以必須編寫代碼定義 IBasicVideo2 Interface 才行。 創(chuàng)建一個(gè) 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 對視頻文件進(jìn)行測試,只要是能在 GraphEdit 中正常播放就可以使用下面的代碼進(jìn)行截圖。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 類的新實(shí)例 FilgraphManagerClass filgraph = new FilgraphManagerClass(); // 載入視頻文件 try { // 嘗試載入視頻文件 filgraph.RenderFile(videoFileName); } catch { // 載入視頻文件失敗,使用 GraphEdit 檢查解碼器是否可以正常工作 return; } // 將開始時(shí)間定位到總時(shí)長的一半,之后將截取此位置的圖像 filgraph.CurrentPosition = filgraph.Duration / 2; // 視頻的原始寬度 int width = filgraph.SourceWidth; // 視頻的原始高度 int height = filgraph.SourceHeight; // 所需的內(nèi)存中的字節(jié)數(shù) // BITMAPINFOHEADER.biSize + 4 * BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight int pBufferSize = 40 + 4 * width * height; // 將 FilgraphManager 轉(zhuǎn)換成 IBasicVideo2.cs 文件中定義的 IBasicVideo2 接口 IBasicVideo2 video = (IBasicVideo2)filgraph; // 從進(jìn)程的非托管內(nèi)存中分配內(nèi)存 IntPtr pDIBImage = Marshal.AllocHGlobal(pBufferSize); // 獲取新分配的內(nèi)存的 IntPtr video.GetCurrentImage(ref pBufferSize, pDIBImage); // 相鄰掃描行開始處之間字節(jié)偏移量 int stride = -4 * width; // 顏色數(shù)據(jù)的格式 PixelFormat format = PixelFormat.Format32bppRgb; // 包含像素?cái)?shù)據(jù)的字節(jié)數(shù)組的指針 IntPtr scan0 = (IntPtr)(((int)pDIBImage) + (pBufferSize - (4 * width))); // 用指定的大小、像素格式和像素?cái)?shù)據(jù)初始化 Bitmap 類的新實(shí)例 Bitmap bmp = new Bitmap(width, height, stride, format, scan0); // 將圖像保存到指定的文件。 bmp.Save(bitmapFileName); // 釋放以前使用 AllocHGlobal 從進(jìn)程的非托管內(nèi)存中分配的內(nèi)存 Marshal.FreeHGlobal(pDIBImage); // 釋放運(yùn)行時(shí)可調(diào)用包裝及原始 COM 對象 while (Marshal.ReleaseComObject(filgraph) > 0); } } } 2.3 添加對 MP4 視頻文件的支持 MP4Splitter.ax 是一個(gè) MP4 分離器,可以從 http://downloads.sourceforge.net/mpc-hc/ 獲取到。 CoreAVCDecoder.ax 是一個(gè) AVC(H.264) 視頻解碼器,從 http://www.coreavc.com/ 下載安裝包,安裝之后可以在安裝目錄找到。 CoreAAC.ax 是一個(gè) AAC 音頻解碼器,可以從 http://www.codecs.com/ 獲取到。 將這3個(gè)文件復(fù)制到 C:/Windows/System32/ 目錄,使用 regsvr32 命令注冊之后就能在 GraphEdit 中播放 MP4 視頻文件了。 注意:只有在有許可的情況下才能注冊 CoreAVCDecoder.ax 組件。 3.獲取 FourCC 信息 微軟在 FOURCC Codes 這篇文檔中闡述了如何將 GUID 類型的 Subtype 轉(zhuǎn)換成 FourCC 四字符代碼。 static string GetFourCC(string videoFileName) { string fourCC = null; // 初始化 MediaDetClass 類的新實(shí)例 MediaDetClass mediaDet = new MediaDetClass(); // 指派視頻文件 mediaDet.Filename = videoFileName; // 獲取流數(shù)量 int streamsNumber = mediaDet.OutputStreams; // 循環(huán)讀取流信息 for (int i = 0; i < streamsNumber; i++) { // 設(shè)置要操作的流 mediaDet.CurrentStream = i; // 獲取流的媒體類型 _AMMediaType streamMediaType = mediaDet.StreamMediaType; // 獲取媒體類型的主類型 Guid majorType = streamMediaType.majortype; // 獲取媒體類型的主類型字節(jié)數(shù)組 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; // 獲取媒體類型的子類型字節(jié)數(shù)組 byte[] subTypeBytes = subType.ToByteArray(); // 獲取媒體類型的子類型的 FourCC 信息 fourCC = string.Concat( (char)majorTypeBytes[0], (char)majorTypeBytes[1], (char)majorTypeBytes[2], (char)majorTypeBytes[3] ); // 跳出循環(huán) break; } } // 釋放運(yùn)行時(shí)可調(diào)用包裝及原始 COM 對象 while (Marshal.ReleaseComObject(mediaDet) > 0) ; // 返回媒體文件的 FourCC return fourCC; } 使用上面的代碼對一個(gè) WMV 視頻文件進(jìn)行操作時(shí),你得到的 FourCC 會(huì)是 WVC1 或者 WMV3。但是對 MP4 視頻操作時(shí)得到的卻是 YV12,而不是 AVC1。這顯然不是你所期望的。一個(gè)可以替代的解決方案是使用 MediaInfo 這個(gè)開源項(xiàng)目所提供的類庫。你可以從 http://downloads.sourceforge.net/mediainfo/ 下載。當(dāng)然,這個(gè)類庫是可以在托管應(yīng)用程序中調(diào)用的。
總結(jié)
以上是生活随笔為你收集整理的C#中手动引用COM组建的例子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《概率论与数理统计》(浙大第四版)第三章
- 下一篇: 必须进行支持的游戏方可使用此功能_C#8