Compartir a través de


Origen de medios personalizados de Frame Server

En este tema se proporciona información sobre la implementación de un origen multimedia personalizado dentro de la arquitectura de Frame Server.

Opciones de streaming de AV y origen de medios personalizados

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 minipuerto de AV Stream (controlador de 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 del controlador de flujo de AV es que el PnP y la administración de energía/Administración de dispositivos ya está controlado por el marco de av Stream.

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 proporciona un controlador de clase UVC 1.5 de Windows para que los dispositivos simplemente necesiten implementar su firmware.

En el caso de los dispositivos basados en MIPI, el proveedor tendrá que implementar su propio controlador de miniporte av Stream.

Origen multimedia personalizado

En el caso de los orígenes cuyo controlador de dispositivo ya está disponible (pero no un controlador de minipuerto de AV Stream) o orígenes que usan captura de cámara no tradicional, es posible que un controlador de secuencia av no sea viable. Por ejemplo, una cámara IP conectada a través de la red no cabría en un modelo de controlador de flujo de AV.

En tales situaciones, un origen multimedia personalizado con 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 flujo de AV
Complemento de modo de usuario No disponible. Custom Media Source incorpora la lógica del modo de usuario específico 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 cámara V1 No compatible Compatible

Requisitos de origen multimedia personalizados

Con la introducción de Cámara de Windows servicio Frame Server (denominado Servidor de fotogramas), esto se puede lograr a través de un origen multimedia personalizado. Esto requiere dos componentes principales:

  • Paquete de controladores con un controlador auxiliar diseñado para registrar o 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 investigación para asegurarse de que el origen multimedia personalizado se instala a través de un proceso de confianza (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 multimedia personalizado para Frame Server difiere del origen genérico de medios personalizados en términos de 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 privilegios muy bajos 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 el acceso de escritura no lo es.

Rendimiento

Como parte del modelo de Frame Server, hay dos casos en el modo en que frame Server crea una instancia de orígenes multimedia personalizados:

  • Durante la generación o publicación del grupo de sensores.

  • Durante la activación de "cámara"

La generación del grupo de sensores se realiza normalmente durante la instalación del dispositivo o el ciclo de alimentación. 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 de medios personalizados, simplemente consultará las distintas secuencias o tipos de medios disponibles y la información del atributo de origen o flujo.

Controlador de código auxiliar

Hay dos requisitos mínimos para el paquete de controladores y el controlador de código auxiliar.

El controlador de código auxiliar se puede escribir mediante 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 de DirectShow heredadas, el controlador también tendrá que registrarse en el KSCATEGORY_VIDEO y la KSCATEGORY_CAPTURE.

  • Agregue una entrada del Registro en el nodo de interfaz de dispositivo (use la directiva AddReg en la sección INF DDInstall.Interface del controlador) que declara el CLSID coCreate-able del 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 se activan, informa al servicio Frame Server de interceptar la llamada de activación y volver a enrutarla al origen multimedia personalizado cocreado.

Inf de ejemplo

En el ejemplo siguiente se muestra un INF típico para un controlador de código auxiliar de origen multimedia 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 anterior se registra en KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE y KSCATEGORY_VIDEO_CAMERA para asegurarse de que cualquier aplicación para UWP y que no esté en UWP busque una cámara RGB estándar.

Si el origen multimedia personalizado también expone secuencias no RGB (IR, Profundidad, etc.), también puede 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 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 en el origen multimedia personalizado si se desea la compatibilidad de aplicaciones heredadas.

Implementación del controlador de código auxiliar

Además del INF, el código auxiliar del 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 de devolución de llamada DRIVER_ADD_DEVICE para controladores basados en WDM y la función WdfDriverCreate para controladores UMDF/KMDF.

A continuación se muestra un código snip de un código auxiliar del controlador UMDF que controla 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 PnP de habilitar y deshabilitar el dispositivo cuando se quita o adjunta el origen subyacente. Por ejemplo, si el origen multimedia personalizado usa un origen de red (por ejemplo, una cámara IP), puede que desee desencadenar una eliminación de dispositivos cuando ese origen de red ya no esté disponible.

Esto garantiza que las aplicaciones escuchen la adición o eliminación de dispositivos a través de las API de PnP obtengan las notificaciones adecuadas. Además, garantiza que no se pueda enumerar un origen que ya no esté disponible.

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 que debe implementar las interfaces siguientes:

Nota

IMFMediaSourceEx hereda de IMFMediaSource y IMFMediaSource hereda de IMFMediaEventGenerator.

Cada secuencia admitida dentro del origen multimedia personalizado debe admitir las interfaces siguientes:

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 el origen multimedia personalizado en el marco de 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 de medios personalizados no necesita exponer ninguna interfaz de servicio adicional.

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 el origen como los flujos individuales dentro del origen deben admitir su propia interfaz IMFMediaEventGenerator . Todo el flujo de control y datos de canalización mf desde el origen se administra a través del generador de eventos mediante el envío de IMFMediaEvent específico.

Para implementar IMFMediaEventGenerator, el origen de medios personalizados 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 expondrá la interfaz IMFMediaEventGenerator y los métodos de esa interfaz enrutarán 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 de Marco 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::P ause debe devolver MF_E_INVALID_STATE_TRANSITION (o MF_E_SHUTDOWN si el origen ya estaba apagado).

IKsControl

IKsControl es la interfaz de control estándar para todos los controles relacionados con la cámara. Si el origen multimedia personalizado implementa cualquier control de cámara, la interfaz IKsControl es cómo la canalización enrutará la E/S del control.

Para obtener más información, vea los siguientes temas 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 trabajo de fotogramas desde el origen de medios personalizados a través de un evento multimedia MENewStream publicado en la cola de eventos de origen durante la finalizació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.

En el caso de 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 establecido en PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW no se admite para orígenes multimedia personalizados.

Nota

PINNAME_IMAGE, aunque se admite, 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, segundo flujo un identificador de 1, etc.

A continuación se muestra una lista de los atributos recomendados en la secuencia:

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES es un atributo UINT32 que es un valor de máscara de bits del tipo de secuencia. Puede establecerse en cualquiera de los siguientes tipos (aunque estos tipos son una marca de máscara de bits, se recomienda que los tipos de origen no se mezclen si es posible):

Tipo Marca 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 que no es de vídeo, normalmente JPEG)
MFFrameSourceTypes_Custom 0x0080 Tipo de flujo 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 frame. Esto permitirá que las aplicaciones abran la secuencia en un modo compartido, incluso cuando otra aplicación la use.

Si no se establece este atributo, Frame Server permitirá que se comparta la primera secuencia no marcada (si el origen de medios personalizados tiene solo una secuencia, esa secuencia se marcará como compartida).

Si este atributo se establece en 0, Frame Server bloqueará la transmisión de aplicaciones compartidas. Si el origen multimedia personalizado marca todas las secuencias con este atributo establecido en 0, ninguna aplicación compartida podrá inicializar el origen.

Asignación de ejemplo

Todos los fotogramas multimedia deben ser producidos como un IMFSample. Los orígenes de medios 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. Todas las marcas de tiempo de ejemplo 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 contenedor alrededor de QueryPerformanceCounter y convierte los tics de QPC en 100 unidades de nanosegundos.

Los orígenes de medios personalizados pueden usar un reloj interno, pero todas las marcas de tiempo se deben correlacionar con 100 unidades de nanosegundos basadas en el QPC actual.

Búfer multimedia

Todos los búferes multimedia agregados al IMFSample deben usar las funciones estándar de asignación de búfer mf. Los orígenes de medios 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 con paso. 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 memoria del sistema, se recomienda usar MFCreate2DMediaBuffer.

Para usar superficies DX (para operaciones aceleradas por GPU, como la representación o la codificación), se debe usar MFCreateDXGISurfaceBuffer .

MFCreateDXGISurfaceBuffer no crea la superficie DX. La superficie se crea mediante el Administrador dxGI pasado al origen multimedia a través del método IMFMediaSourceEx::SetD3DManager .

El IMFDXGIDeviceManager::OpenDeviceHandle proporcionará 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 FMISample 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 el origen de medios personalizados como para la colección subyacente de IMFMediaStream, debe tenerse en cuenta que, al hacerlo, se serializarán los eventos de origen multimedia y los eventos de transmisión (lo que incluye el flujo de medios). En el caso de los orígenes con varias secuencias, esto no es deseable.

El código siguiente muestra una implementación de ejemplo de la secuencia multimedia:

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 de código auxiliar de UMDF además de su paquete de controladores de stream que no es av y adjunta 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 para el origen de medios personalizados no tendrá un medio para comunicar el nombre de vínculo simbólico asociado al origen multimedia personalizado en la hora de creación.

El origen multimedia personalizado puede 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 puede 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.

Por este motivo, en Windows 10, versión 1809, los orígenes multimedia personalizados pueden exponer opcionalmente una interfaz IMFActivate.

Nota

IMFActivate hereda de IMFAttributes.

IMFActivate

Si el servidor COM para el origen de medios personalizados admite la interfaz IMFActivate , la información de inicialización del dispositivo se proporcionará al servidor COM a través de los IMFAttributes heredados por el IMFActivate. Por lo tanto, cuando se invoca el IMFActivate::ActivateObject , el almacén de atributos del IMFActivate contendrá el nombre de vínculo simbólico del controlador de código auxiliar de UMDF y cualquier configuración adicional proporcionada por la canalización o aplicación en el momento de la creación o inicialización de origen.

El origen multimedia personalizado debe usar esta invocación de método para adquirir los recursos de hardware que necesite.

Nota

Si la adquisición de recursos de hardware tarda más de 200 milisegundos, se recomienda que se adquiera de forma asincrónica el recurso de hardware. La activación del origen de medios personalizados no debe bloquearse en la adquisición de recursos de hardware. En su lugar, la operación IMFMediaSource::Start debe serializarse en la adquisición de recursos de hardware.

Los dos métodos adicionales expuestos por IMFActivate, DetachObject y ShutdownObject, deben devolver E_NOTIMPL.

El origen de medios personalizados puede 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 del origen de medios personalizados.

Secuencia de cámara codificada

Un origen multimedia personalizado puede exponer tipos de medios comprimidos (secuencias elementales HEVC o H264) y la canalización del sistema operativo es totalmente compatible con el origen y la configuración de los parámetros de codificación en el origen de medios personalizados (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 pasada al método IKsControl::KsProperty tendrá la siguiente información:

KSPROPERTY.Set = Encoder Property GUID
KSPROPERTY.Id = 0
KSPROPERTY.Flags = (KSPROPERTY_TYPE_SET or KSPROPERTY_TYPE_GET)

Donde GUID de la propiedad encoder 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 codificada (HEVC o H264) o todas 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 personalizados descritos en este tema. 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 de origen (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 de caras

Si el origen multimedia personalizado está diseñado para admitir Windows Hello reconocimiento facial, 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 face debe admitirse en una única secuencia 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 multiespectrum).

  • 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 una de las secuencias IR y RGB.

Controles de secuencia de fotos

Si las secuencias de fotos independientes se exponen marcando una de lasMF_DEVICESTREAM_STREAM_CATEGORY de la secuencia como PINNAME_IMAGE, se requiere una secuencia con la categoría de secuencia de PINNAME_VIDEO_CAPTURE (por ejemplo, una sola secuencia que expone solo el PINNAME_IMAGE no es 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 de control de vídeo.