Источник пользовательского носителя Frame Server
В этом разделе содержатся сведения о реализации пользовательского источника мультимедиа в архитектуре сервера кадров.
Параметры AV Stream и пользовательского источника мультимедиа
При принятии решения о том, как обеспечить поддержку потока захвата видео в архитектуре Frame Server, существует два main варианта: AV Stream и Пользовательский источник мультимедиа.
Модель AV Stream — это стандартная модель драйвера камеры с использованием драйвера miniport AV Stream (драйвер режима ядра). Как правило, драйверы AV Stream делятся на две main категории: драйверы на основе MIPI и драйверы класса USB Video.
Для параметра Пользовательский источник мультимедиа модель драйвера может быть полностью пользовательской (собственной) или быть основана на нетрадиционном источнике камеры (например, файлов или сетевых источниках).
Драйвер AV Stream
Main преимуществом подхода av Stream Driver является то, что PnP и управление питанием/Управление устройствами уже обрабатывается платформой AV Stream.
Однако это также означает, что базовым источником должно быть физическое устройство с драйвером режима ядра для взаимодействия с оборудованием. Для устройств UVC предоставляется драйвер класса Windows UVC 1.5, поэтому устройствам просто необходимо реализовать встроенное ПО.
Для устройств на основе MIPI поставщику потребуется реализовать собственный драйвер мини-порта AV Stream.
Пользовательский источник мультимедиа
Для источников, драйвер устройства которых уже доступен (но не драйвер мини-порта AV Stream), или источников, использующих нетрадиционные камеры захвата, av Stream Driver может оказаться недоступным. Например, IP-камера, подключенная по сети, не поместится в модель драйвера AV Stream.
В таких ситуациях пользовательский источник мультимедиа, использующий модель Frame Server, будет альтернативой.
Компоненты | Пользовательский источник мультимедиа | Драйвер AV Stream |
---|---|---|
Управление питанием и PnP | Должен быть реализован исходным драйвером и (или) драйвером заглушки | Предоставляется платформой AV Stream |
Подключаемый модуль пользовательского режима | Недоступно. Пользовательский источник мультимедиа включает логику пользовательского режима oem/IHV. | DMFT, DMFT платформы и MFT0 для реализации прежних версий |
Группа датчиков | Поддерживается | Поддерживается |
Профиль камеры версии 2 | Поддерживается | Поддерживается |
Профиль камеры версии 1 | Не поддерживается | Поддерживается |
Требования к пользовательскому источнику мультимедиа
С появлением службы Камера Windows Frame Server (называемого сервером кадров) это можно сделать с помощью пользовательского источника мультимедиа. Для этого требуются два main компонента:
Пакет драйверов с запущенным драйвером, предназначенным для регистрации или включения интерфейса устройства камеры.
Библиотека DLL COM, в которой размещается пользовательский источник мультимедиа.
Первое требование необходимо для двух целей:
Процесс проверки, чтобы убедиться, что пользовательский источник мультимедиа установлен с помощью доверенного процесса (пакет драйвера требует сертификации WHQL).
Поддержка стандартного перечисления PnP и обнаружения "камеры".
Безопасность
Пользовательский источник мультимедиа для Frame Server отличается от универсального пользовательского источника мультимедиа с точки зрения безопасности следующим образом:
Фрейм-сервер настраиваемого источника мультимедиа выполняется как локальная служба (не следует путать с локальной системой; Локальная служба — это очень низкая привилегированная учетная запись на компьютерах с Windows.
Пользовательский источник мультимедиа Frame Server выполняется в сеансе 0 (сеанс системной службы) и не может взаимодействовать с рабочим столом пользователя.
Учитывая эти ограничения, пользовательские источники мультимедиа Frame Server не должны пытаться получить доступ к защищенным частям файловой системы или реестра. Как правило, доступ на чтение разрешен, а доступ на запись — нет.
Производительность
В рамках модели Сервера кадров существует два случая, когда экземпляры пользовательских источников мультимедиа будут создаваться сервером frame.
Во время создания или публикации группы датчиков.
Во время активации "камеры"
Создание группы датчиков обычно выполняется во время установки устройства и (или) цикла питания. Учитывая это, мы настоятельно рекомендуем, чтобы пользовательские источники мультимедиа избегали какой-либо значительной обработки во время их создания и откладывали любую такую деятельность на функцию IMFMediaSource::Start . Создание группы датчиков не будет пытаться запустить пользовательский источник мультимедиа, а просто запрашивает различные доступные потоки или типы мультимедиа и сведения об атрибутах источника или потока.
Драйвер заглушки
Существует два минимальных требования к пакету драйверов и драйверу заглушки.
Драйвер-заглушку можно записать с помощью WDF (UMDF или KMDF) или модели драйвера WDM.
Требования к драйверу:
- Зарегистрируйте интерфейс устройства "камера" (пользовательский источник мультимедиа) в категории KSCATEGORY_VIDEO_CAMERA , чтобы его можно было перечислить.
Примечание
Чтобы разрешить перечисление в устаревших приложениях DirectShow, драйвер должен также зарегистрироваться в KSCATEGORY_VIDEO и KSCATEGORY_CAPTURE.
- Добавьте запись реестра в узел интерфейса устройства (используйте директиву AddReg в разделе ДРАЙВЕР INF DDInstall.Interface ), в котором объявляется идентификатор CLSID с возможностью CoCreate для com-объекта Пользовательского источника мультимедиа. Его необходимо добавить, используя следующее имя значения реестра: CustomCaptureSourceClsid.
Это позволяет приложениям обнаруживать источник "камеры", а при активации уведомляет службу Frame Server о том, чтобы перехватить вызов активации и перенаправить его в источник coCreated Custom Media Source.
Пример INF
В следующем примере показан типичный INF-файл для драйвера заглушки пользовательского источника мультимедиа.
;/*++
;
;Module Name:
; SimpleMediaSourceDriver.INF
;
;Abstract:
; INF file for installing the Usermode SimpleMediaSourceDriver Driver
;
;Installation Notes:
; Using Devcon: Type "devcon install SimpleMediaSourceDriver.inf root\SimpleMediaSource" to install
;
;--*/
[Version]
Signature="$WINDOWS NT$"
Class=Sample
ClassGuid={5EF7C2A5-FF8F-4C1F-81A7-43D3CBADDC98}
Provider=%ProviderString%
DriverVer=01/28/2016,0.10.1234
CatalogFile=SimpleMediaSourceDriver.cat
PnpLockdown=1
[DestinationDirs]
DefaultDestDir = 13
UMDriverCopy=13 ; copy to DriverStore
CustomCaptureSourceCopy=13
; ================= Class section =====================
[ClassInstall32]
Addreg=SimpleMediaSourceClassReg
[SimpleMediaSourceClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-24
[SourceDisksNames]
1 = %DiskId1%,,,""
[SourceDisksFiles]
SimpleMediaSourceDriver.dll = 1,,
SimpleMediaSource.dll = 1,,
;*****************************************
; SimpleMFSource Install Section
;*****************************************
[Manufacturer]
%StdMfg%=Standard,NTamd64.10.0...25326
[Standard.NTamd64.10.0...25326]
%SimpleMediaSource.DeviceDesc%=SimpleMediaSourceWin11, root\SimpleMediaSource
;---------------- copy files
[SimpleMediaSourceWin11.NT]
Include=wudfrd.inf
Needs=WUDFRD.NT
CopyFiles=UMDriverCopy, CustomCaptureSourceCopy
AddReg = CustomCaptureSource.ComRegistration
[SimpleMediaSourceWin11.NT.Interfaces]
AddInterface = %KSCATEGORY_VIDEO_CAMERA%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface
AddInterface = %KSCATEGORY_VIDEO%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface
AddInterface = %KSCATEGORY_CAPTURE%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface
[CustomCaptureSourceInterface]
AddReg = CustomCaptureSourceInterface.AddReg, CustomCaptureSource.ComRegistration
[CustomCaptureSourceInterface.AddReg]
HKR,,CLSID,,%ProxyVCap.CLSID%
HKR,,CustomCaptureSourceClsid,,%CustomCaptureSource.CLSID%
HKR,,FriendlyName,,%CustomCaptureSource.Desc%
[CustomCaptureSource.ComRegistration]
HKR,Classes\CLSID\%CustomCaptureSource.CLSID%,,,%CustomCaptureSource.Desc%
HKR,Classes\CLSID\%CustomCaptureSource.CLSID%\InprocServer32,,%REG_EXPAND_SZ%,%CustomCaptureSource.Location%
HKR,Classes\CLSID\%CustomCaptureSource.CLSID%\InprocServer32,ThreadingModel,,Both
[UMDriverCopy]
SimpleMediaSourceDriver.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME
[CustomCaptureSourceCopy]
SimpleMediaSource.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME
;-------------- Service installation
[SimpleMediaSourceWin11.NT.Services]
Include=wudfrd.inf
Needs=WUDFRD.NT.Services
;-------------- WDF specific section -------------
[SimpleMediaSourceWin11.NT.Wdf]
UmdfService=SimpleMediaSource, SimpleMediaSource_Install
UmdfServiceOrder=SimpleMediaSource
[SimpleMediaSource_Install]
UmdfLibraryVersion=$UMDFVERSION$
ServiceBinary=%13%\SimpleMediaSourceDriver.dll
[Strings]
ProviderString = "Microsoft Corporation"
StdMfg = "(Standard system devices)"
DiskId1 = "SimpleMediaSource Disk \#1"
SimpleMediaSource.DeviceDesc = "SimpleMediaSource Capture Source" ; what you will see under SimpleMediaSource dummy devices
ClassName = "SimpleMediaSource dummy devices" ; device type this driver will install as in device manager
WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
KSCATEGORY_VIDEO_CAMERA = "{E5323777-F976-4f5b-9B55-B94699C46E44}"
KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}"
KSCATEGORY_VIDEO="{6994AD05-93EF-11D0-A3CC-00A0C9223196}"
ProxyVCap.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}"
CustomCaptureSource.Desc = "SimpleMediaSource Source"
CustomCaptureSource.ReferenceString = "CustomCameraSource"
CustomCaptureSource.CLSID = "{9812588D-5CE9-4E4C-ABC1-049138D10DCE}"
CustomCaptureSource.Location = "%13%\SimpleMediaSource.dll"
CustomCaptureSource.Binary = "SimpleMediaSource.dll"
REG_EXPAND_SZ = 0x00020000
Указанный выше пользовательский источник мультимедиа регистрируется в разделе KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE и KSCATEGORY_VIDEO_CAMERA , чтобы гарантировать, что "камера" может быть обнаружена любыми приложениями UWP и приложениями, не относящиеся к UWP, которые ищут стандартную RGB-камеру.
Если Пользовательский источник мультимедиа также предоставляет потоки, отличные от RGB (IR, Depth и т. д.), он также может при необходимости зарегистрировать в KSCATEGORY_SENSOR_CAMERA.
Примечание
Большинство веб-камер на основе USB предоставляют форматы YUY2 и MJPG. Из-за этого многие устаревшие приложения DirectShow написаны с предположением о доступности YUY2/MJPG. Чтобы обеспечить совместимость с таким приложением, рекомендуется, чтобы тип носителя YUY2 был доступен из пользовательского источника мультимедиа, если требуется устаревшая совместимость приложений.
Реализация драйвера заглушки
Помимо INF, заглушка драйвера также должна регистрировать и включать интерфейсы устройств камеры. Обычно это делается во время операции DRIVER_ADD_DEVICE .
См . DRIVER_ADD_DEVICE функцию обратного вызова для драйверов на основе WDM и функцию WdfDriverCreate для драйверов UMDF/KMDF.
Ниже приведен фрагмент кода заглушки драйвера UMDF, которая обрабатывает эту операцию:
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
DriverEntry initializes the driver and is the first routine called by the
system after the driver is loaded. DriverEntry specifies the other entry
points in the function driver, such as EvtDevice and DriverUnload.
Parameters Description:
DriverObject - represents the instance of the function driver that is loaded
into memory. DriverEntry must initialize members of DriverObject before it
returns to the caller. DriverObject is allocated by the system before the
driver is loaded, and it is released by the system after the system unloads
the function driver from memory.
RegistryPath - represents the driver specific path in the Registry.
The function driver can use the path to store driver related data between
reboots. The path does not store hardware instance specific data.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.
--*/
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDF_DRIVER_CONFIG_INIT(&config,
EchoEvtDeviceAdd
);
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
KdPrint(("Error: WdfDriverCreate failed 0x%x\n", status));
return status;
}
// ...
return status;
}
NTSTATUS
EchoEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
EvtDeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. We create and initialize a device object to
represent a new instance of the device.
Arguments:
Driver - Handle to a framework driver object created in DriverEntry
DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
UNREFERENCED_PARAMETER(Driver);
KdPrint(("Enter EchoEvtDeviceAdd\n"));
status = EchoDeviceCreate(DeviceInit);
return status;
}
NTSTATUS
EchoDeviceCreate(
PWDFDEVICE_INIT DeviceInit
/*++
Routine Description:
Worker routine called to create a device and its software resources.
Arguments:
DeviceInit - Pointer to an opaque init structure. Memory for this
structure will be freed by the framework when the WdfDeviceCreate
succeeds. Do not access the structure after that point.
Return Value:
NTSTATUS
--*/
{
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT deviceContext;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDFDEVICE device;
NTSTATUS status;
UNICODE_STRING szReference;
RtlInitUnicodeString(&szReference, L"CustomCameraSource");
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
//
// Register pnp/power callbacks so that we can start and stop the timer as the device
// gets started and stopped.
//
pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = EchoEvtDeviceSelfManagedIoStart;
pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = EchoEvtDeviceSelfManagedIoSuspend;
#pragma prefast(suppress: 28024, "Function used for both Init and Restart Callbacks")
pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = EchoEvtDeviceSelfManagedIoStart;
//
// Register the PnP and power callbacks. Power policy related callbacks will be registered
// later in SoftwareInit.
//
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
{
WDF_FILEOBJECT_CONFIG cameraFileObjectConfig;
WDF_OBJECT_ATTRIBUTES cameraFileObjectAttributes;
WDF_OBJECT_ATTRIBUTES_INIT(&cameraFileObjectAttributes);
cameraFileObjectAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
WDF_FILEOBJECT_CONFIG_INIT(
&cameraFileObjectConfig,
EvtCameraDeviceFileCreate,
EvtCameraDeviceFileClose,
WDF_NO_EVENT_CALLBACK);
WdfDeviceInitSetFileObjectConfig(
DeviceInit,
&cameraFileObjectConfig,
&cameraFileObjectAttributes);
}
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (NT_SUCCESS(status)) {
//
// Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
// inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
// device.h header file. This function will do the type checking and return
// the device context. If you pass a wrong object handle
// it will return NULL and assert if run under framework verifier mode.
//
deviceContext = WdfObjectGet_DEVICE_CONTEXT(device);
deviceContext->PrivateDeviceData = 0;
//
// Create a device interface so that application can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface(
device,
&CAMERA_CATEGORY,
&szReference // ReferenceString
);
if (NT_SUCCESS(status)) {
//
// Create a device interface so that application can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface(
device,
&CAPTURE_CATEGORY,
&szReference // ReferenceString
);
}
if (NT_SUCCESS(status)) {
//
// Create a device interface so that application can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface(
device,
&VIDEO_CATEGORY,
&szReference // ReferenceString
);
}
if (NT_SUCCESS(status)) {
//
// Initialize the I/O Package and any Queues
//
status = EchoQueueInitialize(device);
}
}
return status;
}
Операция PnP
Как и любая другая физическая камера, рекомендуется, чтобы драйвер заглушки управлял по крайней мере операциями PnP по включению и отключению устройства при удалении или подключении базового источника. Например, если Пользовательский источник мультимедиа использует сетевой источник (например, IP-камеру), может потребоваться активировать удаление устройства, когда этот сетевой источник больше недоступен.
Это гарантирует, что приложения будут ожидать добавления или удаления устройств через API PnP получать соответствующие уведомления. И гарантирует, что источник, который больше недоступен, не может быть перечислен.
Сведения о драйверах UMDF и KMDF см. в документации по функции WdfDeviceSetDeviceState .
Драйверы WMD см. в документации по функции IoSetDeviceInterfaceState .
Библиотека DLL пользовательского источника мультимедиа
Пользовательский источник мультимедиа — это стандартный com-сервер inproc, который должен реализовывать следующие интерфейсы:
Примечание
IMFMediaSourceEx наследует от IMFMediaSource , а IMFMediaSource наследует от IMFMediaEventGenerator.
Каждый поддерживаемый поток в пользовательском источнике мультимедиа должен поддерживать следующие интерфейсы:
IMFMediaEventGenerator
IMFMediaStream2
Примечание
IMFMediaStream2 наследует от IMFMediaStream , а IMFMediaStream — от IMFMediaEventGenerator.
Сведения о создании пользовательского источника мультимедиа см. в документации по написанию пользовательского источника мультимедиа. В оставшейся части этого раздела описаны различия, необходимые для поддержки пользовательского источника мультимедиа на платформе Frame Server.
IMFGetService
IMFGetService — это обязательный интерфейс для пользовательского источника мультимедиа сервера кадров. IMFGetService может возвращать MF_E_UNSUPPORTED_SERVICE , если вашему пользовательскому источнику мультимедиа не требуется предоставлять дополнительные интерфейсы служб.
В следующем примере показана реализация IMFGetService без интерфейсов службы поддержки:
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::GetService(
_In_ REFGUID guidService,
_In_ REFIID riid,
_Out_ LPVOID * ppvObject
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
if (!ppvObject)
{
return E_POINTER;
}
*ppvObject = NULL;
// We have no supported service, just return
// MF_E_UNSUPPORTED_SERVICE for all calls.
return MF_E_UNSUPPORTED_SERVICE;
}
IMFMediaEventGenerator
Как показано выше, как источник, так и отдельные потоки в источнике должны поддерживать собственный интерфейс IMFMediaEventGenerator . Все потоки данных и управления конвейера MF из источника управляются с помощью генератора событий путем отправки конкретного imfMediaEvent.
Для реализации IMFMediaEventGenerator источник пользовательского носителя должен использовать API MFCreateEventQueue для создания IMFMediaEventQueue и маршрутизации всех методов IMFMediaEventGenerator в объект очереди:
IMFMediaEventGenerator имеет следующие методы:
// IMFMediaEventGenerator
IFACEMETHOD(BeginGetEvent)(_In_ IMFAsyncCallback *pCallback, _In_ IUnknown *punkState);
IFACEMETHOD(EndGetEvent)(_In_ IMFAsyncResult *pResult, _COM_Outptr_ IMFMediaEvent **ppEvent);
IFACEMETHOD(GetEvent)(DWORD dwFlags, _Out_ IMFMediaEvent **ppEvent);
IFACEMETHOD(QueueEvent)(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, _In_opt_ const PROPVARIANT *pvValue);
В следующем коде показана рекомендуемая реализация интерфейса IMFMediaEventGenerator . Реализация пользовательского источника мультимедиа предоставляет интерфейс IMFMediaEventGenerator , а методы для этого интерфейса будут направлять запросы в объект IMFMediaEventQueue , созданный во время создания или инициализации источника мультимедиа.
В приведенном ниже коде _spEventQueue объект — это IMFMediaEventQueue , созданный с помощью функции MFCreateEventQueue :
// IMFMediaEventGenerator methods
IFACEMETHODIMP
SimpleMediaSource::BeginGetEvent(
_In_ IMFAsyncCallback *pCallback,
_In_ IUnknown *punkState
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
RETURN_IF_FAILED (_spEventQueue->BeginGetEvent(pCallback, punkState));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::EndGetEvent(
_In_ IMFAsyncResult *pResult,
_COM_Outptr_ IMFMediaEvent **ppEvent
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
RETURN_IF_FAILED (_spEventQueue->EndGetEvent(pResult, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::GetEvent(
DWORD dwFlags,
_COM_Outptr_ IMFMediaEvent **ppEvent
)
{
// NOTE:
// GetEvent can block indefinitely, so we do not hold the lock.
// This requires some juggling with the event queue pointer.
HRESULT hr = S_OK;
ComPtr<IMFMediaEventQueue> spQueue;
{
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
spQueue = _spEventQueue;
}
// Now get the event.
RETURN_IF_FAILED (spQueue->GetEvent(dwFlags, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::QueueEvent(
MediaEventType eventType,
REFGUID guidExtendedType,
HRESULT hrStatus,
_In_opt_ PROPVARIANT const *pvValue
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
RETURN_IF_FAILED (_spEventQueue->QueueEventParamVar(eventType, guidExtendedType, hrStatus, pvValue));
return hr;
}
Поиск и приостановка
Пользовательские источники мультимедиа, поддерживаемые платформой Frame Server, не поддерживают операции поиска или приостановки. Пользовательский источник мультимедиа не должен обеспечивать поддержку этих операций и не должен публиковать события MFSourceSeeked или MEStreamSeeked .
IMFMediaSource::P спользование должно вернуть MF_E_INVALID_STATE_TRANSITION (или MF_E_SHUTDOWN , если источник уже отключен).
IKsControl
IKsControl — это стандартный интерфейс управления для всех элементов управления, связанных с камерой. Если в пользовательском источнике мультимедиа реализованы какие-либо элементы управления камерой, интерфейс IKsControl — это то, как конвейер будет направлять управляющий ввод-вывод.
Дополнительные сведения см. в следующих разделах документации по набору элементов управления:
Элементы управления являются необязательными. Если они не поддерживаются, рекомендуемый код ошибки для возврата — HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND).
Ниже приведен пример реализации IKsControl без поддерживаемых элементов управления:
// IKsControl methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsProperty(
_In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
_In_ ULONG ulPropertyLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned
)
{
// ERROR_SET_NOT_FOUND is the standard error code returned
// by the AV Stream driver framework when a miniport
// driver does not register a handler for a KS operation.
// We want to mimic the driver behavior here if we do not
// support controls.
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP SimpleMediaSource::KsMethod(
_In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod,
_In_ ULONG ulMethodLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pMethodData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned
)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP SimpleMediaSource::KsEvent(
_In_reads_bytes_opt_(ulEventLength) PKSEVENT pEvent,
_In_ ULONG ulEventLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pEventData,
_In_ ULONG ulDataLength,
_Out_opt_ ULONG* pBytesReturned
)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
IMFMediaStream2
Как описано в статье Написание пользовательского источника мультимедиа, интерфейс IMFMediaStream2 предоставляется для работы кадра из пользовательского источника мультимедиа с помощью события мультимедиа MENewStream , размещенного в очереди событий источника во время завершения метода IMFMediaSource::Start :
IFACEMETHODIMP
SimpleMediaSource::Start(
_In_ IMFPresentationDescriptor *pPresentationDescriptor,
_In_opt_ const GUID *pguidTimeFormat,
_In_ const PROPVARIANT *pvarStartPos
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
DWORD count = 0;
PROPVARIANT startTime;
BOOL selected = false;
ComPtr<IMFStreamDescriptor> streamDesc;
DWORD streamIndex = 0;
if (pPresentationDescriptor == nullptr || pvarStartPos == nullptr)
{
return E_INVALIDARG;
}
else if (pguidTimeFormat != nullptr && *pguidTimeFormat != GUID_NULL)
{
return MF_E_UNSUPPORTED_TIME_FORMAT;
}
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
if (_sourceState != SourceState::Stopped)
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Started;
// This checks the passed in PresentationDescriptor matches the member of streams we
// have defined internally and that at least one stream is selected
RETURN_IF_FAILED (_ValidatePresentationDescriptor(pPresentationDescriptor));
RETURN_IF_FAILED (pPresentationDescriptor->GetStreamDescriptorCount(&count));
RETURN_IF_FAILED (InitPropVariantFromInt64(MFGetSystemTime(), &startTime));
// Send event that the source started. Include error code in case it failed.
RETURN_IF_FAILED (_spEventQueue->QueueEventParamVar(MESourceStarted,
GUID_NULL,
hr,
&startTime));
// We are hardcoding this to the first descriptor
// since this sample is a single stream sample. For
// multiple streams, we need to walk the list of streams
// and for each selected stream, send the MEUpdatedStream
// or MENewStream event along with the MEStreamStarted
// event.
RETURN_IF_FAILED (pPresentationDescriptor->GetStreamDescriptorByIndex(0,
&selected,
&streamDesc));
RETURN_IF_FAILED (streamDesc->GetStreamIdentifier(&streamIndex));
if (streamIndex >= NUM_STREAMS)
{
return MF_E_INVALIDSTREAMNUMBER;
}
if (selected)
{
ComPtr<IUnknown> spunkStream;
MediaEventType met = (_wasStreamPreviouslySelected ? MEUpdatedStream : MENewStream);
// Update our internal PresentationDescriptor
RETURN_IF_FAILED (_spPresentationDescriptor->SelectStream(streamIndex));
RETURN_IF_FAILED (_stream.Get()->SetStreamState(MF_STREAM_STATE_RUNNING));
RETURN_IF_FAILED (_stream.As(&spunkStream));
// Send the MEUpdatedStream/MENewStream to our source event
// queue.
RETURN_IF_FAILED (_spEventQueue->QueueEventParamUnk(met,
GUID_NULL,
S_OK,
spunkStream.Get()));
// But for our stream started (MEStreamStarted), we post to our
// stream event queue.
RETURN_IF_FAILED (_stream.Get()->QueueEvent(MEStreamStarted,
GUID_NULL,
S_OK,
&startTime));
}
_wasStreamPreviouslySelected = selected;
return hr;
}
Это необходимо сделать для каждого потока, выбранного через IMFPresentationDescriptor.
Для пользовательских источников мультимедиа с видеопотоком не следует отправлять события MEEndOfStream и MEEndOfPresentation .
Атрибуты потока
Все потоки пользовательского источника мультимедиа должны иметь MF_DEVICESTREAM_STREAM_CATEGORYPINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW не поддерживается для пользовательских источников мультимедиа.
Примечание
PINNAME_IMAGE, хотя и поддерживается, не рекомендуется. Для предоставления потока с PINNAME_IMAGE требуется, чтобы Пользовательский источник мультимедиа поддерживал все элементы управления триггерами фотографий. Дополнительные сведения см. в разделе Элементы управления потоком фотографий ниже.
MF_DEVICESTREAM_STREAM_ID является обязательным атрибутом для всех потоков. Это должен быть индекс на основе 0. Таким образом, первый поток имеет идентификатор 0, второй поток — идентификатор 1 и т. д.
Ниже приведен список рекомендуемых атрибутов в потоке.
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES является атрибутом UINT32, который является битовой маской типа потока. Ему может быть присвоено любое из следующих значений (хотя эти типы являются флагом битовой маски, рекомендуется не смешивать типы источников, если это возможно):
Тип | Flag | Описание |
---|---|---|
MFFrameSourceTypes_Color | 0x0001 | Стандартный поток цветов RGB |
MFFrameSourceTypes_Infrared | 0x0002 | Поток IR |
MFFrameSourceTypes_Depth | 0x0004 | Поток глубины |
MFFrameSourceTypes_Image | 0x0008 | Поток изображений (не видеоподтип, обычно JPEG) |
MFFrameSourceTypes_Custom | 0x0080 | Пользовательский тип потока |
MF_DEVICESTREAM_FRAMESERVER_SHARED
MF_DEVICESTREAM_FRAMESERVER_SHARED — это атрибут UINT32, для которого можно задать значение 0 или 1. Если задано значение 1, он помечает поток как "доступный для совместного использования" сервером frame. Это позволит приложениям открывать поток в общем режиме даже при использовании другим приложением.
Если этот атрибут не задан, Frame Server разрешит общий доступ к первому потоку без пометки (если в пользовательском источнике мультимедиа есть только один поток, этот поток будет помечен как общий).
Если для этого атрибута задано значение 0, Frame Server будет блокировать поток из общих приложений. Если Пользовательский источник мультимедиа помечает все потоки с этим атрибутом, равным 0, ни в коем случае общее приложение не сможет инициализировать источник.
Выделение примера
Все средства массовой информации должны быть подготовлены в качестве примера МВФ. Пользовательские источники мультимедиа должны использовать функцию MFCreateSample для выделения экземпляра IMFSample и использовать метод AddBuffer для добавления буферов мультимедиа.
Для каждой выборки IMFSample должно быть задано время и длительность выборки. Все примеры меток времени должны основываться на времени QPC (QueryPerformanceCounter).
Рекомендуется, чтобы пользовательские источники мультимедиа использовали функцию MFGetSystemTime , где это возможно. Эта функция является оболочкой для QueryPerformanceCounter и преобразует такты QPC в 100 наносекундных единиц.
Пользовательские источники мультимедиа могут использовать внутренние часы, но все метки времени должны быть сопоставлены с 100 наносекундными единицами на основе текущего QPC.
Буфер мультимедиа
Все буферы мультимедиа, добавленные в imfSample, должны использовать стандартные функции распределения буферов MF. Пользовательские источники мультимедиа не должны реализовывать собственные интерфейсы IMFMediaBuffer или пытаться выделить буфер мультимедиа напрямую (например, new/malloc/VirtualAlloc и т. д., не должны использоваться для данных кадра).
Используйте любой из следующих API для выделения кадров мультимедиа:
MFCreateMemoryBuffer и MFCreateAlignedMemoryBuffer следует использовать для данных мультимедиа без шага. Обычно это пользовательские подтипы или сжатые подтипы (например, H264/HEVC/MJPG).
Для известных несжатых типов носителей (например, YUY2, NV12 и т. д.), использующих системную память, рекомендуется использовать MFCreate2DMediaBuffer.
Для использования поверхностей DX (для операций с ускорением GPU, таких как отрисовка и(или) кодирование), следует использовать MFCreateDXGISurfaceBuffer .
MFCreateDXGISurfaceBuffer не создает поверхность DX. Поверхность создается с помощью диспетчера DXGI, переданного в источник мультимедиа с помощью метода IMFMediaSourceEx::SetD3DManager .
IMFDXGIDeviceManager::OpenDeviceHandle предоставит дескриптор, связанный с выбранным устройством D3D. Затем интерфейс ID3D11Device можно получить с помощью метода IMFDXGIDeviceManager::GetVideoService .
Независимо от того, какой тип буфера используется, созданная мвфская проба должна быть предоставлена конвейеру через событие MEMediaSample в IMFMediaEventGenerator медиапотока .
Хотя можно использовать один и тот же IMFMediaEventQueue как для пользовательского источника мультимедиа, так и для базовой коллекции IMFMediaStream, следует отметить, что это приведет к сериализации событий источника мультимедиа и потоковой передачи событий (включая поток мультимедиа). Для источников с несколькими потоками это нежелательно.
В следующем фрагменте кода показан пример реализации потока мультимедиа:
IFACEMETHODIMP
SimpleMediaStream::RequestSample(
_In_ IUnknown *pToken
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
ComPtr<IMFSample> sample;
ComPtr<IMFMediaBuffer> outputBuffer;
LONG pitch = IMAGE_ROW_SIZE_BYTES;
BYTE *bufferStart = nullptr; // not used
DWORD bufferLength = 0;
BYTE *pbuf = nullptr;
ComPtr<IMF2DBuffer2> buffer2D;
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
RETURN_IF_FAILED (MFCreateSample(&sample));
RETURN_IF_FAILED (MFCreate2DMediaBuffer(NUM_IMAGE_COLS,
NUM_IMAGE_ROWS,
D3DFMT_X8R8G8B8,
false,
&outputBuffer));
RETURN_IF_FAILED (outputBuffer.As(&buffer2D));
RETURN_IF_FAILED (buffer2D->Lock2DSize(MF2DBuffer_LockFlags_Write,
&pbuf,
&pitch,
&bufferStart,
&bufferLength));
RETURN_IF_FAILED (WriteSampleData(pbuf, pitch, bufferLength));
RETURN_IF_FAILED (buffer2D->Unlock2D());
RETURN_IF_FAILED (sample->AddBuffer(outputBuffer.Get()));
RETURN_IF_FAILED (sample->SetSampleTime(MFGetSystemTime()));
RETURN_IF_FAILED (sample->SetSampleDuration(333333));
if (pToken != nullptr)
{
RETURN_IF_FAILED (sample->SetUnknown(MFSampleExtension_Token, pToken));
}
RETURN_IF_FAILED (_spEventQueue->QueueEventParamUnk(MEMediaSample,
GUID_NULL,
S_OK,
sample.Get()));
return hr;
}
Расширение пользовательского источника мультимедиа для предоставления возможности IMFActivate (доступно в Windows 10, версия 1809)
В дополнение к приведенному выше списку интерфейсов, которые должны поддерживаться для пользовательского источника мультимедиа, одно из ограничений, накладываемых операцией пользовательского источника мультимедиа в архитектуре сервера кадров, заключается в том, что через конвейер может быть активирован только один экземпляр драйвера UMDF.
Например, если у вас есть физическое устройство, которое устанавливает драйвер-заглушку UMDF в дополнение к пакету драйверов, отличного от AV Stream, и вы подключаете к компьютеру несколько этих физических устройств, в то время как каждый экземпляр драйвера UMDF получает уникальное символьное имя ссылки, путь активации для пользовательского источника мультимедиа не будет иметь средств для передачи имени символьной ссылки, связанной с пользовательским источником мультимедиа по адресу время создания.
Пользовательский источник мультимедиа может искать стандартный атрибут MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK в хранилище атрибутов пользовательского источника мультимедиа (хранилище атрибутов, возвращаемое из пользовательского источника мультимедиа с помощью метода IMFMediaSourceEx::GetSourceAttributes ) во время вызова IMFMediaSource::Start .
Однако это может привести к более высокой задержке запуска, так как это отложит получение ресурса HW на время начала, а не время создания или инициализации.
По этой причине в Windows 10, версия 1809 пользовательские источники мультимедиа могут при необходимости предоставлять интерфейс IMFActivate.
Примечание
IMFActivate наследуется от IMFAttributes.
IMFActivate
Если COM-сервер для пользовательского источника мультимедиа поддерживает интерфейс IMFActivate , сведения об инициализации устройства будут предоставляться COM-серверу через атрибуты IMFAttributes , унаследованные IMFActivate. Поэтому при вызове IMFActivate::ActivateObject хранилище атрибутов IMFActivate будет содержать символьное имя ссылки драйвера заглушки UMDF и все дополнительные параметры конфигурации, предоставляемые конвейером или приложением во время создания или инициализации источника.
Пользовательский источник мультимедиа должен использовать этот вызов метода для получения необходимых аппаратных ресурсов.
Примечание
Если приобретение аппаратного ресурса занимает более 200 миллисекунд, рекомендуется асинхронно получить аппаратный ресурс. Активация пользовательского источника мультимедиа не должна блокироваться при получении аппаратного ресурса. Вместо этого операция IMFMediaSource::Start должна быть сериализована для получения аппаратных ресурсов.
Два дополнительных метода, предоставляемых IMFActivate, DetachObject и ShutdownObject, должны возвращать E_NOTIMPL.
Пользовательский источник мультимедиа может реализовать интерфейс IMFActivate и IMFAttributes в том же com-объекте, что и IMFMediaSource. В этом случае рекомендуется, чтобы imfMediaSourceEx::GetSourceAttributes возвращал тот же интерфейс IMFAttributes , что и интерфейс IMFActivate.
Если источник пользовательского носителя не реализует атрибуты IMFActivate и IMFAttributes с одинаковым объектом, источник пользовательского носителя должен скопировать все атрибуты, заданные в хранилище атрибутов IMFActivate , в хранилище атрибутов источника пользовательского носителя.
Закодированный поток камеры
Пользовательский источник мультимедиа может предоставлять сжатые типы мультимедиа (hevc или H264 элементарные потоки), а конвейер ОС полностью поддерживает источник и конфигурацию параметров кодирования в пользовательском источнике мультимедиа (параметры кодирования передаются через ICodecAPI, который направляется в виде вызова IKsControl::KsProperty ):
// IKsControl methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsProperty(
_In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
_In_ ULONG ulPropertyLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned
);
Структура KSPROPERTY , переданная в метод IKsControl::KsProperty , будет содержать следующие сведения:
KSPROPERTY.Set = Encoder Property GUID
KSPROPERTY.Id = 0
KSPROPERTY.Flags = (KSPROPERTY_TYPE_SET or KSPROPERTY_TYPE_GET)
Где GUID свойства кодировщика — это список доступных свойств, определенных в разделе Свойства API кодека.
Полезные данные свойства кодировщика будут передаваться через поле pPropertyData объявленного выше метода KsProperty .
Требования к обработчику записи
Хотя закодированные источники полностью поддерживаются Frame Server, на стороне клиента обработчик захвата (IMFCaptureEngine), который используется объектом Windows.Media.Capture.MediaCapture , предъявляются дополнительные требования:
Поток должен быть закодирован (HEVC или H264) или все без сжатия (в этом контексте MJPG считается несжатой).
Должен быть доступен по крайней мере один несжатый поток.
Примечание
Эти требования являются дополнением к требованиям к пользовательскому источнику мультимедиа, описанным в этом разделе. Однако требования к обработчику захвата применяются только в том случае, если клиентское приложение использует пользовательский источник мультимедиа через API IMFCaptureEngine или Windows.Media.Capture.MediaCapture .
Профили камеры (доступны в Windows 10 версии 1803 и более поздних версиях)
Поддержка профиля камеры доступна для пользовательских источников мультимедиа. Рекомендуемый механизм — опубликовать профиль с помощью атрибута MF_DEVICEMFT_SENSORPROFILE_COLLECTION вне исходного атрибута (IMFMediaSourceEx::GetSourceAttributes).
Атрибут MF_DEVICEMFT_SENSORPROFILE_COLLECTION является IUnknown интерфейса IMFSensorProfileCollection . IMFSensorProfileCollection можно получить с помощью функции MFCreateSensorProfileCollection :
IFACEMETHODIMP
SimpleMediaSource::GetSourceAttributes(
_COM_Outptr_ IMFAttributes** sourceAttributes
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == sourceAttributes)
{
return E_POINTER;
}
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
*sourceAttributes = nullptr;
if (_spAttributes.Get() == nullptr)
{
ComPtr<IMFSensorProfileCollection> profileCollection;
ComPtr<IMFSensorProfile> profile;
// Create our source attribute store
RETURN_IF_FAILED (MFCreateAttributes(_spAttributes.GetAddressOf(), 1));
// Create an empty profile collection
RETURN_IF_FAILED (MFCreateSensorProfileCollection(&profileCollection));
// In this example since we have just one stream, we only have one
// pin to add: Pin0
// Legacy profile is mandatory. This is to ensure non-profile
// aware applications can still function, but with degraded
// feature sets.
RETURN_IF_FAILED (MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr,
profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED (profile->AddProfileFilter(0, L"((RES==;FRT<=30,1;SUT==))"));
RETURN_IF_FAILED (profileCollection->AddProfile(profile.Get()));
// High Frame Rate profile will only allow >=60fps
RETURN_IF_FAILED (MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0, nullptr,
profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED (profile->AddProfileFilter(0, L"((RES==;FRT>=60,1;SUT==))"));
RETURN_IF_FAILED (profileCollection->AddProfile(profile.Get()));
// See the profile collection to the attribute store of the IMFTransform
RETURN_IF_FAILED (_spAttributes->SetUnknown(MF_DEVICEMFT_SENSORPROFILE_COLLECTION,
profileCollection.Get()));
}
return _spAttributes.CopyTo(sourceAttributes);
}
Профиль проверки подлинности лиц
Если пользовательский источник мультимедиа предназначен для поддержки Windows Hello распознавания лиц, рекомендуется опубликовать профиль проверки подлинности лиц. Ниже приведены требования к профилю проверки подлинности лиц.
Элемент управления DDI для проверки подлинности лиц должен поддерживаться в одном потоке IR. Дополнительные сведения см. в разделе KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.
Поток IR должен быть не менее 340 x 340 при 15 кадров/с. Формат должен быть L8, NV12 или MJPG, помеченный сжатием L8.
Поток RGB должен быть не менее 480 x 480 при 7,5 кадров/с (это необходимо только при принудительной проверке подлинности Multispectrum).
Профиль проверки подлинности лиц должен иметь идентификатор профиля: KSCAMERAPROFILE_FaceAuth_Mode,0.
Рекомендуется, чтобы профиль проверки подлинности лиц объявлял только один тип мультимедиа для каждого потока IR и RGB.
Элементы управления фотопотоком
Если независимые фотопотоки предоставляются путем пометки одного изMF_DEVICESTREAM_STREAM_CATEGORY потока как PINNAME_IMAGE, то требуется поток с категорией потока PINNAME_VIDEO_CAPTURE (например, один поток, предоставляющий только PINNAME_IMAGE не является допустимым источником мультимедиа).
С помощью IKsControl должен поддерживаться набор свойств PROPSETID_VIDCAP_VIDEOCONTROL . Дополнительные сведения см. в разделе Свойства элемента управления видео.