Partager via


Frame Server Custom Media Source

Cette rubrique fournit des informations sur l’implémentation d’une source multimédia personnalisée dans l’architecture frame Server.

Options de flux AV et de source multimédia personnalisée

Lorsque vous décidez comment fournir la prise en charge du flux de capture vidéo dans l’architecture frame Server, il existe deux options main : AV Stream et Custom Media Source.

Le modèle AV Stream est le modèle de pilote de caméra standard utilisant un pilote de miniport AV Stream (pilote en mode noyau). En règle générale, les pilotes AV Stream appartiennent à deux catégories main : les pilotes basés sur MIPI et les pilotes de classe vidéo USB.

Pour l’option Source multimédia personnalisée, le modèle de pilote peut être entièrement personnalisé (propriétaire) ou basé sur une source de caméra non traditionnelle (comme des sources de fichier ou réseau).

Pilote de flux AV

L’avantage main d’une approche de pilote de flux AV est que le PnP et la gestion de l’alimentation/Gestion des appareils sont déjà gérés par l’infrastructure AV Stream.

Toutefois, cela signifie également que la source sous-jacente doit être un périphérique physique avec un pilote en mode noyau pour interfacer avec le matériel. Pour les appareils UVC, un pilote de classe Windows UVC 1.5 est fourni dans la boîte de réception. Il suffit donc aux appareils d’implémenter leur microprogramme.

Pour les appareils MIPI, le fournisseur doit implémenter son propre pilote de miniport AV Stream.

Source multimédia personnalisée

Pour les sources dont le pilote de périphérique est déjà disponible (mais pas un pilote de miniport AV Stream) ou les sources qui utilisent une capture d’appareil photo non traditionnelle, un pilote AV Stream peut ne pas être viable. Par exemple, une caméra IP connectée sur le réseau ne s’intégrerait pas dans un modèle de pilote de flux AV.

Dans de telles situations, une source multimédia personnalisée utilisant le modèle Frame Server est une alternative.

Fonctionnalités Source multimédia personnalisée Pilote de flux AV
PnP et gestion de l’alimentation Doit être implémenté par le pilote source et/ou stub Fourni par l’infrastructure AV Stream
Plug-in de mode utilisateur Non disponible. La source multimédia personnalisée intègre la logique de mode utilisateur spécifique OEM/IHV. DMFT, DMFT de plateforme et MFT0 pour l’implémentation héritée
Groupe de capteurs Prise en charge Prise en charge
Profil d’appareil photo V2 Prise en charge Prise en charge
Profil d’appareil photo V1 Non prise en charge Prise en charge

Configuration requise pour la source multimédia personnalisée

Avec l’introduction de Caméra Windows service Frame Server (appelé serveur frame), cela peut être effectué via une source multimédia personnalisée. Cela nécessite deux composants main :

  • Un package de pilotes avec un pilote stoublé conçu pour inscrire/activer une interface de périphérique de caméra.

  • DLL COM qui héberge la source multimédia personnalisée.

La première exigence est nécessaire à deux fins :

  • Un processus de vérification pour s’assurer que la source multimédia personnalisée est installée par le biais d’un processus approuvé (le package de pilotes nécessite la certification WHQL).

  • Prise en charge de l’énumération PnP standard et de la découverte de la « caméra ».

Sécurité

La source de média personnalisée pour Frame Server diffère de la source de média personnalisé générique en termes de sécurité de la manière suivante :

  • Frame Server Custom Media Source s’exécute en tant que service local (à ne pas confondre avec système local ; Le service local est un compte à privilèges très faible sur les machines Windows).

  • Frame Server Custom Media Source s’exécute dans la session 0 (session service système) et ne peut pas interagir avec le bureau de l’utilisateur.

Compte tenu de ces contraintes, les sources multimédias personnalisées du serveur Frame ne doivent pas tenter d’accéder aux parties protégées du système de fichiers ni au Registre. En règle générale, l’accès en lecture est autorisé, mais pas l’accès en écriture.

Performances

Dans le cadre du modèle Frame Server, il existe deux cas dans la façon dont les sources multimédias personnalisées seront instanciées par le serveur frame :

  • Pendant la génération/publication du groupe de capteurs.

  • Pendant l’activation de la « caméra »

La génération du groupe de capteurs s’effectue généralement pendant l’installation de l’appareil et/ou le cycle d’alimentation. C’est pourquoi nous vous recommandons vivement d’éviter tout traitement important lors de sa création et de reporter toute activité de ce type à la fonction IMFMediaSource::Start . La génération du groupe de capteurs ne tente pas de démarrer la source multimédia personnalisée, il suffit d’interroger les différents flux/types de médias disponibles et les informations d’attribut source/flux.

Stub Driver

Il existe deux exigences minimales pour le package de pilotes et le pilote stub.

Le pilote stub peut être écrit à l’aide du modèle WDF (UMDF ou KMDF) ou du modèle de pilote WDM.

Les conditions requises pour les pilotes sont les suivantes :

  • Inscrivez votre interface d’appareil « caméra » (la source multimédia personnalisée) sous la catégorie KSCATEGORY_VIDEO_CAMERA afin qu’elle puisse être énumérée.

Notes

Pour autoriser l’énumération par les applications DirectShow héritées, votre pilote doit également s’inscrire sous les KSCATEGORY_VIDEO et KSCATEGORY_CAPTURE.

  • Ajoutez une entrée de Registre sous le nœud d’interface de l’appareil (utilisez la directive AddReg dans la section DDInstall.Interface de votre pilote INF) qui déclare le CLSID compatible cocréé de votre objet COM Custom Media Source. Cela doit être ajouté à l’aide du nom de valeur de Registre suivant : CustomCaptureSourceClsid.

Cela permet à la source « caméra » d’être découverte par les applications et, lorsqu’elle est activée, indique au service Frame Server d’intercepter l’appel d’activation et de le réacheminer vers la source multimédia personnalisée cocréée.

Exemple d’INF

L’exemple suivant montre un inf classique pour un pilote de stub de source multimédia personnalisée :

;/*++
;
;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

La source multimédia personnalisée ci-dessus s’inscrit sous KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE et KSCATEGORY_VIDEO_CAMERA pour s’assurer que la « caméra » est détectable par toutes les applications UWP et non UWP qui recherchent une caméra RVB standard.

Si la source multimédia personnalisée expose également des flux non RVB (IR, Profondeur, etc.), elle peut éventuellement s’inscrire sous le KSCATEGORY_SENSOR_CAMERA.

Notes

La plupart des webcams usb exposent les formats YUY2 et MJPG. En raison de ce comportement, de nombreuses applications DirectShow héritées sont écrites en supposant que YUY2/MJPG est disponible. Pour garantir la compatibilité avec cette application, il est recommandé de rendre le type de média YUY2 disponible à partir de votre source multimédia personnalisée si la compatibilité d’application héritée est souhaitée.

Implémentation du pilote Stub

En plus de l’INF, le stub du pilote doit également inscrire et activer les interfaces du périphérique de caméra. Cela se fait généralement pendant l’opération de DRIVER_ADD_DEVICE .

Consultez la fonction de rappel DRIVER_ADD_DEVICE pour les pilotes WDM et la fonction WdfDriverCreate pour les pilotes UMDF/KMDF.

Voici une capture de code d’un stub de pilote UMDF qui gère cette opération :

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;
}

Opération PnP

Comme toute autre caméra physique, il est recommandé que votre pilote stub gère au moins les opérations PnP d’activation et de désactivation de l’appareil lorsque la source sous-jacente est supprimée/attachée. Par exemple, si votre source multimédia personnalisée utilise une source réseau (telle qu’une caméra IP), vous pouvez déclencher une suppression d’appareil lorsque cette source réseau n’est plus disponible.

Cela garantit que les applications écoutent l’ajout/la suppression d’appareils via les API PnP reçoivent les notifications appropriées. Et garantit qu’une source qui n’est plus disponible ne peut pas être énumérée.

Pour les pilotes UMDF et KMDF, consultez la documentation de la fonction WdfDeviceSetDeviceState .

Pour les pilotes WMD, consultez la documentation de la fonction IoSetDeviceInterfaceState .

DLL source multimédia personnalisée

La source multimédia personnalisée est un serveur COM inproc standard qui doit implémenter les interfaces suivantes :

Notes

IMFMediaSourceEx hérite de IMFMediaSource et IMFMediaSource hérite de IMFMediaEventGenerator.

Chaque flux pris en charge dans la source multimédia personnalisée doit prendre en charge les interfaces suivantes :

Notes

IMFMediaStream2 hérite de IMFMediaStream et IMFMediaStream hérite de IMFMediaEventGenerator.

Reportez-vous à la documentation Écriture d’une source multimédia personnalisée pour savoir comment créer une source multimédia personnalisée. Le reste de cette section explique les différences nécessaires pour prendre en charge votre source multimédia personnalisée dans l’infrastructure Frame Server.

IMFGetService

IMFGetService est une interface obligatoire pour Frame Server Custom Media Source. IMFGetService peut retourner MF_E_UNSUPPORTED_SERVICE si votre source multimédia personnalisée n’a pas besoin d’exposer d’interfaces de service supplémentaires.

L’exemple suivant montre une implémentation d’IMFGetService sans interface de service de support :

_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

Comme indiqué ci-dessus, la source et les flux individuels au sein de la source doivent prendre en charge leur propre interface IMFMediaEventGenerator . L’ensemble des flux de données et de contrôle du pipeline MF à partir de la source sont gérés via le générateur d’événements en envoyant un événement IMFMediaEvent spécifique.

Pour implémenter IMFMediaEventGenerator, la source de média personnalisé doit utiliser l’API MFCreateEventQueue pour créer un IMFMediaEventQueue et acheminer toutes les méthodes pour IMFMediaEventGenerator vers l’objet file d’attente :

IMFMediaEventGenerator utilise les méthodes suivantes :

// 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);

Le code suivant montre l’implémentation recommandée de l’interface IMFMediaEventGenerator . L’implémentation de la source multimédia personnalisée expose l’interface IMFMediaEventGenerator , et les méthodes de cette interface acheminent les requêtes vers l’objet IMFMediaEventQueue créé lors de la création/initialisation de la source multimédia.

Dans le code ci-dessous, _spEventQueue objet est la IMFMediaEventQueue créée à l’aide de la fonction 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;
}

Recherche et mise en pause

Les sources multimédias personnalisées prises en charge par le biais de l’infrastructure Frame Server ne prennent pas en charge les opérations de recherche ou de pause. Votre source multimédia personnalisée n’a pas besoin de prendre en charge ces opérations et ne doit pas publier l’événement MFSourceSeeked ou MEStreamSeeked .

IMFMediaSource::P ause doit retourner MF_E_INVALID_STATE_TRANSITION (ou MF_E_SHUTDOWN si la source a déjà été arrêtée).

IKsControl

IKsControl est l’interface de contrôle standard pour tous les contrôles liés à la caméra. Si votre source multimédia personnalisée implémente des contrôles de caméra, l’interface IKsControl est la façon dont le pipeline acheminera les E/S de contrôle.

Pour plus d’informations, consultez les rubriques de documentation suivantes sur l’ensemble de contrôles :

Les contrôles sont facultatifs et, s’ils ne sont pas pris en charge, le code d’erreur recommandé à retourner est HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND) .

Le code suivant est un exemple d’implémentation IKsControl sans contrôles pris en charge :

// 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

Comme expliqué dans Écriture d’une source multimédia personnalisée, l’interface IMFMediaStream2 est fournie au travail de trame à partir de votre source multimédia personnalisée via un événement multimédia MENewStream publié dans la file d’attente d’événements source pendant l’achèvement de la méthode 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;
}

Cette opération doit être effectuée pour chaque flux sélectionné via IMFPresentationDescriptor.

Pour les sources multimédias personnalisées avec flux vidéo, les événements MEEndOfStream et MEEndOfPresentation ne doivent pas être envoyés.

Attributs de flux

Tous les flux de source multimédia personnalisée doivent avoir le MF_DEVICESTREAM_STREAM_CATEGORY défini pour être PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW n’est pas pris en charge pour les sources multimédias personnalisées.

Notes

PINNAME_IMAGE, bien que pris en charge, n’est pas recommandé. L’exposition d’un flux avec PINNAME_IMAGE nécessite que la source multimédia personnalisée prend en charge tous les contrôles du déclencheur photo. Pour plus d’informations, consultez la section Contrôles de flux de photos ci-dessous.

MF_DEVICESTREAM_STREAM_ID est un attribut obligatoire pour tous les flux. Il doit s’agir d’un index de base 0. Par conséquent, le premier flux a un ID 0, le deuxième flux un ID de 1, et ainsi de suite.

Voici une liste d’attributs recommandés sur le flux :

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES est un attribut UINT32 qui est une valeur masquée de bits de type de flux. Il peut être défini sur l’un des éléments suivants (bien que ces types soient un indicateur de masque de bits, il est recommandé de ne pas mélanger les types sources si possible) :

Type Indicateur Description
MFFrameSourceTypes_Color 0x0001 Flux de couleurs RVB standard
MFFrameSourceTypes_Infrared 0x0002 Flux IR
MFFrameSourceTypes_Depth 0x0004 Flux de profondeur
MFFrameSourceTypes_Image 0x0008 Flux d’images (sous-type non vidéo, généralement JPEG)
MFFrameSourceTypes_Custom 0x0080 Type de flux personnalisé

MF_DEVICESTREAM_FRAMESERVER_SHARED

MF_DEVICESTREAM_FRAMESERVER_SHARED est un attribut UINT32 qui peut être défini sur 0 ou 1. S’il est défini sur 1, il marque le flux comme étant « partageable » par le serveur frame. Cela permet aux applications d’ouvrir le flux en mode partagé, même lorsqu’elles sont utilisées par une autre application.

Si cet attribut n’est pas défini, Frame Server autorise le partage du premier flux non marqué (si la source multimédia personnalisée n’a qu’un seul flux, ce flux sera marqué comme partagé).

Si cet attribut est défini sur 0, Frame Server bloque le flux d’applications partagées. Si la source multimédia personnalisée marque tous les flux avec cet attribut défini sur 0, aucune application partagée ne sera en mesure d’initialiser la source.

Exemple d’allocation

Toutes les trames multimédias doivent être produites sous forme d’un imfSample. Les sources multimédias personnalisées doivent utiliser la fonction MFCreateSample pour allouer un instance de IMFSample et utiliser la méthode AddBuffer pour ajouter des mémoires tampons multimédias.

Chaque échantillon IMFSample doit avoir l’exemple d’heure et de durée défini. Tous les exemples d’horodatage doivent être basés sur l’heure QPC (QueryPerformanceCounter).

Il est recommandé que les sources multimédias personnalisées utilisent la fonction MFGetSystemTime dans la mesure du possible. Cette fonction est un wrapper autour de QueryPerformanceCounter et convertit les graduations QPC en unités de 100 nanosecondes.

Les sources multimédias personnalisées peuvent utiliser une horloge interne, mais tous les horodatages doivent être corrélés à 100 nanosecondes en fonction de la QPC actuelle.

Mémoire tampon multimédia

Tous les tampons multimédias ajoutés à IMFSample doivent utiliser les fonctions d’allocation de mémoire tampon MF standard. Les sources multimédias personnalisées ne doivent pas implémenter leurs propres interfaces IMFMediaBuffer ou tenter d’allouer directement la mémoire tampon multimédia (par exemple, new/malloc/VirtualAlloc, etc., ne doivent pas être utilisées pour les données de trame).

Utilisez l’une des API suivantes pour allouer des trames multimédias :

MFCreateMemoryBuffer et MFCreateAlignedMemoryBuffer doivent être utilisés pour les données multimédias alignées non sur la foulée. Il s’agit généralement de sous-types personnalisés ou de sous-types compressés (tels que H264/HEVC/MJPG).

Pour les types de médias non compressés connus (tels que YUY2, NV12, etc.) qui utilisent la mémoire système, il est recommandé d’utiliser MFCreate2DMediaBuffer.

Pour utiliser des surfaces DX (pour les opérations accélérées par GPU telles que le rendu et/ou l’encodage), MFCreateDXGISurfaceBuffer doit être utilisé.

MFCreateDXGISurfaceBuffer ne crée pas la surface DX. La surface est créée à l’aide du gestionnaire DXGI transmis à la source multimédia via la méthode IMFMediaSourceEx::SetD3DManager .

ImfDXGIDeviceManager::OpenDeviceHandle fournit le handle associé à l’appareil D3D sélectionné. L’interface ID3D11Device peut ensuite être obtenue à l’aide de la méthode IMFDXGIDeviceManager::GetVideoService.

Quel que soit le type de mémoire tampon utilisée, le IMFSample créé doit être fourni au pipeline par le biais de l’événement MEMediaSample sur le IMFMediaEventGenerator du flux multimédia.

Bien qu’il soit possible d’utiliser le même IMFMediaEventQueue à la fois pour la source multimédia personnalisée et la collection sous-jacente d’IMFMediaStream, il convient de noter que cela entraînera la sérialisation des événements de source multimédia et des événements de flux (y compris le flux multimédia). Pour les sources avec plusieurs flux, cela n’est pas souhaitable.

La capture de code suivante montre un exemple d’implémentation du flux multimédia :

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;
}

Extension de source multimédia personnalisée pour exposer IMFActivate (disponible dans Windows 10, version 1809)

En plus de la liste ci-dessus d’interfaces qui doivent être prises en charge pour une source multimédia personnalisée, l’une des limitations imposées par l’opération Custom Media Source au sein de l’architecture frame Server est qu’il ne peut y avoir qu’une seule instance du pilote UMDF « activé » par le biais du pipeline.

Par exemple, si vous avez un périphérique physique qui installe un pilote stub UMDF en plus de son package de pilotes Stream non AV, et que vous attachez plusieurs de ces périphériques physiques à un ordinateur, tandis que chaque instance du pilote UMDF obtient un nom de lien symbolique unique, le chemin d’activation de la source multimédia personnalisée n’aura pas de moyen de communiquer le nom de lien symbolique associé à la source multimédia personnalisée au niveau de la heure de création.

La source multimédia personnalisée peut rechercher l’attribut standard MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK dans le magasin d’attributs de la source multimédia personnalisée (magasin d’attributs retourné par la source multimédia personnalisée par le biais de la méthode IMFMediaSourceEx::GetSourceAttributes ) au moment de l’appel de IMFMediaSource::Start .

Toutefois, cela peut entraîner une latence de démarrage plus élevée, car cela reportera l’acquisition des ressources HW à l’heure de début plutôt qu’au moment de la création/initialisation.

Pour cette raison, dans Windows 10, version 1809, les sources multimédias personnalisées peuvent éventuellement exposer une interface IMFActivate.

Notes

IMFActivate hérite de IMFAttributes.

FMIActiver

Si le serveur COM pour la source multimédia personnalisée prend en charge l’interface IMFActivate , les informations d’initialisation de l’appareil sont fournies au serveur COM par le biais des imfAttributes héritées par IMFActivate. Par conséquent, lorsque l’objet IMFActivate::ActivateObject est appelé, le magasin d’attributs de l’objet IMFActivate contient le nom de lien symbolique du pilote de stub UMDF et tous les paramètres de configuration supplémentaires fournis par le pipeline/l’application au moment de la création/initialisation de la source.

La source multimédia personnalisée doit utiliser cet appel de méthode pour acquérir les ressources matérielles dont elle a besoin.

Notes

Si l’acquisition de ressources matérielles prend plus de 200 millisecondes, il est recommandé que la ressource matérielle soit acquise de manière asynchrone. L’activation de la source multimédia personnalisée ne doit pas bloquer l’acquisition de ressources matérielles. Au lieu de cela, l’opération IMFMediaSource::Start doit être sérialisée sur l’acquisition de ressources matérielles.

Les deux méthodes supplémentaires exposées par IMFActivate, DetachObject et ShutdownObject, doivent retourner E_NOTIMPL.

La source de média personnalisé peut choisir d’implémenter l’interface IMFActivate et IMFAttributes dans le même objet COM que IMFMediaSource. Si cela est effectué, il est recommandé que IMFMediaSourceEx::GetSourceAttributes retourne la même interface IMFAttributes que celles de IMFActivate.

Si la source multimédia personnalisée n’implémente pas les valeurs IMFActivate et IMFAttributes avec le même objet, la source multimédia personnalisée doit copier tous les attributs définis dans le magasin d’attributs IMFActivate dans le magasin d’attributs source de la source multimédia personnalisée.

Flux de caméra encodé

Une source multimédia personnalisée peut exposer des types de médias compressés (flux élémentaires HEVC ou H264) et le pipeline du système d’exploitation prend entièrement en charge la source et la configuration des paramètres d’encodage sur la source multimédia personnalisée (les paramètres d’encodage sont communiqués via ICodecAPI, qui est routé en tant qu’appel 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 structure KSPROPERTY passée dans la méthode IKsControl::KsProperty contient les informations suivantes :

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

Où GUID de propriété d’encodeur correspond à la liste des propriétés disponibles définies dans Propriétés de l’API codec.

La charge utile de la propriété encoder est transmise via le champ pPropertyData de la méthode KsProperty déclarée ci-dessus.

Configuration requise pour le moteur de capture

Bien que les sources encodées soient entièrement prises en charge par Frame Server, le moteur de capture côté client (IMFCaptureEngine) qui est utilisé par l’objet Windows.Media.Capture.MediaCapture impose des exigences supplémentaires :

  • Le flux doit être tous encodés (HEVC ou H264) ou tous non compressés (dans ce contexte, MJPG est traité comme non compressé).

  • Au moins un flux non compressé doit être disponible.

Notes

Ces exigences s’ajoutent aux exigences de la source multimédia personnalisée décrites dans cette rubrique. Toutefois, les exigences du moteur de capture ne sont appliquées que lorsque l’application cliente utilise la source multimédia personnalisée via l’API IMFCaptureEngine ou Windows.Media.Capture.MediaCapture .

Profils d’appareil photo (disponibles dans Windows 10, version 1803 et ultérieure)

La prise en charge des profils d’appareil photo est disponible pour les sources multimédias personnalisées. Le mécanisme recommandé consiste à publier le profil via l’attribut MF_DEVICEMFT_SENSORPROFILE_COLLECTION hors de l’attribut source (IMFMediaSourceEx::GetSourceAttributes).

L’attribut MF_DEVICEMFT_SENSORPROFILE_COLLECTION est un IUnknown de l’interface IMFSensorProfileCollection. IMFSensorProfileCollection peut être obtenu à l’aide de la fonction 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);
}

Profil d’authentification faciale

Si la source multimédia personnalisée est conçue pour prendre en charge Windows Hello reconnaissance faciale, il est recommandé de publier un profil d’authentification faciale. Les exigences d’un profil d’authentification faciale sont les suivantes :

  • Le contrôle DDI d’authentification faciale doit être pris en charge sur un seul flux IR. Pour plus d’informations, consultez KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.

  • Le flux IR doit être d’au moins 340 x 340 à 15 fps. Le format doit être L8, NV12 ou MJPG marqué avec la compression L8.

  • Le flux RVB doit être d’au moins 480 x 480 à 7,5 fps (cela n’est nécessaire que si l’authentification Multispectrum est appliquée).

  • Le profil d’authentification faciale doit avoir l’ID de profil : KSCAMERAPROFILE_FaceAuth_Mode,0.

Nous avons recommandé que le profil d’authentification faciale ne publie qu’un seul type de média pour chacun des flux IR et RVB.

Contrôles de flux photo

Si des flux photo indépendants sont exposés en marquant l’un desMF_DEVICESTREAM_STREAM_CATEGORY du flux comme PINNAME_IMAGE, un flux avec une catégorie de flux de PINNAME_VIDEO_CAPTURE est requis (par exemple, un seul flux exposant uniquement le PINNAME_IMAGE n’est pas une source multimédia valide).

Via IKsControl, le jeu de propriétés PROPSETID_VIDCAP_VIDEOCONTROL doit être pris en charge. Pour plus d’informations, consultez Propriétés du contrôle vidéo.