截图(Grabbing a Poster Frame)
来源:互联网 发布:淘宝可以分期付款吗 编辑:程序博客网 时间:2024/05/18 04:01
本文描述了如何使用 "媒体侦测器"(Media Detector - MediaDet) 显示数字媒体文件中的一帧,"媒体侦测器" 对象是由 DirectShow Editing Services 提供。
"媒体侦测器"是一个助手对象,他可以从媒体源文件 (media source file) 中获得格式信息,他也可以从视频流文件中截取位图 (Bitmap)。
假设视频流文件是可定位 (seekable) 的,你就可以从该文件的任何位置获取图像。返回的图像格式一定 24 位 RGB。
"媒体侦测器" 并不是一个 "过滤器" (Filter),你的应用程序也不需要使用 "过滤器图表管理器" (Filter Graph Manager),也不需要创建"
过滤器图表" (filter graph)。"媒体侦测器"会创建"过滤器图表",其中包含"样本截取过滤器" (Sample Grabber Filter),为了获取位图,"
过滤器图表"定位并暂停"过滤器图表",然后从"样本截取过滤器"中获取位图。应用程序通过 IMediaDet 接口,和"媒体侦测器"进行通信。
"媒体侦测器"是把"过滤器图表"封装到助手对象中的不错的例子,从而把图表相关的细节与应用程序隔离(shield)开来。
"媒体侦测器"有两种模式,当你第一次创建该对象时,他出于"信息获取"(information gathering)模式。你可以在该模式下,指定媒体文件,
并从该文件中包含的每一个流(stream)中获得信息,例如格式类型,帧比率(frame rate),文件播放长度。如果媒体文件中包含视频流,你可以
把"媒体侦测器"切换到另一种模式 - 位图截取模式("bitmap grab" mode ),并从源文件中获得位图。一旦你切换到该模式下 ,将无法再切回
原来的模式,如需处理文件中的其他流或其他文件,你就必须重新创建一个新的"媒体侦测器"实例。
我们分4步来完成解图:
1.创建窗口
2.添加一个菜单项
3.实现帧截取(Frame-Grabbing)功能
4.在客户区(Client Area)绘制位图
1.创建窗口
为应用程序创建一个框架窗口,包含 WinMain 函数和一个窗口处理(window procedure)函数。在消息循环(message loop )之前通过调用 CoInitialize 来初始化 COM 库,在退出消息循环时,调用 CoUninitialize 。以下是窗口处理函数:
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static BITMAPINFOHEADER *pbmi = NULL;
static BYTE *pBuffer = NULL;
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
if (pbmi) delete [] pbmi;
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}
当你要从"媒体侦测器"中获取一帧图像时,他会返回一个缓存(buffer),该缓存包含 BITMAPINFOHEADER 结构,以及图像数据。所以我们在窗口处理函数定义了两个静态变量:pbmi 是 BITMAPINFOHEADER 指针,pBuffer 是位图数据的指针。应用程序必须通过 new 来为 pbmi 分配缓存,所以必须在窗口销毁(destroy)之前 delete 掉。而 pBuffer 指针其实只是 pbmi 指针的一个偏移量,所以无需 delete。
2.添加一个菜单项添加一个命令让用户可以从媒体文件中截取一帧。创建菜单项,其资源 ID 是 IDM_BITMAP,并添加以下 case 语句到窗口处理函数中:
case WM_COMMAND:
switch (LOWORD(wparam))
{
case IDM_BITMAP:
{
HRESULT hr = DoShowBitmap(hwnd, &pbmi);
if (SUCCEEDED(hr))
{
pBuffer = reinterpret_cast<BYTE*>(pbmi) +
sizeof(BITMAPINFOHEADER);
InvalidateRect(hwnd, NULL, TRUE);
}
else
{
MessageBox(hwnd, TEXT("Cannot display the image."),
TEXT("Error"), MB_OK | MB_ICONERROR);
}
}
break; // IDM_BITMAP
}
break; // WM_COMMAND
DoShowBitmap 函数将为 pbmi 分配缓存。如果函数成功执行,位图数据地址(pBuffer),可以通过 pbmi 指针计算获得。在 DoShowBitmap 函数中显示一个打开文件对话框,让用户选择一个媒体文件,然后通过调用 GetBitmap 函数来截取位图:
HRESULT DoShowBitmap(HWND hwnd, BITMAPINFOHEADER** ppbmih)
{
OPENFILENAME ofn; // 通用对话框结构
// 初始化 OPENFILENAME (略).
// 显示一个打开文件对话框
if (GetOpenFileName(&ofn) != TRUE) // 打开失败
{
return E_FAIL;
}
return GetBitmap(ofn.lpstrFile, ppbmih);
}
3.实现帧截取(Frame-Grabbing)功能
以下就来实现 GetBitmap 函数,该函数进行如下操作:
1.创建"媒体侦测器"对象
2.指定媒体文件
3.检查媒体文件中包含的每一流,如果是视频流的话,就取得视频原始的高度和宽度。
4.获取图像帧,指定定位时间和目标图像文件的高度,宽度。
调用 CoCreateInstance 来创建"媒体侦测器"
CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));
使用 IMediaDet::put_Filename 方法指定媒体文件,该方法使用 BSTR 参数。
hr = pDet->put_Filename(bstrFilename);
获取文件中包含流的数量,然后通过循环语句检查每个流的媒体类型(media type),IMediaDet::get_OutputStreams 方法可以获得流数量,IMediaDet::put_CurrentStream 方法用来指派检查某个流,当找到视频流时,就退出循环语句。
long lStreams;
bool bFound = false;
hr = pDet->get_OutputStreams(&lStreams);
for (long i = 0; i < lStreams; i++)
{
GUID major_type;
hr = pDet->put_CurrentStream(i);
hr = pDet->get_StreamType(&major_type);
if (major_type == MEDIATYPE_Video) // 找到视频流
{
bFound = true;
break;
}
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;
如果未能找到视频流,函数就退出。
在先前的代码中,IMediaDet::get_StreamType 方法返回的只是媒体主类型 (major type) 的 GUID,如果你只想获得媒体主类型的话,调用该方法即可,如果希望获得视频的高宽等信息时,你必须检查媒体文件的格式,所以你需要全部的媒体类型描述,可以通过IMediaDet::get_StreamMediaType 方法来获得,该方法返回 AM_MEDIA_TYPE 结构。"媒体侦测器"把所以视频流转换为非压缩格式,其中包含VIDEOINFOHEADER 格式块。
long width = 0, height = 0;
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt);
if (mt.formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
width = pVih->bmiHeader.biWidth;
height = pVih->bmiHeader.biHeight;
// 我们需要高度的绝对值,不考虑图像的方向
if (height < 0) height *= -1;
}
else {
return VFW_E_INVALIDMEDIATYPE; // This should not happen, in theory.
}
FreeMediaType(mt);
get_StreamMediaType 方法分配的格式块,函数调用者必须在使用完毕后释放他,可以通过调用函数 FreeMediaType 来完成。
好了,你现在就可以截取图像了,先调用 IMediaDet::GetBitmapBits 方法,参数是 NULL,来取得缓存:
long lSize;
hr = pDet->GetBitmapBits(0, &lSize, NULL, width, height);
该调用返回所需的缓存大小(参数 lSize),函数的第一个参数是用来指定流中的定位时间,该例子使用零,对于高度和宽度还是使用原始视频的大小,如果你改变该大小,"媒体侦测器"会伸缩位图来适合你指定的大小。
如果该方法执行成功,分配好缓存,然后再调用一次 GetBitmapBits 方法。
if (SUCCEEDED(hr))
{
char *pBuffer = new char[lSize];
if (!pBuffer) return E_OUTOFMEMORY;
hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
if (FAILED(hr))
delete [] pBuffer;
}
以下是完整的函数:
HRESULT GetBitmap(LPTSTR pszFileName, BITMAPINFOHEADER** ppbmih)
{
HRESULT hr;
CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));
if (FAILED(hr)) return hr;
// Convert the file name to a BSTR.
CComBSTR bstrFilename(pszFileName);
hr = pDet->put_Filename(bstrFilename);
if (FAILED(hr)) return hr;
long lStreams;
bool bFound = false;
hr = pDet->get_OutputStreams(&lStreams);
if (FAILED(hr)) return hr;
for (long i = 0; i < lStreams; i++)
{
GUID major_type;
hr = pDet->put_CurrentStream(i);
if (SUCCEEDED(hr))
{
hr = pDet->get_StreamType(&major_type);
}
if (FAILED(hr)) break;
if (major_type == MEDIATYPE_Video)
{
bFound = true;
break;
}
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;
long width = 0, height = 0;
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt);
if (SUCCEEDED(hr))
{
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
width = pVih->bmiHeader.biWidth;
height = pVih->bmiHeader.biHeight;
// We want the absolute height, don't care about orientation.
if (height < 0) height *= -1;
}
else
{
hr = VFW_E_INVALIDMEDIATYPE; // Should not happen, in theory.
}
FreeMediaType(mt);
}
if (FAILED(hr)) return hr;
// Find the required buffer size.
long size;
hr = pDet->GetBitmapBits(0, &size, NULL, width, height);
if (SUCCEEDED(hr))
{
char *pBuffer = new char[size];
if (!pBuffer) return E_OUTOFMEMORY;
try {
hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
if (SUCCEEDED(hr))
{
// Delete the old image, if any.
if (*ppbmih) delete[] (*ppbmih);
(*ppbmih) = (BITMAPINFOHEADER*)pBuffer;
}
else
{
delete [] pBuffer;
}
}
catch (...) {
delete [] pBuffer;
return E_OUTOFMEMORY;
}
}
return hr;
}
4.在客户去绘制位图
使用 SetDIBitsToDevice 函数即可,该例子仅仅是简单地把位图画到客户区的左上角,而不考虑窗口的大小:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (pbmi)
{
int result = SetDIBitsToDevice(hdc, 0, 0,
pbmi->biWidth,
pbmi->biHeight,
0, 0, 0,
pbmi->biHeight,
pBuffer,
reinterpret_cast<BITMAPINFO*>(pbmi),
DIB_RGB_COLORS);
}
EndPaint(hwnd, &ps);
}
break;
- 截图(Grabbing a Poster Frame)
- codeforces 412A Poster
- Codeforces 412A Poster(贪心)
- poster
- Adding a frame (C++)
- Grabbing cheap Football Jerseys
- Banner Grabbing: Enumeration & Exploitation
- Poster Design
- A-frame vr的初学
- A-Frame WebVR开发入门教程
- A-Frame使用方法 教程 API
- Direct Show: Grabbing Media Samples
- Mac截图基本方法a
- 第四章 A Frame with a View
- A-Frame WEB VR框架初体验
- tf教程(四):Adding a frame
- [A-frame文档学习笔记]HTML&Primitives
- 通过target为a标签指定frame
- Q.931呼叫信令
- 对等网络中主流分布式哈希算法比较分析
- 活用设计模式
- linux 下的光盘拷贝
- 用java实现对数组的排列组合
- 截图(Grabbing a Poster Frame)
- 利用myeclipse 生成Hibernate Mapping文件
- MVC中严重丢失的概念
- VC论坛
- ftp服务
- jcst
- 安装ubuntu server版本全记录(1)
- WEB 考试,系统
- 神奇的域名