Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье содержатся сведения о реализации пользовательского источника мультимедиа в архитектуре сервера кадров.
Параметры AV Stream и пользовательского медиа-ресурса
При выборе способа поддержки потоковой передачи видеозахвата в архитектуре Frame Server существует два основных варианта: AV Stream и Собственный источник мультимедиа.
Модель AV Stream — это стандартная модель драйвера камеры с помощью драйвера мини-порта AV Stream (драйвер режима ядра). Как правило, драйверы AV Stream делятся на две основные категории: драйверы на основе MIPI и драйверы КЛАССА ВИДЕО USB.
Для параметра "Источник пользовательского носителя" модель драйвера может быть полностью пользовательской (собственной) или может основываться на нетрадиционном источнике камер (например, на основе файлов или сетевых источников).
Драйвер потоков AV
Основным преимуществом подхода av Stream Driver является то, что PnP и управление питанием или устройством уже обрабатываются платформой AV Stream.
Однако это также означает, что базовый источник должен быть физическим устройством с драйвером режима ядра для взаимодействия с оборудованием. Для устройств UVC драйвер класса Windows UVC 1.5 предоставляется по умолчанию, поэтому устройства просто должны реализовать своё встроенное ПО.
Для устройств на основе MIPI поставщик должен реализовать собственный драйвер мини-порта AV Stream.
Источник пользовательского медиа контента
Для источников, драйвер устройства которых уже доступен (но не драйвер минипорта AV Stream), или источников, использующих нестандартный захват изображения с камеры, драйвер AV Stream может оказаться непригодным. Например, IP-камера, подключенная через сеть, не будет соответствовать модели av Stream Driver.
В таких ситуациях пользовательский источник мультимедиа, использующий модель фрейм-сервера, будет альтернативой.
| Функции | Кастомный источник медиа | Драйвер потоков AV |
|---|---|---|
| PnP и управление питанием | Должен быть реализован исходным и/или заглушечным драйвером | Предоставляется платформой AV Stream |
| Плагин пользовательского режима | Недоступно. Пользовательский источник мультимедиа включает логику конкретного пользовательского режима OEM/IHV. | DMFT, Platform DMFT и MFT0 для устаревшей реализации |
| Группа датчиков | Поддерживается | Поддерживается |
| Профиль камеры версии 2 | Поддерживается | Поддерживается |
| Профиль камеры версии 1 | Не поддерживается | Поддерживается |
Требования к источнику пользовательских мультимедиа
С помощью службы фреймов камеры Windows (называемого сервером кадров) это можно сделать с помощью пользовательского источника мультимедиа. Для этого требуются два основных компонента:
Пакет драйвера с псевдо-драйвером, предназначенным для регистрации и включения интерфейса устройства камеры.
DLL-компонент COM, в котором размещается источник пользовательских мультимедиа.
Первое требование необходимо для двух целей:
Процесс проверки, обеспечивающий установку пользовательского источника мультимедиа через доверенный процесс (пакет драйвера требует сертификации WHQL).
Поддержка стандартного перечисления PnP и обнаружения "камеры".
Безопасность
Пользовательский источник мультимедиа для Frame Server отличается от общего пользовательского источника мультимедиа с точки зрения безопасности следующим образом:
Пользовательский источник мультимедиа Frame Server запускается как Local Service (не следует путать с Local System; Local Service — это низкопривилегированная учетная запись на компьютерах Windows).
Пользовательский источник медиа Frame Server работает в сеансе 0 (сеанс системной службы) и не может взаимодействовать с рабочим столом пользователя.
Учитывая эти ограничения, пользовательские источники мультимедиа frame Server не должны пытаться получить доступ к защищенным частям файловой системы или реестру. Как правило, доступ на чтение разрешен, но доступ на запись отсутствует.
Производительность
В рамках модели сервера фреймов есть два сценария того, как кастомные источники мультимедиа будут инициироваться сервером фреймов.
Во время создания или публикации группы датчиков.
Во время активации «камеры»
Создание группы датчиков обычно выполняется во время установки устройства и (или) цикла питания. Учитывая это, мы настоятельно рекомендуем пользовательским источникам мультимедиа избегать любой существенной обработки во время их создания и откладывать любые подобные действия до вызова функции IMFMediaSource::Start. Поколение группы датчиков не будет пытаться запустить пользовательский источник мультимедиа, а просто запрашивает различные доступные потоки, типы мультимедиа и сведения об атрибутах источника или потока.
Драйвер заглушки
Существуют две минимальные требования для пакета драйверов и драйвера-заглушки.
Драйвер-стаб можно разработать с использованием модели драйвера WDF (UMDF или KMDF) или WDM.
Требования к драйверу:
- Зарегистрируйте интерфейс устройства "камера" (Пользовательский источник мультимедиа) в категории KSCATEGORY_VIDEO_CAMERA , чтобы его можно было перечислить.
Замечание
Чтобы разрешить перечисление устаревшими приложениями DirectShow, драйвер должен также зарегистрироваться под KSCATEGORY_VIDEO и KSCATEGORY_CAPTURE.
- Добавьте запись реестра в узел интерфейса устройства (используйте директиву AddReg в разделе DDInstall.Interface INF драйвера), которая объявляет CoCreate CLSID вашего объекта пользовательского медиа-источника COM. Это необходимо добавить с помощью следующего имени значения реестра: CustomCaptureSourceClsid.
Это позволяет источнику "камера" обнаруживаться приложениями и, при активации, уведомляет службу сервера кадров перехватить сигнал активации и перенаправить его в пользовательский источник мультимедиа, созданный с использованием CoCreated.
Пример 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 (например, инфракрасные, глубинные и т. д.), он может также зарегистрироваться в 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-сервер, который должен реализовать следующие интерфейсы:
Замечание
IMFMediaSourceEx наследует от IMFMediaSource и IMFMediaSource наследует от IMFMediaEventGenerator.
Каждый поддерживаемый поток в пользовательском источнике мультимедиа должен поддерживать следующие интерфейсы:
МВФMediaEventGenerator
IMFMediaStream2
Замечание
IMFMediaStream2 наследует от МВФMediaStream и МВФMediaStream наследует от МВФMediaEventGenerator.
Сведения о создании пользовательского источника мультимедиа см. в документации по написанию пользовательского источника мультимедиа . В остальной части этого раздела описываются различия, необходимые для поддержки пользовательского источника мультимедиа в платформе Frame Server.
МВФGetService
IMFGetService — это обязательный интерфейс для Custom Media Source сервера Frame. 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;
}
МВФMediaEventGenerator
Как показано выше, источник и отдельные потоки в источнике должны поддерживать собственный интерфейс 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::Pause должен возвращать 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_CATEGORY, установленную на PINNAME_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, представляющий собой битмаску значения типа потока. Для него может быть задано любое из следующих типов (хотя эти типы являются флагом битовой маски, рекомендуется, чтобы исходные типы не были смешанными, если это возможно):
| Тип | Флаг | Описание |
|---|---|---|
| MFFrameSourceTypes_Color | 0x0001 | Стандартный цветной поток RGB |
| MFFrameSourceTypes_Infrared | 0x0002 | Поток IR |
| MFFrameSourceTypes_Глубина | 0x0004 | Глубинный поток |
| MFFrameSourceTypes_Image | 0x0008 | Поток изображений (подтип nonvideo, обычно JPEG) |
| MFFrameSourceTypes_Custom | 0x0080 | Пользовательский тип потока |
MF_DEVICESTREAM_FRAMESERVER_SHARED
MF_DEVICESTREAM_FRAMESERVER_SHARED — это атрибут UINT32, который можно задать как 0, так и 1. Если задано значение 1, он помечает поток как доступный для совместного использования сервером кадра. Это позволяет приложениям открывать поток в общем режиме, даже если используется другим приложением.
Если этот атрибут не задан, frame Server позволяет совместно использовать первый немаркованный поток (если в пользовательском источнике мультимедиа есть только один поток, этот поток помечается как общий).
Если для этого атрибута задано значение 0, Frame Server блокирует поток из общих приложений. Если пользовательский источник мультимедиа помечает все потоки с этим атрибутом, установленным в 0, ни одно совместное приложение не может инициализировать источник.
Распределение выборки
Все кадры медиа должны быть созданы как IMFSample. Пользовательские источники мультимедиа должны использовать функцию 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 .
Независимо от того, какой тип буфера используется, созданный IMFSample должен быть предоставлен конвейеру через событие MEMediaSample на IMFMediaEventGenerator потока мультимедиа.
Хотя можно использовать один и тот же МВФMediaEventQueue как для пользовательского источника мультимедиа, так и для базовой коллекции МВФMediaStream, следует отметить, что это приведет к сериализации исходных событий мультимедиа и потоковых событий (включая поток мультимедиа). Для источников с несколькими потоками это не желательно.
В следующем фрагменте кода показана пример реализации потока мультимедиа:
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;
}
Расширение custom Media Source для предоставления МВФActivate (доступно в Windows 10 версии 1809)
В дополнение к приведенному выше списку интерфейсов, которые должны поддерживаться для пользовательского источника мультимедиа, одним из ограничений, введенных работой пользовательского источника мультимедиа в архитектуре сервера кадров, является то, что через конвейер может быть активирован только один экземпляр драйвера UMDF.
Например, если у вас есть физическое устройство, которое устанавливает заглушку UMDF в дополнение к своему пакету драйверов, не относящемуся к AV Stream, и вы подключаете несколько таких физических устройств к компьютеру, то, хотя каждый экземпляр драйвера UMDF получит уникальное имя символьной ссылки, путь активации для Пользовательского Источника Мультимедиа не будет иметь возможности передать имя символьной ссылки, связанное с Пользовательским Источником Мультимедиа во время его создания.
Пользовательский источник медиа может искать стандартный атрибут MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK в хранилище атрибутов пользовательского источника медиа (хранилище атрибутов, которое возвращается из пользовательского источника медиа через метод IMFMediaSourceEx::GetSourceAttributes) в момент вызова IMFMediaSource::Start.
Однако это может привести к более высокой задержке запуска, так как это отложит приобретение ресурсов аппаратного обеспечения на время их начала, а не на время создания и инициализации.
Из-за этого в Windows 10 версии 1809 пользовательские источники мультимедиа могут при необходимости предоставлять интерфейс IMFActivate .
Замечание
IMFActivate наследует от IMFAttributes.
МВФActivate
Если 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 Codec.
Полезные данные свойства кодировщика передаются через поле pPropertyData метода KsProperty, объявленного выше.
Требования к обработчику записи
В то время как кодированные источники полностью поддерживаются сервером кадров, клиентский модуль захвата (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 кадров в секунду (это необходимо только в случае применения мультиспектральной аутентификации).
Профиль аутентификации лица должен иметь идентификатор профиля: KSCAMERAPROFILE_FaceAuth_Mode:0.
Мы рекомендуем, чтобы профиль аутентификации лиц объявлял только один тип носителя для каждого из потоков IR и RGB.
Элементы управления потока фотографий
Если независимые фотопотоки предоставляются путем маркировки одного изMF_DEVICESTREAM_STREAM_CATEGORY потока как PINNAME_IMAGE, требуется поток с категорией потоков PINNAME_VIDEO_CAPTURE (например, один поток, предоставляющий только PINNAME_IMAGE не является допустимым источником мультимедиа).
С помощью IKsControl необходимо поддерживать набор свойств PROPSETID_VIDCAP_VIDEOCONTROL . Дополнительные сведения см. в разделе "Свойства управления видео".