Спроектировать и построить структуру для приложения виртуальной веб-камеры на основе Microsoft DirectShow в Windows 10

Я пытаюсь создать простейшее приложение для виртуальной веб-камеры, которое может отображать файл изображения в моей локальной файловой системе.

После первоначального исследования ссылок на stackoverflow и просмотра исходный код OBS Studio Я получил некоторое представление, как мне этого добиться.

  1. Мне нужно будет использовать Microsoft DirectShow.

  2. Мне нужно было бы разработать один исходный фильтр, который работал бы как фильтр захвата, используя IBaseFilter

  3. Мне нужно было бы разработать другой исходный фильтр, который работал бы как выходной фильтр или фильтр виртуальной веб-камеры. Мне нужно было бы скомпилировать этот фильтр как .dll и необходимо будет зарегистрироваться с помощью regsvr32.exe
    Как указано на https://docs.microsoft.com/en-us/windows/win32/directshow/building-directshow-filters

  4. Мне нужно было бы создать фильтр График и Фильтр захвата График с использованием CoCreateInstance лайка

    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IFilterGraph, (void **)&graph);

    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&builder);

  5. Затем мне нужно будет добавить эти фильтры в Filter Graph

  6. Тогда я бы установил Filter Graph на Capture Filter Graph, как hr = builder->SetFiltergraph(graph);

Вот моя путаница:
После этих шагов я не уверен, что мне нужно обернуть эти Graph Filters и Capture Graph Filter в одно приложение, которое будет иметь метод main, и скомпилировать его для получения файла .exe, или мне нужно скомпилировать как другое .dll файл.

Или как мне завершить эти шаги, чтобы создать окончательное приложение?


person Alok Singh Mahor    schedule 09.01.2021    source источник
comment
stackoverflow.com/questions/ 3481713 /   -  person Prashant Singh    schedule 09.01.2021
comment
Вопросу недостает целостности. В названии написано, что вам нужна виртуальная веб-камера. Предполагается, что вам нужен код для реализации камеры, которая не существует физически (виртуальная). В теле вопроса вы пытаетесь делать не связанные между собой вещи: показывать видео, создавать приложение и т. Д.   -  person Roman R.    schedule 12.01.2021
comment
Думаю, он просто хочет захватить с веб-камеры.   -  person user123    schedule 12.01.2021
comment
@RomanR .: Да, я хочу создать простейшее приложение для виртуальной веб-камеры, которое может выводить любое изображение или видео на виртуальную камеру. Эта виртуальная камера должна быть видна как видеоустройство на онлайн-встречах, таких как Google Meet или Zoom. Поэтому я предположил, что для этого мне нужно создать два фильтра. один для виртуальной камеры, а другой для передачи кадра изображений или видео на эту виртуальную камеру.   -  person Alok Singh Mahor    schedule 13.01.2021
comment
@ user123: Нет, я хочу создать виртуальную веб-камеру, которая будет отображать изображение или видео из локального файла   -  person Alok Singh Mahor    schedule 13.01.2021
comment
Я неправильно понял твой вопрос. Я ничем здесь не могу помочь. В любом случае вам обязательно стоит использовать MediaFoundation. Я пытался использовать DirectShow для нескольких проектов, и когда я узнал о MediaFoundation, я понял, насколько я был неправ, работая с такой устаревшей библиотекой (DirectShow).   -  person user123    schedule 13.01.2021
comment
@ user123: Я полагаю, что в настоящее время MediaFoundation недостаточно развит, чтобы предоставить все возможности DirectShow. Можем ли мы создать виртуальную веб-камеру с помощью MediaFoundation?   -  person Alok Singh Mahor    schedule 13.01.2021
comment
Он определенно достаточно зрелый! Это рекомендуемая библиотека для использования в расширенных приложениях C ++ WinAPI. Он рекомендован Microsoft уже давно. Это итерация DirectShow. Это не переход на более раннюю версию DirectShow.   -  person user123    schedule 13.01.2021
comment
@ user123: Мне бы хотелось использовать MediaFoundation, но я не уверен, смогу ли я создать устройство виртуальной камеры с помощью MediaFoundation   -  person Alok Singh Mahor    schedule 13.01.2021


Ответы (2)


Я хочу создать простейшее приложение для виртуальной веб-камеры, которое может выводить любое изображение или видео на виртуальную камеру. Эта виртуальная камера должна быть видна как видеоустройство на онлайн-встречах, таких как Google Meet или Zoom.

В Windows нет поддержки виртуальных веб-камер в качестве унифицированного API, и то, что вы пытаетесь достичь, в целом возможно, но намного сложнее, чем вопрос настройки.

Задачу можно разделить на три части, и вы сможете найти прошлые вопросы StackOverflow, которые подробно описывают все три (некоторые ссылки приведены ниже).

Во-первых, необходимо решить проблему интеграции виртуальной камеры в стороннее программное обеспечение. Согласно заявлению, с которого я начал, API ОС не предлагает возможности для универсального интерфейса виртуальной камеры с точки зрения точки расширения ОС, которая позволяет стороннему приложению видеть новое устройство камеры.

Популярным способом внедрения поддельной камеры в приложения является виртуальный источник видео DirectShow (и, соответственно, Vivek's Исходный код VCam).

Схема из Регистрация сетевого видеопотока в качестве виртуальной камеры описывает API-интерфейсы, используемые приложениями для работы с камерами, и иллюстрирует ограничения виртуальные камеры DirectShow, в частности, почему они не видны всем приложениям с поддержкой видео в Windows.

См. Также вопросы Виртуальная камера водителя не распознается браузером и Фильтр DirectShow не отображается как устройство ввода данных.

В общем, чтобы разработать виртуальную веб-камеру для всех и любого приложения в Windows, вам потребуется разработать драйвер, с чем немногие готовы иметь дело.

Newer Media Foundation API не предлагает ничего, что могло бы помочь с функциональностью виртуальной веб-камеры.

Во-вторых, вам необходимо определить метод внедрения видеокадров в любую виртуальную камеру, которую вы разрабатываете. Нет необходимости использовать DirectShow или Media Foundation, потому что в конце концов все, что вам нужно, - это отправить видеокадры на серверную часть реализации вашей виртуальной камеры, и вы можете использовать любой удобный метод.

Использование DirectShow для этой задачи в целом имеет смысл, но вам это не нужно. Если вы не знакомы с API и начинаете с основ создания графа фильтров, то вполне вероятно, что проще выбрать решение, отличное от DirectShow. Если вам нужно добавить в ленту реальное изображение с веб-камеры, вы можете сделать это, в частности, с помощью Media Foundation. Если вы планируете использовать своего рода сервисы GPU, Media Foundation будет лучшим API для повторного использования. DirectShow по-прежнему остается хорошим вариантом в качестве API для построения вашего конвейера.

В-третьих, часто возникает вопрос о межпроцессном взаимодействии для связи реализации виртуальной камеры и источника видео. В некоторых случаях в этом нет необходимости, но чаще об этом просто забывают.

Виртуальная камера DirectShow (или виртуальная камера Media Foundation, если вы, например, будете объезжать) работает в контексте процесса потребления камеры, и к камерам в целом можно получить доступ из нескольких приложений, в том числе одновременно. Довольно часто вы ожидаете создания видео из другого [единственного] приложения, в том числе в случае применения разблокировки битности / архитектуры, поэтому вы должны позаботиться о передаче данных между процессами. Если вы пытаетесь разработать драйвер для виртуальной камеры, у вас тоже будет такая же задача.

Я упомянул аспекты этого в вопросе MSDN: Как реализовать исходный фильтр для разделения видео с камеры на основе vcam Vivek?, затем там Прочтите входное изменение USB-камеры и отправьте выходные данные на виртуальную камеру в Windows, а также там Как создать фильтр Directshow?.

В общем, вопрос не в настройке проекта. Вместо этого, это набор довольно сложных проблем, которые необходимо решить (которые, тем не менее, выполнимы, и мы видим примеры этого).

person Roman R.    schedule 13.01.2021
comment
Спасибо, что так хорошо поделились своими мыслями :) - person Alok Singh Mahor; 13.01.2021

DirectShow устарел. Вместо этого вы должны использовать Microsoft MediaFoundation. Это хорошо задокументировано и хорошо работает. Следующий код будет работать для захвата с веб-камеры:

void Webcam::StartRecording()
{
    HRESULT hr = MFStartup(MF_VERSION);

    hr = MFCreateAttributes(&pConfig, 1);
    if (FAILED(hr)){
        std::cout << "Failed to create attribute store";
    }

    hr = pConfig->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
    if (FAILED(hr)){
        std::cout << "Failed to request capture devices";
    }

    hr = MFEnumDeviceSources(pConfig, &ppDevices, &count);
    if (FAILED(hr)){
        std::cout << "Failed to enumerate capture devices";
    }

    hr = ppDevices[0]->ActivateObject(IID_PPV_ARGS(&pSource));
    if (FAILED(hr)){
        std::cout << "Failed to connect camera to source";
    }

    hr = MFCreateSourceReaderFromMediaSource(pSource, pConfig, &pReader);
    if (FAILED(hr)){
        std::cout << "Failed to create source reader";
    }

    IMFMediaType* pType = NULL;
    DWORD dwMediaTypeIndex = 0;
    DWORD dwStreamIndex = 0;
    hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
    LPVOID representation;
    pType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, &representation);
    GUID subType = ((AM_MEDIA_TYPE*)representation)->subtype;
    BYTE* pbFormat = ((AM_MEDIA_TYPE*)representation)->pbFormat;
    GUID formatType = ((AM_MEDIA_TYPE*)representation)->formattype;
    if (subType == MEDIASUBTYPE_YUY2) { std::cout << 1; };
    RECT rect;
    if (formatType == FORMAT_DvInfo) { std::cout << 1; }
    if (formatType == FORMAT_MPEG2Video) { std::cout << 2; }
    if (formatType == FORMAT_MPEGStreams) { std::cout << 3; }
    if (formatType == FORMAT_MPEGVideo) { std::cout << 4; }
    if (formatType == FORMAT_None) { std::cout << 5; }
    if (formatType == FORMAT_VideoInfo) { std::cout << 6; }
    if (formatType == FORMAT_VideoInfo2){
        rect = ((VIDEOINFOHEADER2*)pbFormat)->rcSource;
    }
    if (formatType == FORMAT_WaveFormatEx) { std::cout << 8; }
    if (formatType == GUID_NULL) { std::cout << 9; }

    int videoWidth = ((VIDEOINFOHEADER2*)pbFormat)->bmiHeader.biWidth;
    int videoHeight = ((VIDEOINFOHEADER2*)pbFormat)->bmiHeader.biHeight;

    IsRecording = true;
    DWORD streamIndex, flags;
    LONGLONG llTimeStamp;
    IMFSample* pSample;

    while (IsRecording){
        hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);
        if (FAILED(hr)){
            std::cout << "Failed to get image from camera";
        }
        if (pSample != NULL){
            IMFMediaBuffer* pBuffer;
            pSample->ConvertToContiguousBuffer(&pBuffer);
            unsigned char* data;
            DWORD length;
            pBuffer->GetCurrentLength(&length);
            HRESULT hr = pBuffer->Lock(&data, NULL, &length);
            if (FAILED(hr)){
                std::cout << "Failed to get data from buffer";
            }

            HDC hdc = GetDC(hwnd);
            HBITMAP bitmap = CreateCompatibleBitmap(hdc, 640, 480);
            BITMAPINFOHEADER header = { sizeof(BITMAPINFOHEADER), 640, 480, 1, 24, BI_RGB, 0, NULL, NULL, NULL, NULL };
            BITMAPINFO info = { header, NULL };
            SetDIBits(hdc, bitmap, 0, 480, &rgb[0], &info, DIB_RGB_COLORS);

            HIMAGELIST imageList = ImageList_Create(640, 480, ILC_COLOR24, 1, 500);
            if (bitmap != NULL) {
                ImageList_Add(imageList, bitmap, NULL);
                BOOL drawn = ImageList_Draw(imageList, 0, hdc, 0, 0, ILD_IMAGE);
                
                DeleteObject(bitmap);
            }
            else {
                std::cout << "Failed to create bitmap" << std::endl;
            }
            ImageList_Destroy(imageList);
            DeleteObject(hdc);
            pBuffer->Unlock();
            pBuffer->Release();
            pSample->Release();
        }
    }
    pSource->Stop();
    pSource = NULL;
    MFShutdown();
}

Возможно, вам потребуется преобразовать буфер данных в формат RGB, прежде чем отправлять его в список изображений. Большинство современных камер выводят в формате RGB, но моя старая веб-камера на моем ноутбуке выводит YUY2. Если вам нужно конвертировать из YUY2 в RGB, не стесняйтесь спрашивать. Возможно, есть лучший код, чтобы сделать то же самое, но этот код работает хорошо. Это дает вам возможность контролировать изображение. Вы, вероятно, могли бы вместо этого показать изображение в статическом элементе управления. image_list позволяет вам добавлять несколько изображений и изменять их размер по желанию.

person user123    schedule 12.01.2021
comment
Я не хочу снимать с веб-камеры. Я хочу создать виртуальную веб-камеру - person Alok Singh Mahor; 13.01.2021