Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se proporciona información sobre la implementación de un origen multimedia personalizado dentro de la arquitectura de Frame Server.
Opciones de transmisión audiovisual y fuente de medios personalizada
Al decidir cómo proporcionar compatibilidad con secuencias de captura de vídeo dentro de la arquitectura de Frame Server, hay dos opciones principales: AV Stream y Custom Media Source.
El modelo de AV Stream es el modelo de controlador de cámara estándar mediante un controlador de miniporte de AV Stream (controlador en modo kernel). Normalmente, los controladores de AV Stream se dividen en dos categorías principales: controladores basados en MIPI y controladores de clase de vídeo USB.
Para la opción Origen multimedia personalizado, el modelo de controlador puede ser completamente personalizado (propietario) o puede basarse en un origen de cámara no tradicional (como archivos o orígenes de red).
Controlador de flujo de AV
La principal ventaja de un enfoque de controlador de transmisión AV es que el framework de transmisión AV ya se encarga de la Administración de dispositivos y de la Administración de energía/PnP.
Sin embargo, también significa que el origen subyacente debe ser un dispositivo físico con un controlador de modo kernel para interactuar con el hardware. En el caso de los dispositivos UVC, se incluye de forma predeterminada un controlador de clase UVC 1.5 de Windows, por lo que los dispositivos solo necesitan implementar su firmware.
En el caso de los dispositivos basados en MIPI, el proveedor debe implementar su propio controlador de miniporte AV Stream.
Fuente de medios personalizada
En el caso de los orígenes cuyo controlador de dispositivo ya está disponible (pero no un controlador de miniporte de flujo de AV) o en los orígenes que usan la captura de cámara no tradicional, es posible que un controlador de flujo de AV no sea viable. Por ejemplo, una cámara IP conectada a través de la red no cabería en un modelo de controlador de transmisión de AV.
En tales situaciones, un origen multimedia personalizado mediante el modelo de Frame Server sería una alternativa.
Características | Origen multimedia personalizado | Controlador de flujo de AV |
---|---|---|
PnP y administración de energía | Debe implementarse mediante el controlador de código auxiliar o de origen. | Proporcionado por el marco de streaming de AV |
Plugin de modo de usuario | No está disponible. Custom Media Source incorpora la lógica de modo de usuario específica de OEM/IHV. | DMFT, Platform DMFT y MFT0 para la implementación heredada |
Grupo de sensores | Compatible | Compatible |
Perfil de cámara V2 | Compatible | Compatible |
Perfil de la cámara V1 | No está soportado | Compatible |
Requisitos de origen multimedia personalizados
Con la introducción del servicio Windows Camera Frame Server (denominado Frame Server), esto se puede lograr a través de un origen multimedia personalizado. Esto requiere dos componentes principales:
Un paquete de controladores con un controlador simulado diseñado para registrar y habilitar una interfaz de dispositivo de cámara.
Un archivo DLL COM que hospeda el origen multimedia personalizado.
El primer requisito es necesario para dos propósitos:
Un proceso de verificación para asegurarse de que el origen multimedia personalizado está instalado a través de un proceso confiable (el paquete de controladores requiere la certificación WHQL).
Compatibilidad con la enumeración PnP estándar y la detección de la "cámara".
Seguridad
El origen de medios personalizado para Frame Server difiere del origen genérico de medios personalizado en cuanto a seguridad de la siguiente manera:
Frame Server Custom Media Source se ejecuta como servicio local (no confundirse con el sistema local; El servicio local es una cuenta con pocos privilegios en máquinas Windows).
El origen multimedia personalizado de Frame Server se ejecuta en la sesión 0 (sesión del servicio del sistema) y no puede interactuar con el escritorio del usuario.
Dadas estas restricciones, los orígenes multimedia personalizados de Frame Server no deben intentar acceder a partes protegidas del sistema de archivos ni del registro. Por lo general, se permite el acceso de lectura, pero no el acceso de escritura.
Rendimiento
Como parte del modelo de Frame Server, hay dos casos en la forma en que el servidor frame server crea instancias de orígenes multimedia personalizados:
Durante la generación o publicación del grupo de sensores.
Durante la activación de "cámara"
Normalmente, la generación del grupo de sensores se realiza durante la instalación del dispositivo o el ciclo de energía. Dado esto, se recomienda encarecidamente que los orígenes multimedia personalizados eviten cualquier procesamiento significativo durante su creación y aplazan cualquier actividad de este tipo a la función IMFMediaSource::Start . La generación del grupo de sensores no intentará iniciar el origen multimedia personalizado, simplemente consultar las distintas secuencias o tipos de medios disponibles y la información del atributo source/stream.
Controlador stub
Hay dos requisitos mínimos para el paquete de controladores y el controlador de stub.
El controlador 'stub' se puede escribir mediante el WDF (UMDF o KMDF) o el modelo de controlador WDM.
Los requisitos del controlador son:
- Registre la interfaz del dispositivo "cámara" (origen multimedia personalizado) en la categoría KSCATEGORY_VIDEO_CAMERA para que se pueda enumerar.
Nota:
Para permitir la enumeración de las aplicaciones directShow heredadas, el controlador también debe registrarse en el KSCATEGORY_VIDEO y KSCATEGORY_CAPTURE.
- Agregue una entrada del Registro en el nodo de interfaz del dispositivo (use la directiva AddReg en la sección DDInstall.Interface del INF del controlador) que declare el CLSID que puede ser creado mediante CoCreateInstance para su objeto COM de origen multimedia personalizado. Debe agregarse con el siguiente nombre de valor del Registro: CustomCaptureSourceClsid.
Esto permite que las aplicaciones detecten el origen de la "cámara" y, cuando este se activa, informa al servicio Frame Server para que intercepte la llamada de activación y la vuelva a enrutar al Origen Multimedia Personalizado CoCreado.
INF de ejemplo
En el ejemplo siguiente se muestra un INF típico para un controlador stub de Custom Media Source personalizado:
;/*++
;
;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
El origen multimedia personalizado mencionado anteriormente se registra en KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE y KSCATEGORY_VIDEO_CAMERA para asegurar que la "cámara" esté disponible tanto para aplicaciones UWP como para aplicaciones que no utilizan UWP que buscan una cámara RGB estándar.
Si el origen multimedia personalizado también expone secuencias no RGB (IR, profundidad, etc.), podría, opcionalmente, también registrarse en el KSCATEGORY_SENSOR_CAMERA.
Nota:
La mayoría de las webcams basadas en USB exponen los formatos YUY2 y MJPG. Debido a este comportamiento, muchas aplicaciones de DirectShow heredadas se escriben con la suposición de que YUY2/MJPG está disponible. Para garantizar la compatibilidad con dicha aplicación, se recomienda que el tipo de medio YUY2 esté disponible desde su fuente de medios personalizada si se desea la compatibilidad con una aplicación heredada.
Implementación del controlador de simulación
Además del INF, el stub de controlador también debe registrar y habilitar las interfaces del dispositivo de cámara. Normalmente, esto se realiza durante la operación de DRIVER_ADD_DEVICE .
Consulte la función DRIVER_ADD_DEVICE para controladores basados en WDM y la función WdfDriverCreate para controladores UMDF/KMDF.
A continuación se muestra un fragmento de código de un stub de controlador UMDF que maneja esta operación.
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;
}
Operación PnP
Al igual que cualquier otra cámara física, se recomienda que el controlador de código auxiliar administre al menos las operaciones de Plug and Play para habilitar y deshabilitar el dispositivo cuando se quite o conecte la fuente subyacente. Por ejemplo, si el origen multimedia personalizado usa un origen de red (por ejemplo, una cámara IP), es posible que desee desencadenar una eliminación de dispositivos cuando ese origen de red ya no esté disponible.
Esto garantiza que las aplicaciones que escuchan la adición o eliminación de dispositivos a través de las API de PnP obtengan las notificaciones adecuadas. Además, garantiza que un origen que ya no esté disponible no se pueda enumerar.
Para los controladores UMDF y KMDF, consulte la documentación de la función WdfDeviceSetDeviceState .
Para los controladores WMD, consulte la documentación de la función IoSetDeviceInterfaceState.
DLL de origen multimedia personalizado
El origen multimedia personalizado es un servidor COM estándar en proceso que debe implementar las siguientes interfaces.
Nota:
IMFMediaSourceEx hereda de IMFMediaSource y IMFMediaSource hereda de IMFMediaEventGenerator.
Cada secuencia admitida en el origen multimedia personalizado debe admitir las interfaces siguientes:
IMFMediaEventGenerator
IMFMediaStream2
Nota:
IMFMediaStream2 hereda de IMFMediaStream y IMFMediaStream hereda de IMFMediaEventGenerator.
Consulte la documentación Sobre la escritura de un origen multimedia personalizado sobre cómo crear un origen multimedia personalizado. En el resto de esta sección se explican las diferencias necesarias para admitir su Fuente de Medios Personalizada dentro del marco del Sistema Frame Server.
IMFGetService
IMFGetService es una interfaz obligatoria para el origen de medios personalizados de Frame Server. IMFGetService puede devolver MF_E_UNSUPPORTED_SERVICE si el origen multimedia personalizado no necesita exponer ninguna otra interfaz de servicio.
En el ejemplo siguiente se muestra una implementación imfGetService sin interfaces de servicio de soporte técnico:
_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
Como se ha mostrado anteriormente, tanto la fuente como las secuencias individuales dentro de la fuente deben admitir su propia interfaz IMFMediaEventGenerator . Todos los flujos de control y de datos de la canalización MF desde el origen se administran a través del generador de eventos mediante el envío de IMFMediaEvent específico.
Para implementar IMFMediaEventGenerator, el origen multimedia personalizado debe usar la API MFCreateEventQueue para crear un IMFMediaEventQueue y enrutar todos los métodos para IMFMediaEventGenerator al objeto de cola:
IMFMediaEventGenerator tiene los métodos siguientes:
// 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);
El código siguiente muestra la implementación recomendada de la interfaz IMFMediaEventGenerator . La implementación del origen multimedia personalizado expone la interfaz IMFMediaEventGenerator, y los métodos de esa interfaz están enrutando las solicitudes al objeto IMFMediaEventQueue creado durante la creación o inicialización del origen multimedia.
En el código siguiente, _spEventQueue objeto es el IMFMediaEventQueue creado con la función 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;
}
Buscar y pausar
Los orígenes multimedia personalizados admitidos a través del marco frame Server no admiten las operaciones Seek o Pause. El origen multimedia personalizado no necesita proporcionar compatibilidad con estas operaciones y no debe publicar el evento MFSourceSeeked o MEStreamSeeked .
IMFMediaSource::Pause debe devolver MF_E_INVALID_STATE_TRANSITION (o MF_E_SHUTDOWN si la fuente ya se detuvo).
IKsControl
IKsControl es la interfaz de control estándar para todos los controles relacionados con la cámara. Si el origen multimedia personalizado implementa algún control de cámara, la interfaz IKsControl es el medio por el cual la canalización enruta la E/S de control.
Para obtener más información, consulte los siguientes artículos de documentación del conjunto de controles:
Los controles son opcionales y, si no se admiten, el código de error recomendado para devolver es HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND).
El código siguiente es una implementación de IKsControl de ejemplo sin controles admitidos:
// 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
Como se explica en Escritura de un origen multimedia personalizado, la interfaz IMFMediaStream2 se proporciona al marco de trabajo desde el origen multimedia personalizado a través de un evento multimedia MENewStream publicado en la cola de eventos de origen durante la ejecución del método 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;
}
Esto debe hacerse para cada secuencia seleccionada a través del IMFPresentationDescriptor.
Para orígenes multimedia personalizados con secuencia de vídeo, no se deben enviar eventos MEEndOfStream y MEEndOfPresentation .
Atributos de secuencia
Todas las secuencias de origen multimedia personalizado deben tener el MF_DEVICESTREAM_STREAM_CATEGORY configurado para ser PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW no se admite para fuentes de medios personalizadas.
Nota:
PINNAME_IMAGE, aunque es compatible, no se recomienda. La exposición de una secuencia con PINNAME_IMAGE requiere que el origen multimedia personalizado admita todos los controles de desencadenador de fotos. Consulte la sección Controles de secuencia de fotos a continuación para obtener más detalles.
MF_DEVICESTREAM_STREAM_ID es un atributo obligatorio para todas las secuencias. Debe ser un índice basado en 0. Por lo tanto, la primera secuencia tiene un identificador de 0, la segunda secuencia un identificador de 1, y así sucesivamente.
A continuación se muestra una lista de atributos recomendados en el flujo.
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES (Tipos de fuente de fotogramas de flujo de dispositivo MF)
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES es un atributo UINT32 que representa un valor enmascarado por bits del tipo de secuencia. Puede configurarse para cualquiera de los siguientes tipos (aunque estos tipos son un indicador de máscara de bits, se recomienda que los tipos de origen no se mezclen si es posible).
Tipo | Bandera | Descripción |
---|---|---|
MFFrameSourceTypes_Color | 0x0001 | Secuencia de color RGB estándar |
MFFrameSourceTypes_Infrared | 0x0002 | Flujo de IR |
MFFrameSourceTypes_Depth | 0x0004 | Flujo de profundidad |
MFFrameSourceTypes_Image | 0x0008 | Secuencia de imágenes (subtipo novideo, normalmente JPEG) |
MFFrameSourceTypes_Custom | 0x0080 | Tipo de secuencia personalizado |
MF_DEVICESTREAM_FRAMESERVER_SHARED
MF_DEVICESTREAM_FRAMESERVER_SHARED es un atributo UINT32 que se puede establecer en 0 o 1. Si se establece en 1, marca la secuencia como "compartible" por el servidor de fotogramas. Esto permite a las aplicaciones abrir la secuencia en un modo compartido, incluso cuando otra aplicación la usa.
Si no se establece este atributo, Frame Server permite compartir la primera secuencia sin marca (si el origen de medios personalizados tiene solo una secuencia, esa secuencia se marca como compartida).
Si este atributo se establece en 0, Frame Server bloquea el flujo de aplicaciones compartidas. Si el origen multimedia personalizado marca todas las secuencias con este atributo establecido en 0, ninguna aplicación compartida puede inicializar el origen.
Distribución de muestras
Todos los fotogramas multimedia deben ser producidos como un elemento IMFSample. Los orígenes multimedia personalizados deben usar la función MFCreateSample para asignar una instancia de IMFSample y usar el método AddBuffer para agregar búferes multimedia.
Cada IMFSample debe tener establecido el tiempo de muestra y la duración de la muestra. Todos los ejemplos de marcas de tiempo deben basarse en la hora QPC (QueryPerformanceCounter).
Se recomienda que los orígenes multimedia personalizados usen la función MFGetSystemTime siempre que sea posible. Esta función es un recubrimiento alrededor de QueryPerformanceCounter y convierte los ticks de QPC en unidades de 100 nanosegundos.
Los orígenes multimedia personalizados pueden usar un reloj interno, pero todas las marcas de tiempo deben estar correlacionadas con unidades de 100 nanosegundos basadas en el QPC actual.
Buffer de medios
Todos los búferes multimedia agregados al IMFSample deben usar las funciones estándar de asignación de búfer mf. Los orígenes multimedia personalizados no deben implementar sus propias interfaces IMFMediaBuffer ni intentar asignar directamente el búfer multimedia (por ejemplo, new/malloc/VirtualAlloc, etc., no deben usarse para los datos de fotogramas).
Use cualquiera de las siguientes API para asignar fotogramas multimedia:
MFCreateMemoryBuffer y MFCreateAlignedMemoryBuffer deben usarse para datos multimedia no alineados. Normalmente, estos serían subtipos personalizados o subtipos comprimidos (como H264/HEVC/MJPG).
Para los tipos de medios no comprimidos conocidos (como YUY2, NV12, etc.) mediante la memoria del sistema, se recomienda usar MFCreate2DMediaBuffer.
Para usar superficies DX (para operaciones aceleradas por GPU, como representación o codificación), se debe usar MFCreateDXGISurfaceBuffer .
MFCreateDXGISurfaceBuffer no crea la superficie DX. La superficie se crea utilizando el administrador DXGI que se pasa al origen multimedia mediante el método IMFMediaSourceEx::SetD3DManager.
IMFDXGIDeviceManager::OpenDeviceHandle proporciona el identificador asociado al dispositivo D3D seleccionado. La interfaz ID3D11Device se puede obtener utilizando el método IMFDXGIDeviceManager::GetVideoService .
Independientemente del tipo de búfer que se use, el IMFSample creado debe proporcionarse a la canalización a través del evento MEMediaSample en el IMFMediaEventGenerator de la secuencia multimedia.
Aunque es posible usar el mismo IMFMediaEventQueue tanto para la fuente multimedia personalizada como para la colección subyacente de IMFMediaStream, debe tenerse en cuenta que si lo hace, se serializarán los eventos de origen multimedia y los eventos de transmisión (que incluye el flujo multimedia). En el caso de las fuentes con varios flujos, esto no es deseable.
El siguiente fragmento de código muestra una implementación de ejemplo del flujo de medios.
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;
}
Extensión de origen multimedia personalizado para exponer IMFActivate (disponible en Windows 10, versión 1809)
Además de la lista anterior de interfaces que se deben admitir para un origen multimedia personalizado, una de las limitaciones impuestas por la operación de origen multimedia personalizado dentro de la arquitectura de Frame Server es que solo puede haber una instancia del controlador UMDF "activado" a través de la canalización.
Por ejemplo, si tiene un dispositivo físico que instala un controlador stub de UMDF además de su paquete de controladores no-AV Stream, y asocia más de uno de esos dispositivos físicos a un equipo, mientras que cada instancia del controlador UMDF obtendrá un nombre de vínculo simbólico único, la ruta de acceso de activación del origen multimedia personalizado no tendrá un medio para comunicar el nombre del vínculo simbólico asociado al origen multimedia personalizado en el momento de su creación.
Custom Media Source podría buscar el atributo estándar MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK en el almacén de atributos del origen multimedia personalizado (el almacén de atributos devuelto desde el origen multimedia personalizado a través del método IMFMediaSourceEx::GetSourceAttributes ) en el momento en que se invoca IMFMediaSource::Start .
Sin embargo, esto podría dar lugar a una mayor latencia de inicio, ya que esto aplazará la adquisición de recursos de HW a la hora de inicio en lugar de la hora de creación o inicialización.
Debido a esto, en Windows 10, versión 1809, los orígenes multimedia personalizados podrían exponer opcionalmente una interfaz IMFActivate .
Nota:
IMFActivate hereda de IMFAttributes.
IMFActivate
Si el servidor COM para el origen multimedia personalizado admite la interfaz IMFActivate , la información de inicialización del dispositivo se proporciona al servidor COM a través de los IMFAttributes heredados por el IMFActivate. Por lo tanto, cuando se invoca IMFActivate::ActivateObject, el almacén de atributos del IMFActivate contiene el nombre del enlace simbólico del controlador auxiliar UMDF y cualquier otra configuración proporcionada por la canalización o aplicación en el momento de la creación o inicialización de la fuente.
El origen multimedia personalizado debe usar esta invocación de método para adquirir los recursos de hardware que necesita.
Nota:
Si la adquisición de recursos de hardware tarda más de 200 milisegundos, se recomienda adquirir de forma asincrónica el recurso de hardware. La activación del origen multimedia personalizado no debe bloquearse durante la adquisición de recursos de hardware. En su lugar, la operación IMFMediaSource::Start debe serializarse frente a la adquisición de recursos de hardware.
Los otros dos métodos expuestos por IMFActivate, DetachObject y ShutdownObject, deben devolver E_NOTIMPL.
El origen de medios personalizados podría optar por implementar la interfaz IMFActivate y IMFAttributes dentro del mismo objeto COM que IMFMediaSource. Si esto se hace, se recomienda que IMFMediaSourceEx::GetSourceAttributes devuelva la misma interfaz IMFAttributes que las del IMFActivate.
Si el origen de medios personalizados no implementa IMFActivate y IMFAttributes con el mismo objeto, el origen de medios personalizados debe copiar todos los atributos establecidos en el almacén de atributos IMFActivate en el almacén de atributos de origen de medios personalizados.
Secuencia de cámara codificada
Un origen multimedia personalizado podría exponer tipos de medios comprimidos (secuencias elementales HEVC o H264) y la canalización del sistema operativo admite completamente el origen y la configuración de los parámetros de codificación en el origen multimedia personalizado (los parámetros de codificación se comunican a través de ICodecAPI, que se enruta como una llamada A 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
);
La estructura KSPROPERTY que se pasa al método IKsControl::KsProperty tiene la siguiente información:
KSPROPERTY.Set = Encoder Property GUID
KSPROPERTY.Id = 0
KSPROPERTY.Flags = (KSPROPERTY_TYPE_SET or KSPROPERTY_TYPE_GET)
Donde el GUID de la propiedad del codificador es la lista de propiedades disponibles definidas en Propiedades de la API de códec.
La carga de la propiedad Encoder se pasará a través del campo pPropertyData del método KsProperty declarado anteriormente.
Requisitos del motor de captura
Aunque los orígenes codificados son totalmente compatibles con Frame Server, el motor de captura del lado cliente (IMFCaptureEngine) que usa el objeto Windows.Media.Capture.MediaCapture impone requisitos adicionales:
La secuencia debe estar toda codificada (HEVC o H264) o toda sin comprimir (en este contexto, MJPG se trata como sin comprimir).
Debe haber al menos una secuencia sin comprimir disponible.
Nota:
Estos requisitos se agregan a los requisitos de origen multimedia personalizado descritos en este artículo. Sin embargo, los requisitos del motor de captura solo se aplican cuando la aplicación cliente usa el origen multimedia personalizado a través de IMFCaptureEngine o Windows.Media.Capture.MediaCapture API.
Perfiles de cámara (disponibles en Windows 10, versión 1803 y posteriores)
La compatibilidad con el perfil de cámara está disponible para orígenes multimedia personalizados. El mecanismo recomendado es publicar el perfil a través del atributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION del atributo source (IMFMediaSourceEx::GetSourceAttributes).
El atributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION es un IUnknown de la interfaz IMFSensorProfileCollection . IMFSensorProfileCollection se puede obtener mediante la función 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);
}
Perfil de autenticación facial
Si el origen multimedia personalizado está diseñado para admitir el reconocimiento facial de Windows Hello, se recomienda publicar un perfil de autenticación facial. Los requisitos de un perfil de autenticación facial son:
El control DDI de autenticación facial debe ser compatible con un único flujo de IR. Para obtener más información, consulte KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.
La secuencia de IR debe tener al menos 340 x 340 a 15 fps. El formato debe ser L8, NV12 o MJPG marcados con compresión L8.
La secuencia RGB debe tener al menos 480 x 480 a 7,5 fps (esto solo es necesario si se aplica la autenticación multispectrum).
El perfil de autenticación facial debe tener el identificador de perfil de: KSCAMERAPROFILE_FaceAuth_Mode,0.
Se recomienda que el perfil de autenticación facial solo anuncie un tipo de medio para cada uno de los flujos IR y RGB.
Controles de flujo de fotos
Si las secuencias de fotos independientes se exponen marcando una de las MF_DEVICESTREAM_STREAM_CATEGORY de la secuencia como PINNAME_IMAGE, se necesita una secuencia que tenga la categoría PINNAME_VIDEO_CAPTURE (por ejemplo, una secuencia que solo exponga el PINNAME_IMAGE no se considera un origen multimedia válido).
A través de IKsControl, se debe admitir el conjunto de propiedades PROPSETID_VIDCAP_VIDEOCONTROL . Para obtener más información, vea Propiedades del control de vídeo.