Origine multimediale personalizzata del server frame
In questo argomento vengono fornite informazioni sull'implementazione di un'origine multimediale personalizzata all'interno dell'architettura del server frame.
Opzioni di origine multimediale personalizzata e di flusso AV
Quando si decide come fornire il supporto del flusso di acquisizione video all'interno dell'architettura del server frame, sono disponibili due opzioni principali: Flusso AV e Origine multimediale personalizzata.
Il modello AV Stream è il modello di driver della fotocamera standard usando un driver miniport AV Stream (driver in modalità kernel). In genere i driver di flusso AV 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 può essere completamente personalizzato (proprietario) o può essere basato su un'origine fotocamera non tradizionale (ad esempio file o origini di rete).
Driver di flusso AV
Il vantaggio principale di un approccio del driver di flusso AV è che la gestione di PnP e Power Management/Gestione dispositivi è già gestita dal framework di flusso AV.
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 un driver di classe UVC 1.5 di Windows in modo che i dispositivi debbano semplicemente implementare il firmware.
Per i dispositivi basati su MIPI, il fornitore dovrà implementare il proprio driver miniport di flusso AV.
Origine multimediale personalizzata
Per le origini il cui driver di dispositivo è già disponibile (ma non un driver miniport di flusso AV) o origini che usano l'acquisizione di fotocamere non tradizionali, un driver di flusso AV potrebbe non essere valida. Ad esempio, una fotocamera IP connessa sulla rete non si adatta a un modello di driver di flusso AV.
In tali situazioni, un'origine multimediale personalizzata usando il modello server frame sarebbe un'alternativa.
Funzionalità | Origine multimediale personalizzata | Driver di flusso AV |
---|---|---|
PnP e Power Management | Deve essere implementato dal driver di origine e/o stub | Fornito dal framework di flusso AV |
Plug-in modalità utente | Non disponibile. L'origine multimediale personalizzata incorpora la logica di modalità utente specifica OEM/IHV. | DMFT, Platform DMFT e MFT0 per l'implementazione legacy |
Gruppo di sensori | Supportato | Supportato |
Profilo fotocamera V2 | Supportato | Supportato |
Profilo fotocamera V1 | Non supportato | Supportato |
Requisiti di origine multimediale personalizzati
Con l'introduzione di Fotocamera Windows Frame Server (definito server frame) è possibile eseguire questa operazione tramite un'origine multimediale personalizzata. Ciò richiede due componenti principali:
Pacchetto driver con un driver sbbed progettato per registrare/abilitare un'interfaccia del dispositivo fotocamera.
DLL COM che ospita l'origine multimediale personalizzata.
Il primo requisito è necessario per due scopi:
Un processo di controllo per assicurarsi che l'origine multimediale personalizzata sia installata 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:
L'origine multimediale personalizzata del server frame viene eseguita come servizio locale (non da confondere con il sistema locale; Il servizio locale è un account con privilegi molto basso 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 utente.
Dato questi vincoli, le origini multimediali personalizzate del server frame non devono tentare di accedere alle parti protette del file system o al Registro di sistema. In genere, l'accesso in lettura è consentito, ma l'accesso in scrittura non è.
Prestazioni
Come parte del modello server frame, esistono due casi in cui le origini multimediali personalizzate verranno create da un'istanza del server frame:
Durante la generazione/pubblicazione del gruppo di sensori.
Durante l'attivazione di "fotocamera"
La generazione del gruppo di sensori viene in genere eseguita durante l'installazione del dispositivo e/o il ciclo di alimentazione. Dato questo, è consigliabile che le origini multimediali personalizzate evitino qualsiasi elaborazione significativa durante la sua creazione e rinviino qualsiasi attività di tale attività alla funzione FMMediaSource::Start . La generazione del gruppo di sensori non tenterà di avviare l'origine multimediale personalizzata, semplicemente eseguire una query sui vari tipi di flussi/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.
Nota
Per consentire l'enumerazione da parte delle applicazioni DirectShow legacy, il driver dovrà anche registrarsi nel KSCATEGORY_VIDEOe 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 di origine multimediale personalizzata. Questa operazione deve essere aggiunta usando il nome del valore del Registro di sistema seguente: CustomCaptureSourceClsid.
Ciò consente di individuare l'origine "fotocamera" dalle applicazioni e, quando attivata, informa il servizio Frame Server di intercettare la chiamata di attivazione e reindirizzarla all'origine multimediale personalizzata CoCreata.
Esempio DI INF
L'esempio seguente mostra un tipico inF per un driver stub di origine 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 assicurarsi che la "fotocamera" sia individuabile da qualsiasi app UWP e non UWP che cerca una fotocamera RGB standard.
Se l'origine multimediale personalizzata espone anche flussi non RGB (IR, Profondità e così via) può anche registrarsi nella KSCATEGORY_SENSOR_CAMERA.
Nota
La maggior parte delle webcam basate su USB espone i 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 supporto YUY2 sia reso disponibile dall'origine multimediale personalizzata se è desiderata la compatibilità dell'app legacy.
Implementazione del driver Stub
Oltre a INF, lo stub del driver deve anche registrare e abilitare le interfacce del dispositivo 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 del 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
Analogamente a 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 ascoltino l'aggiunta/rimozione dei dispositivi tramite le API PnP ottengono le notifiche appropriate. E assicura che non sia più disponibile un'origine che non sia 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 origine multimediale personalizzata
L'origine multimediale personalizzata è un server COM standard che deve implementare le interfacce seguenti:
Nota
FMMediaSourceEx eredita da FMMediaSource e FMMediaSource eredita da FMMediaEventGenerator.
Ogni flusso supportato all'interno dell'origine multimediale personalizzata deve supportare le interfacce seguenti:
FMMediaEventGenerator
FMMediaStream2
Nota
FMMediaStream2 eredita da FMMediaStream e FMMediaStream eredita da FMMediaEventGenerator.
Fare riferimento alla documentazione relativa alla scrittura di un'origine multimediale personalizzata su come creare un'origine multimediale personalizzata. La parte restante di questa sezione illustra le differenze necessarie per supportare l'origine multimediale personalizzata nel framework del server frame.
FMGetService
IMFGetService è un'interfaccia obbligatoria per l'origine multimediale personalizzata del server frame. FMGetService può restituire MF_E_UNSUPPORTED_SERVICE se l'origine multimediale personalizzata non deve esporre alcuna interfaccia di servizio aggiuntiva.
Nell'esempio seguente viene illustrata un'implementazione di IMFGetService senza interfacce di 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;
}
FMMediaEventGenerator
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 della pipeline MF e del controllo dall'origine viene gestito tramite il generatore di eventi inviando specifici FMMediaEvent.
Per l'implementazione di IMFMediaEventGenerator, l'origine multimediale personalizzata deve usare l'API MFCreateEventQueue per creare un'api FMMediaEventQueue e instradare tutti i metodi per FMMediaEventGenerator 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 mostra l'implementazione consigliata dell'interfaccia IMFMediaEventGenerator . L'implementazione dell'origine multimediale personalizzata espone l'interfaccia IMFMediaEventGenerator e i metodi per tale interfaccia instradano le richieste nell'oggetto FMMediaEventQueue creato durante la creazione/inizializzazione dell'origine multimediale.
Nel codice seguente, _spEventQueue oggetto è l'oggetto 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 sospensione
Le origini multimediali personalizzate supportate tramite il framework server frame non supportano le operazioni Seek o Pause. L'origine multimediale personalizzata non deve fornire supporto per queste operazioni e non deve pubblicare l'evento MFSourceSeeked o MEStreamSeeked .
FMMediaSource::P ause deve restituire MF_E_INVALID_STATE_TRANSITION (o MF_E_SHUTDOWN se l'origine è già stata arrestata).
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 modo in cui la pipeline instrada il controllo I/O.
Per altre informazioni, vedere gli argomenti della documentazione del 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 di 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);
}
FMMediaStream2
Come illustrato in Scrittura di un'origine multimediale personalizzata, l'interfaccia FMMediaStream2 viene fornita al lavoro del frame dall'origine multimediale personalizzata tramite un evento multimediale MENewStream pubblicato nella coda di 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 fmiPresentationDescriptor.
Per 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 per essere PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW non è supportato per origini multimediali personalizzate.
Nota
PINNAME_IMAGE, mentre supportato, non è consigliabile. L'esposizione di un flusso con PINNAME_IMAGE richiede l'origine multimediale personalizzata per supportare tutti i controlli del trigger foto. Per altre informazioni, vedere 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 di attributi consigliati nel flusso:
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES è un attributo UINT32 che rappresenta un valore bitmasked di tipo di flusso. Può essere impostato su uno dei seguenti (mentre questi tipi sono un flag di maschera bit, è consigliabile che i tipi di origine non siano misti se possibile):
Tipo | Flag | Descrizione |
---|---|---|
MFFrameSourceTypes_Color | 0x0001 | Flusso di colori RGB standard |
MFFrameSourceTypes_Infrared | 0x0002 | Flusso ir |
MFFrameSourceTypes_Depth | 0x0004 | Flusso di profondità |
MFFrameSourceTypes_Image | 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 potranno aprire il flusso in modalità condivisa, anche se usate da un'altra app.
Se questo attributo non è impostato, Il server frame consentirà la condivisione del primo flusso non contrassegnato (se l'origine multimediale personalizzata ha un solo flusso, tale flusso verrà contrassegnato come condiviso).
Se questo attributo è impostato su 0, Frame Server blocca il flusso da app condivise. Se l'origine multimediale personalizzata contrassegna tutti i flussi con questo attributo impostato su 0, non sarà possibile inizializzare l'origine.
Allocazione di esempio
Tutti i fotogrammi multimediali devono essere prodotti come fmiSample. Le origini multimediali personalizzate devono usare la funzione MFCreateSample per allocare un'istanza di FMSample e usare il metodo AddBuffer per aggiungere buffer multimediali.
Ogni FMISample deve avere il tempo di esempio e il set di durata di esempio. Tutti i timestamp di esempio devono essere basati sull'ora QPC (QueryPerformanceCounter).
È consigliabile usare la funzione MFGetSystemTime se possibile. Questa funzione è un wrapper intorno a QueryPerformanceCounter e converte i tick QPC in 100 unità nanosecondi.
Le origini multimediali personalizzate possono usare un orologio interno, ma tutti i timestamp devono essere correlati a 100 unità nanosecondhe in base al QPC corrente.
Buffer multimediale
Tutti i buffer multimediali aggiunti a FMSample devono usare le funzioni di allocazione del buffer MF standard. Le origini multimediali personalizzate non devono implementare le proprie interfacce FMMediaBuffer o tentare di allocare direttamente il buffer multimediale (ad esempio, new/malloc/VirtualAlloc e così via, non devono essere usate per i dati frame).
Usare una delle API seguenti per allocare fotogrammi multimediali:
MFCreateMemoryBuffer e MFCreateAlignedMemoryBuffer devono essere usati per i dati multimediali allineati non stride. In genere questi sono 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), è necessario usare MFCreateDGISurfaceBuffer .
MFCreateDXGISurfaceBuffer non crea la superficie DX. La superficie viene creata usando il metodo DXGI Manager passato all'origine multimediale tramite il metodo IMFMediaSourceEx::SetD3DManager .
FmiDXGIDeviceManager::OpenDeviceHandle fornirà 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, l'FMSample creato deve essere fornito alla pipeline tramite l'evento MEMediaSample sul FMMediaEventGenerator del flusso multimediale.
Anche se è possibile usare lo stesso FMMediaEventQueue sia per l'origine multimediale personalizzata che per la raccolta sottostante di FMMediaStream, è necessario notare che in questo modo la serializzazione degli eventi di origine multimediale e degli eventi di flusso (che include il flusso multimediale). Per le origini con più flussi, questo non è auspicabile.
L'snip 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 FMActivate (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 "attivato" tramite la pipeline.
Ad esempio, se si dispone di un dispositivo fisico che installa un driver stub UMDF oltre al pacchetto di driver di flusso non AV e si associano più di uno di questi dispositivi fisici a un computer, mentre ogni istanza del driver UMDF otterrà un nome di collegamento simbolico univoco, il percorso di attivazione per l'origine multimediale personalizzata non avrà un mezzo per comunicare il nome di collegamento simbolico associato all'origine multimediale personalizzata all'indirizzo ora di creazione.
L'origine multimediale personalizzata può cercare l'attributo standard MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK nell'archivio attributi dell'origine multimediale personalizzata (l'archivio attributi restituito dall'origine multimediale personalizzata tramite il metodo IMFMediaSourceEx::GetSourceAttributes ) al momento in cui viene richiamato FMMediaSource: :Start .
Tuttavia, ciò può comportare una latenza di avvio più elevata poiché questa operazione rinvierà l'acquisizione delle risorse HW all'ora di inizio anziché all'ora di creazione/inizializzazione.
A causa di questo, in Windows 10, versione 1809, origini multimediali personalizzate possono esporre facoltativamente un'interfaccia FMActivate.
Nota
FMIActivate eredita da FMAttributes.
FMIActivate
Se il server COM per l'origine multimediale personalizzata supporta l'interfaccia FMActivate, le informazioni di inizializzazione del dispositivo verranno fornite al server COM tramite FMAttributes ereditato dall'FMIActivate. Quindi, quando viene richiamato l'oggetto IMFActivate::ActivateObject, l'archivio attributi dell'FMActivate conterrà il nome simbolico del driver stub UMDF e eventuali impostazioni di configurazione aggiuntive fornite 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.
Nota
Se l'acquisizione di risorse hardware richiede più di 200 millisecondi, è consigliabile acquisire la risorsa hardware in modo asincrono. L'attivazione dell'origine multimediale personalizzata non deve essere bloccata nell'acquisizione delle risorse hardware. Al contrario , l'operazione IMFMediaSource::Start deve essere serializzata rispetto all'acquisizione di risorse hardware.
I due metodi aggiuntivi esposti da FMActivate, DetachObject e ShutdownObject devono restituire E_NOTIMPL.
L'origine multimediale personalizzata può scegliere di implementare l'interfaccia FMActivate e FMAttributes all'interno dello stesso oggetto COM dell'FMIMediaSource. Se questa operazione viene eseguita, è consigliabile che l'FMIMediaSourceEx::GetSourceAttributes restituisca la stessa interfaccia FMAttributes di quelle dell'FMIActivate.
Se l'origine multimediale personalizzata non implementa l'oggetto FMActivate e FMAttributes con lo stesso oggetto, l'origine multimediale personalizzata deve copiare tutti gli attributi impostati nell'archivio attributi fmiActivate nell'archivio attributi di origine di Origine multimediale personalizzata.
Flusso di fotocamera codificato
Un'origine multimediale personalizzata può 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 avrà 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à codificatore è l'elenco delle proprietà disponibili definite nelle proprietà dell'API Codec.
Il payload della proprietà 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 da Frame Server, il motore di acquisizione lato client (FMCaptureEngine) usato dall'oggetto Windows.Media.Capture.MediaCapture impone requisiti aggiuntivi:
Il flusso deve essere tutto codificato (HEVC o H264) o tutto non compresso (in questo contesto MJPG viene considerato non compresso).
È necessario che sia disponibile almeno un flusso non compresso.
Nota
Questi requisiti sono oltre ai requisiti di origine multimediale personalizzati descritti in questo argomento. Tuttavia, i requisiti del motore di acquisizione vengono applicati solo quando l'applicazione client usa l'origine multimediale personalizzata tramite l'API FMCaptureEngine o Windows.MediaCapture.
Profili fotocamera (disponibili in Windows 10, versione 1803 e successiva)
Il supporto del profilo della fotocamera è disponibile per origini multimediali personalizzate. Il meccanismo consigliato consiste nel pubblicare il profilo tramite l'attributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION dall'attributo di origine (FMMediaSourceEx::GetSourceAttributes).
L'attributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION è un oggetto IUnknown dell'interfaccia FMSensorProfileCollection . FMSensorProfileCollection 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 viso
Se l'origine multimediale personalizzata è progettata per supportare Windows Hello riconoscimento facciale, è consigliabile pubblicare un profilo di autenticazione viso. I requisiti di un profilo di autenticazione viso sono:
Il controllo DDI per l'autenticazione viso deve essere supportato in un singolo flusso di integrazione. 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 (questo è necessario solo se viene applicata l'autenticazione Multispectrum).
Il profilo di autenticazione viso deve avere l'ID profilo di: KSCAMERAPROFILE_FaceAuth_Mode.0.
È consigliabile che il profilo di autenticazione viso pubblicizzi un solo tipo di supporto per ognuno dei flussi IR e RGB.
Controlli di flusso foto
Se i flussi di foto indipendenti vengono esposti contrassegnando una delle MF_DEVICESTREAM_STREAM_CATEGORY del flusso come PINNAME_IMAGE, è necessario un flusso con categoria di flusso diPINNAME_VIDEO_CAPTURE (ad esempio, un singolo flusso che espone solo la PINNAME_IMAGE non è un'origine multimediale valida).
Tramite IKsControl, il set di proprietà PROPSETID_VIDCAP_VIDEOCONTROL deve essere supportato. Per altre informazioni, vedere Proprietà controllo video.