DirectShow: Video-Preview и Image (с рабочим кодом) - PullRequest
1 голос
/ 03 марта 2010

Вопросы / проблемы

  • Если кто-то может порекомендовать мне хороший бесплатный хостинг, я могу предоставить весь файл проекта.
  • Как упомянуто в тексте ниже, метод TakePicture () не работает должным образом на устройстве HTC HD 2. Было бы неплохо, если бы кто-то мог взглянуть на приведенный ниже код и сказать мне, правильно ли это или нет, что я делаю.

Введение

Недавно я задал вопрос об отображении предварительного просмотра видео, съемке изображения с камеры и вращении видеопотока с помощью DirectShow. Сложность этой темы в том, что очень трудно найти хорошие примеры и документацию, а сам фреймворк очень трудно понять для новичка в программировании Windows и C ++ в целом.

Тем не менее мне удалось создать класс, который реализует большинство этих функций и, вероятно, работает с большинством мобильных устройств. Возможно, потому что реализация DirectShow во многом зависит от самого устройства. Я мог протестировать его только с HTC HD и HTC HD2, которые известны как несовместимые.

HTC HD

  • Работа: предварительный просмотр видео, запись фотографии в файл
  • Не работает: установить разрешение видео (CRASH), установить разрешение фотографии (низкое качество)

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

Ответы [ 2 ]

1 голос
/ 08 ноября 2010

К сожалению, я не могу поделиться решением здесь по юридическим причинам.

Тем не менее, я могу вам сказать, что захват видео и изображений с поддержкой полного разрешения возможен на HTC HD 2 без использования специальных библиотек HTC HD.

Подсказка: вам, вероятно, понадобится NULL-рендер.

0 голосов
/ 14 августа 2014

Недавно я столкнулся с проблемой, используя такой подход, когда снимок сработал в первый раз, но во втором не получилось. Проблема была похожа на вашу, где установка разрешения вызывала сбой FindPin, но он делал это только во второй раз.

Исправлено: выпуск объекта конфигурации в конце SetResolution!

config->Release();

После этого он работал каждый раз.

...