您的位置:首页 > 其它

在托管应用程序中使用 DirectShow 组件获取视频文件截图和 FourCC 信息

2008-12-01 17:13 826 查看
最近需要完成一个对 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;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

使用上面的代码对一个 WMV 视频文件进行操作时,你得到的 FourCC 会是 WVC1 或者 WMV3。但是对 MP4 视频操作时得到的却是 YV12,而不是 AVC1。这显然不是你所期望的。一个可以替代的解决方案是使用 MediaInfo 这个开源项目所提供的类库。你可以从 http://downloads.sourceforge.net/mediainfo/ 下载。当然,这个类库是可以在托管应用程序中调用的。

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

参考资料:IBasicVideo2インターフェイスのGetCurrentImageメソッドをVB2005で使う

相关下载:Platform SDK for Windows Server 2003 R2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: