DirectShow:视频预览和图像(带工作代码)

时间:2010-03-03 14:08:55

标签: c++ windows-mobile video-streaming directshow

问题/问题

  • 如果有人可以推荐我一个好的免费托管网站,我可以提供整个项目文件。
  • 如下文所述,TakePicture()方法在HTC HD 2设备上无法正常工作。如果有人能够查看下面的代码,并告诉我,我正在做的事情是对还是错,那就太好了。

简介

我最近询问question关于显示视频预览,拍摄相机图像以及使用DirectShow旋转视频流的问题。关于这个主题的棘手问题是,很难找到好的例子,对于那些不熟悉Windows编程和C ++的人来说,文档和框架本身很难理解。

尽管如此,我设法创建了一个实现大部分功能的类,并且可能适用于大多数移动设备。可能是因为DirectShow实现很大程度上取决于设备本身。我只能用HTC HD和HTC HD2进行测试,它们被认为是非常不兼容的。

HTC HD

  • 工作:视频预览,将照片写入文件
  • 不工作:设置视频分辨率(CRASH),设置照片分辨率(低质量)

HTC HD 2

  • 工作:设置视频分辨率,设置照片分辨率
  • 有问题:视频预览已旋转
  • 不工作:将照片写入文件

为了通过提供一个工作示例让其他人更容易,我决定分享到目前为止我所拥有的一切。为简单起见,我删除了所有错误处理。就文档而言,我建议您阅读MSDN documentation,之后代码非常简单。

void Camera::Init()
{
    CreateComObjects();

    _captureGraphBuilder->SetFiltergraph(_filterGraph);

    InitializeVideoFilter();
    InitializeStillImageFilter();
}

Dipslay视频预览(与任何经过测试的手持设备一起使用):

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

2 个答案:

答案 0 :(得分:1)

不幸的是,由于法律原因,我不能在这里分享解决方案。

尽管如此,我可以告诉您,在不使用HTC HD特定库的情况下,可以在HTC HD 2上支持全分辨率的视频和图像捕获。

提示:您可能需要一个NULL渲染器。

答案 1 :(得分:0)

我最近使用这样的方法遇到了问题,其中快照第一次工作,但第二次失败。问题与你的问题类似,设置分辨率会导致FindPin失败,但它只是第二次失败。

修复是在SetResolution结束时释放配置对象!

config->Release();

之后,每次都有效。