DirectShow:视频预览和图像(带有工作代码)
疑问/问题
- 如果有人可以向我推荐一个好的免费托管网站,我可以提供整个项目文件。
- 如下文所述,TakePicture() 方法在 HTC HD 2 设备上无法正常工作。如果有人可以查看下面的代码并告诉我我正在做的事情是对还是错,那就太好了。
简介
我最近问了一个关于显示视频预览的问题,相机图像并使用 DirectShow 旋转视频流。该主题的棘手之处在于,对于刚接触 Windows 编程和 C++ 的人来说,很难找到好的示例,而且文档和框架本身也很难理解。
尽管如此,我还是设法创建了一个类,它实现了大部分功能,并且可能适用于大多数移动设备。可能是因为 DirectShow 的实现很大程度上取决于设备本身。我只能用 HTC HD 和 HTC HD2 来测试,这两款手机被认为非常不兼容。
HTC HD
- 工作:视频预览、将照片写入文件
- 不工作:设置视频分辨率(崩溃)、设置照片分辨率(低质量)
HTC HD 2
- 工作:设置视频分辨率、设置照片分辨率
- 问题:视频预览旋转
- 不工作:写入照片为了
通过提供一个工作示例让其他人更容易,我决定在下面分享我到目前为止所得到的所有内容。为了简单起见,我删除了所有错误处理。就文档而言,我建议您阅读 MSDN 文档,之后下面的代码就非常简单了。
void Camera::Init()
{
CreateComObjects();
_captureGraphBuilder->SetFiltergraph(_filterGraph);
InitializeVideoFilter();
InitializeStillImageFilter();
}
显示视频预览(适用于任何经过测试的手持设备):
void Camera::DisplayVideoPreview(HWND windowHandle)
{
IVideoWindow *_vidWin;
_filterGraph->QueryInterface(IID_IMediaControl,(void **) &_mediaControl);
_filterGraph->QueryInterface(IID_IVideoWindow, (void **) &_vidWin);
_videoCaptureFilter->QueryInterface(IID_IAMVideoControl,
(void**) &_videoControl);
_captureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, _videoCaptureFilter, NULL, NULL);
CRect rect;
long width, height;
GetClientRect(windowHandle, &rect);
_vidWin->put_Owner((OAHWND)windowHandle);
_vidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
_vidWin->get_Width(&width);
_vidWin->get_Height(&height);
height = rect.Height();
_vidWin->put_Height(height);
_vidWin->put_Width(rect.Width());
_vidWin->SetWindowPosition(0,0, rect.Width(), height);
_mediaControl->Run();
}
HTC HD2:如果设置 SetPhotoResolution(),则调用 FindPin 将返回 E_FAIL。如果没有,它将创建一个充满空字节的文件。 HTC HD:工作
void Camera::TakePicture(WCHAR *fileName)
{
CComPtr<IFileSinkFilter> fileSink;
CComPtr<IPin> stillPin;
CComPtr<IUnknown> unknownCaptureFilter;
CComPtr<IAMVideoControl> videoControl;
_imageSinkFilter.QueryInterface(&fileSink);
fileSink->SetFileName(fileName, NULL);
_videoCaptureFilter.QueryInterface(&unknownCaptureFilter);
_captureGraphBuilder->FindPin(unknownCaptureFilter, PINDIR_OUTPUT,
&PIN_CATEGORY_STILL, &MEDIATYPE_Video, FALSE, 0, &stillPin);
_videoCaptureFilter.QueryInterface(&videoControl);
videoControl->SetMode(stillPin, VideoControlFlag_Trigger);
}
设置分辨率:在 HTC HD2 上运行良好。 HTC HD 不允许 SetVideoResolution() 并且仅提供一种低分辨率照片分辨率:
void Camera::SetVideoResolution(int width, int height)
{
SetResolution(true, width, height);
}
void Camera::SetPhotoResolution(int width, int height)
{
SetResolution(false, width, height);
}
void Camera::SetResolution(bool video, int width, int height)
{
IAMStreamConfig *config;
config = NULL;
if (video)
{
_captureGraphBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, _videoCaptureFilter, IID_IAMStreamConfig,
(void**) &config);
}
else
{
_captureGraphBuilder->FindInterface(&PIN_CATEGORY_STILL,
&MEDIATYPE_Video, _videoCaptureFilter, IID_IAMStreamConfig,
(void**) &config);
}
int resolutions, size;
VIDEO_STREAM_CONFIG_CAPS caps;
config->GetNumberOfCapabilities(&resolutions, &size);
for (int i = 0; i < resolutions; i++)
{
AM_MEDIA_TYPE *mediaType;
if (config->GetStreamCaps(i, &mediaType,
reinterpret_cast<BYTE*>(&caps)) == S_OK )
{
int maxWidth = caps.MaxOutputSize.cx;
int maxHeigth = caps.MaxOutputSize.cy;
if(maxWidth == width && maxHeigth == height)
{
VIDEOINFOHEADER *info =
reinterpret_cast<VIDEOINFOHEADER*>(mediaType->pbFormat);
info->bmiHeader.biWidth = maxWidth;
info->bmiHeader.biHeight = maxHeigth;
info->bmiHeader.biSizeImage = DIBSIZE(info->bmiHeader);
config->SetFormat(mediaType);
DeleteMediaType(mediaType);
break;
}
DeleteMediaType(mediaType);
}
}
}
用于构建过滤器图和创建 COM 对象的其他方法:
void Camera::CreateComObjects()
{
CoInitialize(NULL);
CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2, (void **) &_captureGraphBuilder);
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **) &_filterGraph);
CoCreateInstance(CLSID_VideoCapture, NULL, CLSCTX_INPROC,
IID_IBaseFilter, (void**) &_videoCaptureFilter);
CoCreateInstance(CLSID_IMGSinkFilter, NULL, CLSCTX_INPROC,
IID_IBaseFilter, (void**) &_imageSinkFilter);
}
void Camera::InitializeVideoFilter()
{
_videoCaptureFilter->QueryInterface(&_propertyBag);
wchar_t deviceName[MAX_PATH] = L"\0";
GetDeviceName(deviceName);
CComVariant comName = deviceName;
CPropertyBag propertyBag;
propertyBag.Write(L"VCapName", &comName);
_propertyBag->Load(&propertyBag, NULL);
_filterGraph->AddFilter(_videoCaptureFilter,
L"Video Capture Filter Source");
}
void Camera::InitializeStillImageFilter()
{
_filterGraph->AddFilter(_imageSinkFilter, L"Still image filter");
_captureGraphBuilder->RenderStream(&PIN_CATEGORY_STILL,
&MEDIATYPE_Video, _videoCaptureFilter, NULL, _imageSinkFilter);
}
void Camera::GetDeviceName(WCHAR *deviceName)
{
HRESULT hr = S_OK;
HANDLE handle = NULL;
DEVMGR_DEVICE_INFORMATION di;
GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, 0x93, 0x3E,
0x4D, 0x7E, 0x3C, 0x86 };
di.dwSize = sizeof(di);
handle = FindFirstDevice(DeviceSearchByGuid, &guidCamera, &di);
StringCchCopy(deviceName, MAX_PATH, di.szLegacyName);
}
完整头文件:
#ifndef __CAMERA_H__
#define __CAMERA_H__
class Camera
{
public:
void Init();
void DisplayVideoPreview(HWND windowHandle);
void TakePicture(WCHAR *fileName);
void SetVideoResolution(int width, int height);
void SetPhotoResolution(int width, int height);
private:
CComPtr<ICaptureGraphBuilder2> _captureGraphBuilder;
CComPtr<IGraphBuilder> _filterGraph;
CComPtr<IBaseFilter> _videoCaptureFilter;
CComPtr<IPersistPropertyBag> _propertyBag;
CComPtr<IMediaControl> _mediaControl;
CComPtr<IAMVideoControl> _videoControl;
CComPtr<IBaseFilter> _imageSinkFilter;
void GetDeviceName(WCHAR *deviceName);
void InitializeVideoFilter();
void InitializeStillImageFilter();
void CreateComObjects();
void SetResolution(bool video, int width, int height);
};
#endif
Questions / Issues
- If someone can recommend me a good free hosting site I can provide the whole project file.
- As mentioned in the text below the TakePicture() method is not working properly on the HTC HD 2 device. It would be nice if someone could look at the code below and tell me if it is right or wrong what I'm doing.
Introduction
I recently asked a question about displaying a video preview, taking camera image and rotating a video stream with DirectShow. The tricky thing about the topic is, that it's very hard to find good examples and the documentation and the framework itself is very hard to understand for someone who is new to windows programming and C++ in general.
Nevertheless I managed to create a class that implements most of this features and probably works with most mobile devices. Probably because the DirectShow implementation depends a lot on the device itself. I could only test it with the HTC HD and HTC HD2, which are known as quite incompatible.
HTC HD
- Working: Video preview, writing photo to file
- Not working: Set video resolution (CRASH), set photo resolution (LOW quality)
HTC HD 2
- Working: Set video resolution, set photo resolution
- Problematic: Video Preview rotated
- Not working: Writing photo to file
To make it easier for others by providing a working example, I decided to share everything I have got so far below. I removed all of the error handling for the sake of simplicity. As far as documentation goes, I can recommend you to read the MSDN documentation, after that the code below is pretty straight forward.
void Camera::Init()
{
CreateComObjects();
_captureGraphBuilder->SetFiltergraph(_filterGraph);
InitializeVideoFilter();
InitializeStillImageFilter();
}
Dipslay a video preview (working with any tested handheld):
void Camera::DisplayVideoPreview(HWND windowHandle)
{
IVideoWindow *_vidWin;
_filterGraph->QueryInterface(IID_IMediaControl,(void **) &_mediaControl);
_filterGraph->QueryInterface(IID_IVideoWindow, (void **) &_vidWin);
_videoCaptureFilter->QueryInterface(IID_IAMVideoControl,
(void**) &_videoControl);
_captureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, _videoCaptureFilter, NULL, NULL);
CRect rect;
long width, height;
GetClientRect(windowHandle, &rect);
_vidWin->put_Owner((OAHWND)windowHandle);
_vidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
_vidWin->get_Width(&width);
_vidWin->get_Height(&height);
height = rect.Height();
_vidWin->put_Height(height);
_vidWin->put_Width(rect.Width());
_vidWin->SetWindowPosition(0,0, rect.Width(), height);
_mediaControl->Run();
}
HTC HD2: If set SetPhotoResolution() is called FindPin will return E_FAIL. If not, it will create a file full of null bytes. HTC HD: Works
void Camera::TakePicture(WCHAR *fileName)
{
CComPtr<IFileSinkFilter> fileSink;
CComPtr<IPin> stillPin;
CComPtr<IUnknown> unknownCaptureFilter;
CComPtr<IAMVideoControl> videoControl;
_imageSinkFilter.QueryInterface(&fileSink);
fileSink->SetFileName(fileName, NULL);
_videoCaptureFilter.QueryInterface(&unknownCaptureFilter);
_captureGraphBuilder->FindPin(unknownCaptureFilter, PINDIR_OUTPUT,
&PIN_CATEGORY_STILL, &MEDIATYPE_Video, FALSE, 0, &stillPin);
_videoCaptureFilter.QueryInterface(&videoControl);
videoControl->SetMode(stillPin, VideoControlFlag_Trigger);
}
Set resolution: Works great on HTC HD2. HTC HD won't allow SetVideoResolution() and only offers one low resolution photo resolution:
void Camera::SetVideoResolution(int width, int height)
{
SetResolution(true, width, height);
}
void Camera::SetPhotoResolution(int width, int height)
{
SetResolution(false, width, height);
}
void Camera::SetResolution(bool video, int width, int height)
{
IAMStreamConfig *config;
config = NULL;
if (video)
{
_captureGraphBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, _videoCaptureFilter, IID_IAMStreamConfig,
(void**) &config);
}
else
{
_captureGraphBuilder->FindInterface(&PIN_CATEGORY_STILL,
&MEDIATYPE_Video, _videoCaptureFilter, IID_IAMStreamConfig,
(void**) &config);
}
int resolutions, size;
VIDEO_STREAM_CONFIG_CAPS caps;
config->GetNumberOfCapabilities(&resolutions, &size);
for (int i = 0; i < resolutions; i++)
{
AM_MEDIA_TYPE *mediaType;
if (config->GetStreamCaps(i, &mediaType,
reinterpret_cast<BYTE*>(&caps)) == S_OK )
{
int maxWidth = caps.MaxOutputSize.cx;
int maxHeigth = caps.MaxOutputSize.cy;
if(maxWidth == width && maxHeigth == height)
{
VIDEOINFOHEADER *info =
reinterpret_cast<VIDEOINFOHEADER*>(mediaType->pbFormat);
info->bmiHeader.biWidth = maxWidth;
info->bmiHeader.biHeight = maxHeigth;
info->bmiHeader.biSizeImage = DIBSIZE(info->bmiHeader);
config->SetFormat(mediaType);
DeleteMediaType(mediaType);
break;
}
DeleteMediaType(mediaType);
}
}
}
Other methods used to build the filter graph and create the COM objects:
void Camera::CreateComObjects()
{
CoInitialize(NULL);
CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2, (void **) &_captureGraphBuilder);
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **) &_filterGraph);
CoCreateInstance(CLSID_VideoCapture, NULL, CLSCTX_INPROC,
IID_IBaseFilter, (void**) &_videoCaptureFilter);
CoCreateInstance(CLSID_IMGSinkFilter, NULL, CLSCTX_INPROC,
IID_IBaseFilter, (void**) &_imageSinkFilter);
}
void Camera::InitializeVideoFilter()
{
_videoCaptureFilter->QueryInterface(&_propertyBag);
wchar_t deviceName[MAX_PATH] = L"\0";
GetDeviceName(deviceName);
CComVariant comName = deviceName;
CPropertyBag propertyBag;
propertyBag.Write(L"VCapName", &comName);
_propertyBag->Load(&propertyBag, NULL);
_filterGraph->AddFilter(_videoCaptureFilter,
L"Video Capture Filter Source");
}
void Camera::InitializeStillImageFilter()
{
_filterGraph->AddFilter(_imageSinkFilter, L"Still image filter");
_captureGraphBuilder->RenderStream(&PIN_CATEGORY_STILL,
&MEDIATYPE_Video, _videoCaptureFilter, NULL, _imageSinkFilter);
}
void Camera::GetDeviceName(WCHAR *deviceName)
{
HRESULT hr = S_OK;
HANDLE handle = NULL;
DEVMGR_DEVICE_INFORMATION di;
GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, 0x93, 0x3E,
0x4D, 0x7E, 0x3C, 0x86 };
di.dwSize = sizeof(di);
handle = FindFirstDevice(DeviceSearchByGuid, &guidCamera, &di);
StringCchCopy(deviceName, MAX_PATH, di.szLegacyName);
}
Full header file:
#ifndef __CAMERA_H__
#define __CAMERA_H__
class Camera
{
public:
void Init();
void DisplayVideoPreview(HWND windowHandle);
void TakePicture(WCHAR *fileName);
void SetVideoResolution(int width, int height);
void SetPhotoResolution(int width, int height);
private:
CComPtr<ICaptureGraphBuilder2> _captureGraphBuilder;
CComPtr<IGraphBuilder> _filterGraph;
CComPtr<IBaseFilter> _videoCaptureFilter;
CComPtr<IPersistPropertyBag> _propertyBag;
CComPtr<IMediaControl> _mediaControl;
CComPtr<IAMVideoControl> _videoControl;
CComPtr<IBaseFilter> _imageSinkFilter;
void GetDeviceName(WCHAR *deviceName);
void InitializeVideoFilter();
void InitializeStillImageFilter();
void CreateComObjects();
void SetResolution(bool video, int width, int height);
};
#endif
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不幸的是,由于法律原因,我无法在这里分享解决方案。
不过,我可以告诉您,在 HTC HD 2 上无需使用 HTC HD 特定库即可实现全分辨率支持的视频和图像捕获。
提示:您可能需要一个 NULL 渲染器。
Unfortunately I can't share the solution here, because of legal reasons.
Nevertheless I can tell you that video and image capturing with full resolution support is possible on the HTC HD 2 without using the HTC HD specific libraries.
Hint: You will probably need a NULL renderer.
我最近使用这样的方法遇到了一个问题,快照第一次可以工作,但第二次失败。这个问题和你的类似,设置分辨率会导致FindPin失败,但只第二次就出现了。
修复方法是在 SetResolution 结束时释放配置对象!
之后,每次都有效。
I recently ran into a problem using an approach like this, where the snapshot would work the first time around, but failed the second. The problem was similar to yours, where setting the resolution would cause the FindPin to fail, but it only did it the second time.
The fix was to release the config object at the end of SetResolution!
After that, it worked every time.