[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayer、 IMFMediaEngine 和 媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayer、 IMFMediaEngine 和 Media Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]
[此 API 不受支持,将来可能会更改或不可用。]
本主题是 抓取海报框架的步骤 3。
下一步是实现 GetBitmap 函数,该函数使用媒体检测器抓取海报帧。 在此函数中,执行以下步骤:
- 创建媒体检测器。
- 指定媒体文件。
- 检查文件中的每个流。 如果是视频流,请获取视频的本机尺寸。
- 获取海报框架,指定寻道时间和目标尺寸。
通过调用 CoCreateInstance 创建媒体检测器对象:
CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));
使用 IMediaDet::p ut_Filename 方法指定文件名。 此方法采用 BSTR 参数。
hr = pDet->put_Filename(bstrFilename);
获取流的数量,并循环访问检查媒体类型的每个流。 IMediaDet::get_OutputStreams 方法检索流的数量,IMediaDet::p ut_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) // Found a video stream.
{
bFound = true;
break;
}
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;
如果未找到视频流,则函数将退出。
在前面的代码中, IMediaDet::get_StreamType 方法仅返回主类型 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;
// We want the absolute height, and don't care about up-down orientation.
if (height < 0) height *= -1;
}
else {
return VFW_E_INVALIDMEDIATYPE; // This should not happen, in theory.
}
FreeMediaType(mt);
get_StreamMediaType 方法分配调用方必须释放的格式块。 此示例使用基类库中的 FreeMediaType 函数。
现在,你已准备好获取海报框架。 首先使用缓冲区的 NULL 指针调用 IMediaDet::GetBitmapBits 方法:
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;
}
相关主题