Condividi tramite


Origine multimediale personalizzata del frame server

Questo articolo fornisce informazioni sull'implementazione di un'origine multimediale personalizzata all'interno dell'architettura del server frame.

Opzioni di flusso AV e origine multimediale personalizzata

Quando si decide come fornire il supporto per il flusso di acquisizione video all'interno dell'architettura del Frame Server, ci sono due opzioni principali: AV Stream e Origine multimediale personalizzata.

Il modello AV Stream è il modello standard di driver per fotocamere che utilizza un driver miniport AV Stream (driver in modalità kernel). In genere i driver av Stream rientrano in due categorie principali: driver basati su MIPI e driver di classe video USB.

Per l'opzione Origine multimediale personalizzata, il modello di driver potrebbe essere completamente personalizzato (proprietario) o potrebbe essere basato su un'origine della fotocamera non tradizionale ,ad esempio file o origini di rete.

Driver di streaming Audio-Video

Il principale vantaggio di un approccio con driver AV Stream è che PnP e la gestione dell'alimentazione/dispositivi sono già gestiti dal framework AV Stream.

Tuttavia, significa anche che l'origine sottostante deve essere un dispositivo fisico con un driver in modalità kernel per interfacciarsi con l'hardware. Per i dispositivi UVC, viene fornito preinstallato un driver di classe UVC 1.5 di Windows, in modo che i dispositivi debbano semplicemente implementare il loro firmware.

Per i dispositivi basati su MIPI, il fornitore deve implementare il proprio driver miniport AV Stream.

Origine multimediale personalizzata

Per le sorgenti il cui driver del dispositivo è già disponibile (ma non un driver miniport AV Stream) o le sorgenti che usano l'acquisizione di fotocamere non tradizionali, un driver di flusso AV potrebbe non essere fattibile. Ad esempio, una fotocamera IP connessa in rete non si adatta a un modello di driver di flusso AV.

In tali situazioni, un'origine multimediale personalizzata che usa il modello Frame Server è un'alternativa.

Funzionalità Origine multimediale personalizzata Driver di flusso AV
PnP e Risparmio energia Deve essere implementato dal driver di origine e/o dal driver stub Fornito dal framework AV Stream
Plugin in modalità utente Non disponibile. Il Custom Media Source incorpora la logica specifica della modalità utente OEM/IHV. DMFT, Platform DMFT e MFT0 per l'implementazione legacy
Gruppo di sensori Sostenuto Sostenuto
Profilo fotografico V2 Sostenuto Sostenuto
Profilo della fotocamera V1 Non supportato Sostenuto

Requisiti dell'origine multimediale personalizzata

Con l'introduzione del servizio Windows Camera Frame Server (detto server frame), questa operazione può essere eseguita tramite un'origine multimediale personalizzata. Ciò richiede due componenti principali:

  • Pacchetto di driver con un driver stub progettato per registrare o abilitare un'interfaccia del dispositivo della fotocamera.

  • Una DLL COM che ospita l'origine multimediale personalizzata.

Il primo requisito è necessario per due scopi:

  • Processo di verifica dell'installazione dell'origine multimediale personalizzata tramite un processo attendibile (il pacchetto driver richiede la certificazione WHQL).

  • Supporto per l'enumerazione PnP standard e l'individuazione della "fotocamera".

Sicurezza

L'origine multimediale personalizzata per il server frame differisce dall'origine multimediale personalizzata generica in termini di sicurezza nel modo seguente:

  • Il Frame Server Custom Media Source viene eseguito come Servizio Locale (da non confondere con il Sistema Locale; Servizio Locale è un account con privilegi limitati nei computer Windows).

  • Frame Server Custom Media Source viene eseguito nella sessione 0 (sessione del servizio di sistema) e non può interagire con il desktop dell'utente.

Considerati questi vincoli, le origini multimediali personalizzate del server frame non devono tentare di accedere a parti protette del file system né al Registro di sistema. In genere, l'accesso in lettura è consentito, ma l'accesso in scrittura non è consentito.

Prestazioni

Come parte del modello Frame Server, ci sono due casi in cui le origini multimediali personalizzate verranno istanziate dal Frame Server:

  • Durante la generazione e pubblicazione dei gruppi sensori.

  • Durante l'attivazione della "fotocamera"

La generazione del gruppo di sensori viene in genere eseguita durante l'installazione del dispositivo e/o il ciclo di alimentazione. Dato questo, si consiglia vivamente che origini multimediali personalizzate evitino qualsiasi elaborazione significativa durante la sua creazione e rinviino qualsiasi attività di questo tipo alla funzione IMFMediaSource::Start . La generazione del gruppo di sensori non tenterà di avviare l'origine multimediale personalizzata, semplicemente eseguire una query sui vari flussi/tipi di supporti disponibili e sulle informazioni sull'attributo di origine/flusso.

Stub Driver

Esistono due requisiti minimi per il pacchetto driver e il driver stub.

Il driver stub può essere scritto usando il modello di driver WDF (UMDF o KMDF) o WDM.

I requisiti del driver sono:

  • Registrare l'interfaccia del dispositivo "fotocamera" (origine multimediale personalizzata) nella categoria KSCATEGORY_VIDEO_CAMERA in modo che possa essere enumerata.

Annotazioni

Per consentire l'enumerazione da parte delle applicazioni DirectShow legacy, il driver deve anche registrarsi nel KSCATEGORY_VIDEO e KSCATEGORY_CAPTURE.

  • Aggiungere una voce del Registro di sistema nel nodo dell'interfaccia del dispositivo (usare la direttiva AddReg nella sezione driver INF DDInstall.Interface) che dichiara il CLSID CoCreate-able dell'oggetto COM origine multimediale personalizzato. Questa operazione deve essere aggiunta usando il nome del valore del Registro di sistema seguente: CustomCaptureSourceClsid.

In questo modo, l'origine della "camera" può essere individuata dalle applicazioni e, quando attivata, informa il servizio Frame Server di intercettare la chiamata di attivazione e reindirizzarla all'origine multimediale personalizzata CoCreated.

INF di esempio

L'esempio seguente mostra un tipico INF per un driver stub di sorgente multimediale personalizzata.

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

L'origine multimediale personalizzata precedente viene registrata in KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE e KSCATEGORY_VIDEO_CAMERA per garantire che la "fotocamera" sia individuabile da qualsiasi app UWP e non UWP alla ricerca di una fotocamera RGB standard.

Se l'origine multimediale personalizzata espone anche flussi non RGB (IR, Depth e così via) potrebbe anche registrarsi nel KSCATEGORY_SENSOR_CAMERA.

Annotazioni

La maggior parte delle webcam basate su USB espone formati YUY2 e MJPG. A causa di questo comportamento, molte applicazioni DirectShow legacy vengono scritte con il presupposto che YUY2/MJPG sia disponibile. Per garantire la compatibilità con tale applicazione, è consigliabile che il tipo di media YUY2 sia reso disponibile dalla fonte multimediale personalizzata se si desidera compatibilità con le applicazioni legacy.

Implementazione del driver Stub

Oltre all'INF, anche lo stub del driver deve registrare e abilitare le interfacce del dispositivo della fotocamera. Questa operazione viene in genere eseguita durante l'operazione di DRIVER_ADD_DEVICE .

Vedere la funzione di callback DRIVER_ADD_DEVICE per i driver basati su WDM e la funzione WdfDriverCreate per i driver UMDF/KMDF.

Di seguito è riportato un snip di codice di uno stub del driver UMDF che gestisce questa operazione:

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

Operazione PnP

Proprio come qualsiasi altra fotocamera fisica, è consigliabile che il driver stub gestisca almeno le operazioni PnP di abilitazione e disabilitazione del dispositivo quando l'origine sottostante viene rimossa/collegata. Ad esempio, se l'origine multimediale personalizzata usa un'origine di rete ,ad esempio una fotocamera IP, potrebbe essere necessario attivare una rimozione del dispositivo quando tale origine di rete non è più disponibile.

Ciò garantisce che le applicazioni, ascoltando l'aggiunta/rimozione del dispositivo tramite le API PnP, ricevano le notifiche appropriate. E garantisce che non sia più possibile enumerare un'origine non più disponibile.

Per i driver UMDF e KMDF, vedere la documentazione della funzione WdfDeviceSetDeviceState .

Per i driver WMD, vedere la documentazione della funzione IoSetDeviceInterfaceState .

DLL sorgente multimediale personalizzata

L'origine multimediale personalizzata è un server COM standard che deve implementare le interfacce seguenti:

Annotazioni

IMFMediaSourceEx eredita da IMFMediaSource e IMFMediaSource eredita da IMFMediaEventGenerator.

Ogni flusso supportato all'interno dell'origine multimediale personalizzata deve supportare le interfacce seguenti:

Annotazioni

IMFMediaStream2 eredita da IMFMediaStream e IMFMediaStream eredita da IMFMediaEventGenerator.

Per informazioni su come creare un'origine multimediale personalizzata , vedere la documentazione relativa alla scrittura di un'origine multimediale personalizzata. Nella parte restante di questa sezione vengono illustrate le differenze necessarie per supportare l'origine multimediale personalizzata all'interno del framework server frame.

IMFGetService

IMFGetService è un'interfaccia obbligatoria per Media Source personalizzata del Frame Server. IMFGetService potrebbe restituire MF_E_UNSUPPORTED_SERVICE se la tua origine multimediale personalizzata non necessita di esporre altre interfacce del servizio.

L'esempio seguente mostra un'implementazione IMFGetService senza interfacce del servizio di supporto:

_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

Come illustrato in precedenza, sia l'origine che i singoli flussi all'interno dell'origine devono supportare la propria interfaccia IMFMediaEventGenerator . L'intero flusso di dati e controllo della pipeline MF dall'origine viene gestito tramite il generatore di eventi inviando specifici IMFMediaEvent.

Per implementare IMFMediaEventGenerator, l'origine multimediale personalizzata deve usare l'API MFCreateEventQueue per creare un IMFMediaEventQueue e instradare tutti i metodi per IMFMediaEventGenerator all'oggetto queue:

IMFMediaEventGenerator ha i metodi seguenti:

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

Il codice seguente illustra l'implementazione consigliata dell'interfaccia IMFMediaEventGenerator . L'implementazione custom Media Source espone l'interfaccia IMFMediaEventGenerator e i metodi per tale interfaccia instradano le richieste nell'oggetto IMFMediaEventQueue creato durante la creazione/inizializzazione dell'origine multimediale.

Nel codice seguente , _spEventQueue oggetto è il IMFMediaEventQueue creato usando la funzione 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;
}

Ricerca e pausa

Le fonti multimediali personalizzate supportate tramite il framework del server Frame non supportano le operazioni di ricerca né di pausa. L'origine multimediale personalizzata non deve fornire supporto per queste operazioni e non deve inviare l'evento MFSourceSeeked né l'evento MEStreamSeeked.

IMFMediaSource::Pause deve restituire MF_E_INVALID_STATE_TRANSITION (o MF_E_SHUTDOWN se l'origine è già stata disattivata).

IKsControl

IKsControl è l'interfaccia di controllo standard per tutti i controlli correlati alla fotocamera. Se l'origine multimediale personalizzata implementa qualsiasi controllo della fotocamera, l'interfaccia IKsControl è il metodo con cui la pipeline instrada l'I/O del controllo.

Per altre informazioni, vedere gli articoli della documentazione sul set di controlli seguenti:

I controlli sono facoltativi e, se non supportati, il codice di errore consigliato da restituire è HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND).

Il codice seguente è un'implementazione IKsControl di esempio senza controlli supportati:

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

Come illustrato in Scrittura di un'origine multimediale personalizzata, l'interfaccia IMFMediaStream2 viene fornita al framework dalla vostra origine multimediale personalizzata tramite un evento multimediale MENewStream inviato alla coda degli eventi di origine durante il completamento del metodo 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;
}

Questa operazione deve essere eseguita per ogni flusso selezionato tramite IMFPresentationDescriptor.

Per le origini multimediali personalizzate con flusso video, gli eventi MEEndOfStream e MEEndOfPresentation non devono essere inviati.

Attributi di flusso

Tutti i flussi di origine multimediale personalizzata devono avere il MF_DEVICESTREAM_STREAM_CATEGORY impostato su PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW non è compatibile con le origini multimediali personalizzate.

Annotazioni

PINNAME_IMAGE, se supportato, non è consigliato. L'esposizione di un flusso con PINNAME_IMAGE richiede che la sorgente multimediale personalizzata supporti tutti i controlli del trigger fotografico. Per altri dettagli, vedi la sezione Controlli flusso foto di seguito.

MF_DEVICESTREAM_STREAM_ID è un attributo obbligatorio per tutti i flussi. Deve essere un indice basato su 0. Il primo flusso ha quindi un ID pari a 0, secondo flusso un ID pari a 1 e così via.

Di seguito è riportato un elenco degli attributi consigliati nel flusso:

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES è un attributo UINT32 che rappresenta un valore mascherato di bit relativo al tipo di flusso. Potrebbe essere impostato su uno dei seguenti (mentre questi tipi sono una flag di maschera di bit, è consigliabile che i tipi di sorgente non vengano combinati, se possibile):

TIPO Bandiera Descrizione
MFFrameSourceTypes_Color 0x0001 Flusso di colori RGB standard
MFFrameSourceTypes_Infrared 0x0002 Flusso IR
TipiDiSorgenteMF_Frame_Profondità 0x0004 Flusso di profondità
MFFrameSourceTypes_Immagine 0x0008 Flusso di immagini (sottotipo non video, in genere JPEG)
MFFrameSourceTypes_Custom 0x0080 Tipo di flusso personalizzato

MF_DEVICESTREAM_FRAMESERVER_SHARED

MF_DEVICESTREAM_FRAMESERVER_SHARED è un attributo UINT32 che può essere impostato su 0 o 1. Se impostato su 1, contrassegna il flusso come "condivisibile" dal server frame. In questo modo, le applicazioni possono aprire il flusso in modalità condivisa, anche se usate da un'altra app.

Se questo attributo non è impostato, Frame Server consente la condivisione del primo flusso non contrassegnato (se l'origine multimediale personalizzata ha un solo flusso, tale flusso viene contrassegnato come condiviso).

Se questo attributo è impostato su 0, Frame Server blocca il flusso dalle app condivise. Se l'origine multimediale personalizzata contrassegna tutti i flussi con questo attributo impostato su 0, nessuna applicazione condivisa è in grado di inizializzare l'origine.

Allocazione di esempio

Tutti i fotogrammi multimediali devono essere prodotti come IMFSample. Le origini multimediali personalizzate devono usare la funzione MFCreateSample per allocare un'istanza di IMFSample e utilizzare il metodo AddBuffer per aggiungere buffer multimediali.

Ogni IMFSample deve avere il tempo di campionamento e la durata del campione impostata. Tutti i timestamp di esempio devono essere basati sull'ora QPC (QueryPerformanceCounter).

È consigliabile che le origini multimediali personalizzate utilizzino, dove possibile, la funzione MFGetSystemTime. Questa funzione è un wrapper per QueryPerformanceCounter e converte i tick QPC in unità di 100 nanosecondi.

Le origini multimediali personalizzate possono usare un orologio interno, ma tutti i timestamp devono essere correlati a unità di 100 nanosecondi in base al QPC corrente.

Buffer multimediale

Tutti i buffer multimediali aggiunti a IMFSample devono usare le funzioni standard di allocazione del buffer MF. Le sorgenti multimediali personalizzate non devono implementare le proprie interfacce IMFMediaBuffer né tentare di allocare direttamente il buffer multimediale (ad esempio, new/malloc/VirtualAlloc e così via, non devono essere usate per i dati di frame).

Usare una delle API seguenti per allocare fotogrammi multimediali:

MFCreateMemoryBuffer e MFCreateAlignedMemoryBuffer devono essere usati per i dati multimediali non allineati. In genere si tratta di sottotipi personalizzati o sottotipi compressi (ad esempio H264/HEVC/MJPG).

Per i tipi di supporti non compressi noti (ad esempio YUY2, NV12 e così via) usando la memoria di sistema, è consigliabile usare MFCreate2DMediaBuffer.

Per l'uso di superfici DX (per operazioni con accelerazione GPU, ad esempio rendering e/o codifica), è consigliabile usare MFCreateDXGISurfaceBuffer .

MFCreateDXGISurfaceBuffer non crea la superficie DX. La superficie viene creata utilizzando il DXGI Manager passato alla sorgente multimediale tramite il metodo IMFMediaSourceEx::SetD3DManager.

IMFDXGIDeviceManager::OpenDeviceHandle fornisce l'handle associato al dispositivo D3D selezionato. L'interfaccia ID3D11Device può quindi essere ottenuta usando il metodo IMFDXGIDeviceManager::GetVideoService .

Indipendentemente dal tipo di buffer usato, il IMFSample creato deve essere fornito alla pipeline tramite l'evento MEMediaSample sul IMFMediaEventGenerator del flusso multimediale.

Anche se è possibile usare lo stesso IMFMediaEventQueue sia per l'origine multimediale personalizzata che per la raccolta sottostante di IMFMediaStream, è necessario notare che questa operazione comporterà la serializzazione degli eventi di origine multimediale e degli eventi di flusso (che include il flusso multimediale). Per le sorgenti con più flussi, questo non è desiderabile.

L'analisi del codice seguente mostra un'implementazione di esempio del flusso multimediale:

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

Estensione origine multimediale personalizzata per esporre IMFActivate (disponibile in Windows 10 versione 1809)

Oltre all'elenco precedente di interfacce che devono essere supportate per un'origine multimediale personalizzata, una delle limitazioni imposte dall'operazione origine multimediale personalizzata all'interno dell'architettura del server frame è che può essere presente solo un'istanza del driver UMDF "attivata" tramite la pipeline.

Ad esempio, se si dispone di un dispositivo fisico che installa un driver stub UMDF oltre al pacchetto driver non AV Stream e si collegano più di questi dispositivi fisici a un computer, ogni istanza del driver UMDF otterrà un nome di collegamento simbolico univoco. Tuttavia, il percorso di attivazione per l'origine multimediale personalizzata non avrà un modo per comunicare, al momento della creazione, il nome di collegamento simbolico associato all'origine stessa.

L'origine multimediale personalizzata potrebbe cercare l'attributo MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK standard nell'archivio attributi dell'origine multimediale personalizzata (l'archivio attributi restituito dall'origine multimediale personalizzata tramite il metodo IMFMediaSourceEx::GetSourceAttributes) quando viene richiamato IMFMediaSource::Start.

Tuttavia, ciò potrebbe comportare una latenza di avvio più elevata, perché questa operazione rinvierà l'acquisizione della risorsa HW all'ora di inizio anziché all'ora di creazione/inizializzazione.

Per questo motivo, in Windows 10 versione 1809, origini multimediali personalizzate potrebbero esporre facoltativamente un'interfaccia IMFActivate .

Annotazioni

IMFActivate eredita da IMFAttributes.

IMFActivate

Se il server COM per l'origine multimediale personalizzata supporta l'interfaccia IMFActivate , le informazioni di inizializzazione del dispositivo vengono fornite al server COM tramite IMFAttributes ereditate da IMFActivate. Pertanto, quando viene richiamato IMFActivate::ActivateObject , l'archivio attributi di IMFActivate contiene il nome simbolico del collegamento simbolico del driver stub di UMDF e qualsiasi altra impostazione di configurazione fornita dalla pipeline/applicazione al momento della creazione/inizializzazione dell'origine.

L'origine multimediale personalizzata deve usare questa chiamata al metodo per acquisire tutte le risorse hardware necessarie.

Annotazioni

Se l'acquisizione delle risorse hardware richiede più di 200 millisecondi, è consigliabile acquisire in modo asincrono la risorsa hardware. L'attivazione dell'origine multimediale personalizzata non deve bloccare l'acquisizione delle risorse hardware. Al contrario , l'operazione IMFMediaSource::Start deve essere serializzata in base all'acquisizione delle risorse hardware.

I due altri metodi esposti da IMFActivate, DetachObject e ShutdownObject, devono restituire E_NOTIMPL.

L'origine multimediale personalizzata potrebbe scegliere di implementare l'interfaccia IMFActivate e IMFAttributes all'interno dello stesso oggetto COM di IMFMediaSource. In questo caso, è consigliabile che IMFMediaSourceEx::GetSourceAttributes restituisca la stessa interfaccia IMFAttributes di quelle del IMFActivate.

Se l'origine multimediale personalizzata non implementa IMFActivate e IMFAttributes con lo stesso oggetto, l'origine multimediale personalizzata deve copiare tutti gli attributi impostati nell'archivio attributi IMFActivate nell'archivio attributi di origine di Origine multimediale personalizzata.

Flusso della fotocamera codificata

Un'origine multimediale personalizzata potrebbe esporre tipi di supporti compressi (flussi elementari HEVC o H264) e la pipeline del sistema operativo supporta completamente l'origine e la configurazione dei parametri di codifica nell'origine multimediale personalizzata (i parametri di codifica vengono comunicati tramite ICodecAPI, che viene instradato come chiamata 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 struttura KSPROPERTY passata al metodo IKsControl::KsProperty contiene le informazioni seguenti:

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

Dove il GUID della proprietà del codificatore è l'elenco delle proprietà disponibili definite nelle proprietà del codec API.

Il payload della proprietà del codificatore verrà passato attraverso il campo pPropertyData del metodo KsProperty dichiarato in precedenza.

Requisiti del motore di acquisizione

Sebbene le origini codificate siano completamente supportate dal server frame, il motore di acquisizione lato client (IMFCaptureEngine) usato dall'oggetto Windows.Media.Capture.MediaCapture impone requisiti aggiuntivi:

  • Stream deve essere tutto codificato (HEVC o H264) o non compresso (in questo contesto MJPG viene considerato non compresso).

  • Deve essere disponibile almeno un flusso non compresso.

Annotazioni

Questi requisiti si aggiungono ai requisiti dell'origine multimediale personalizzata descritti in questo articolo. Tuttavia, i requisiti del motore di acquisizione vengono applicati solo quando l'applicazione client usa l'origine multimediale personalizzata tramite l'API IMFCaptureEngine o Windows.Media.Capture.MediaCapture .

Profili fotocamera (disponibili in Windows 10, versione 1803 e successive)

Il supporto per il profilo della fotocamera è disponibile per le origini multimediali personalizzate. Il meccanismo consigliato consiste nel pubblicare il profilo tramite l'attributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION dall'attributo della sorgente (IMFMediaSourceEx::GetSourceAttributes).

L'attributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION è un oggetto IUnknown dell'interfaccia IMFSensorProfileCollection . IMFSensorProfileCollection può essere ottenuto usando la funzione 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);
}

Profilo di autenticazione del volto

Se l'origine multimediale personalizzata è progettata per supportare il riconoscimento facciale di Windows Hello, è consigliabile pubblicare un profilo di autenticazione viso. I requisiti di un profilo di autenticazione viso sono i seguenti:

  • Il controllo DDI per l'autenticazione del viso deve essere supportato in un singolo flusso IR. Per altre informazioni, vedere KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.

  • Il flusso IR deve essere almeno 340 x 340 a 15 fps. Il formato deve essere L8, NV12 o MJPG contrassegnato con la compressione L8.

  • Il flusso RGB deve essere almeno 480 x 480 a 7,5 fps (questa operazione è necessaria solo se viene applicata l'autenticazione Multispectrum).

  • Il profilo di autenticazione viso deve avere l'ID profilo di: KSCAMERAPROFILE_FaceAuth_Mode,0.

Abbiamo raccomandato che il profilo di autenticazione facciale annunci solamente un tipo di supporto per ognuno dei flussi IR e RGB.

Controlli del flusso di foto

Se i flussi di foto indipendenti vengono esposti contrassegnando uno deiMF_DEVICESTREAM_STREAM_CATEGORY del flusso come PINNAME_IMAGE, è necessario un flusso con categoria di flusso di PINNAME_VIDEO_CAPTURE (ad esempio, un singolo flusso che espone solo il PINNAME_IMAGE non è un'origine multimediale valida).

Tramite IKsControl, il set di proprietà PROPSETID_VIDCAP_VIDEOCONTROL deve essere supportato. Per altre informazioni, vedere Proprietà del controllo video.