Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este artigo fornece informações sobre a implementação de uma fonte de mídia personalizada na arquitetura do Frame Server.
Opções de fluxo AV e fonte de mídia personalizada
Ao decidir como fornecer suporte a fluxo de captura de vídeo dentro da arquitetura do Frame Server, há duas opções principais: AV Stream e Custom Media Source.
O modelo AV Stream é o modelo padrão de controlador de câmera que utiliza um controlador de miniporta AV Stream (controlador em modo kernel). Normalmente, os drivers AV Stream se enquadram em duas categorias principais: drivers baseados em MIPI e drivers de classe de vídeo USB.
Para a opção Fonte de mídia personalizada, o modelo de driver pode ser completamente personalizado (proprietário) ou pode ser baseado em uma fonte de câmera não tradicional (como fontes de arquivo ou de rede).
Controlador de fluxo AV
O principal benefício de uma abordagem AV Stream Driver é que o PnP e o Gerenciamento de Energia/Gerenciamento de Dispositivos já são tratados pela estrutura AV Stream.
No entanto, isso também significa que a fonte subjacente deve ser um dispositivo físico com um driver de modo kernel para interagir com o hardware. Para dispositivos UVC, um driver de classe Windows UVC 1.5 é fornecido nativamente para que os dispositivos precisem apenas implementar o seu firmware.
Para dispositivos baseados em MIPI, o fornecedor precisa implementar seu próprio driver de miniporta AV Stream.
Fonte de mídia personalizada
Para fontes cujo driver de dispositivo já está disponível (mas não um driver de miniporta AV Stream) ou fontes que usam captura de câmera não tradicional, um driver de fluxo AV pode não ser viável. Por exemplo, uma câmera IP conectada pela rede não caberia em um modelo de driver de fluxo AV.
Em tais situações, uma fonte de mídia personalizada usando o modelo de Frame Server seria uma alternativa.
| Caraterísticas | Fonte de mídia personalizada | Controlador de fluxo AV |
|---|---|---|
| PnP e Gestão de Energia | Deve ser implementado pelo driver base e/ou stub | Fornecido pela estrutura AV Stream |
| Plug-in de modo de utilizador | Não disponível. A fonte de mídia personalizada incorpora a lógica de modo de usuário específica do OEM/IHV. | DMFT, Platform DMFT e MFT0 para implementação herdada |
| Grupo de Sensores | Suportado | Suportado |
| Perfil da câmera V2 | Suportado | Suportado |
| Perfil da câmera V1 | Não suportado | Suportado |
Requisitos de fonte de mídia personalizada
Com a introdução do serviço Windows Camera Frame Server (conhecido como Frame Server), isso pode ser feito por meio de uma fonte de mídia personalizada. Isto requer duas componentes principais:
Um pacote de driver com um driver stubbed projetado para registrar/habilitar uma interface de dispositivo de câmera.
Uma DLL COM que hospeda a fonte de mídia personalizada.
O primeiro requisito é necessário para dois fins:
Um processo de verificação para garantir que a fonte de mídia personalizada seja instalada por meio de um processo confiável (o pacote de driver requer certificação WHQL).
Suporte para a enumeração padrão PnP e detecção da "câmara".
Segurança
A fonte de mídia personalizada para o Frame Server difere da fonte de mídia personalizada genérica em termos de segurança da seguinte maneira:
Frame Server Custom Media Source é executado como Serviço Local (não confundir com Sistema Local; Local Service é uma conta com privilégios baixos em máquinas Windows).
A fonte de mídia personalizada do Frame Server é executada na Sessão 0 (sessão do Serviço do Sistema) e não pode interagir com a área de trabalho do usuário.
Dadas essas restrições, as fontes de mídia personalizadas do Frame Server não devem tentar acessar partes protegidas do sistema de arquivos nem o registro. Geralmente, o acesso de leitura é permitido, mas o acesso de gravação não.
Desempenho
Como parte do modelo do Frame Server, há dois casos em como as fontes de mídia personalizadas serão instanciadas pelo Frame Server:
Durante a geração/publicação do Sensor Group.
Durante a ativação da "câmara"
A geração do Grupo de Sensores é normalmente feita durante a instalação do dispositivo e/ou ciclo de alimentação. Diante disso, recomendamos vivamente que as Fontes de Mídia Personalizadas evitem qualquer processamento significativo durante a sua criação e adiem qualquer atividade desse tipo para a função IMFMediaSource::Start . A geração do Grupo de Sensores não tentará iniciar a Fonte de Mídia Personalizada, apenas consultará os vários fluxos/tipos de mídia disponíveis e informações de atributos de origem/fluxo.
Driver de esboço
Há dois requisitos mínimos para o pacote de driver e o driver stub.
O driver de stub pode ser escrito usando o WDF (UMDF ou KMDF) ou o modelo de driver WDM.
Os requisitos do driver são:
- Registe a interface do dispositivo "camera" (a fonte de multimédia personalizada) na categoria KSCATEGORY_VIDEO_CAMERA para que possa ser enumerada.
Observação
Para permitir a enumeração por aplicativos DirectShow herdados, seu driver também precisa se registrar sob o KSCATEGORY_VIDEO e KSCATEGORY_CAPTURE.
- Adicione uma entrada do Registro sob o nó da interface do dispositivo (use a diretiva AddReg na seção INF DDInstall.Interface do driver) que declara o CLSID CoCreate-able do seu objeto COM de fonte de mídia personalizada. Isso deve ser adicionado usando o seguinte nome de valor do registo: CustomCaptureSourceClsid.
Isso permite que a fonte da "câmera" seja descoberta pelos aplicativos e, quando ativada, informa o serviço Frame Server para intercetar a chamada de ativação e redirecioná-la para a fonte de mídia personalizada CoCreated.
Amostra INF
O exemplo abaixo mostra um INF típico para um driver stub de Origem de Mídia Personalizada.
;/*++
;
;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
A fonte de mídia personalizada acima se registra em KSCATEGORY_VIDEO, KSCATEGORY_CAPTURE e KSCATEGORY_VIDEO_CAMERA para garantir que a "câmera" seja detetável por qualquer aplicativo UWP e não UWP que procure uma câmera RGB padrão.
Se a fonte de mídia personalizada também expõe fluxos não RGB (IR, profundidade e assim por diante), ela também pode, opcionalmente, se registrar sob o KSCATEGORY_SENSOR_CAMERA.
Observação
A maioria das webcams baseadas em USB expõem os formatos YUY2 e MJPG. Devido a esse comportamento, muitos aplicativos DirectShow herdados são escritos com a suposição de que YUY2/MJPG está disponível. Para garantir a compatibilidade com tal aplicativo, é recomendável que o tipo de mídia YUY2 seja disponibilizado a partir de sua fonte de mídia personalizada caso seja desejada a compatibilidade com aplicações legadas.
Implementação do driver de stub
Além do INF, o driver stub deve também registrar e ativar as interfaces do dispositivo da câmera. Isso geralmente é feito durante a operação de DRIVER_ADD_DEVICE.
Consulte a função de retorno de chamada DRIVER_ADD_DEVICE para drivers baseados em WDM e a função WdfDriverCreate para drivers UMDF/KMDF.
A seguir está um trecho de código de um stub de driver UMDF que lida com essa operação:
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;
}
Operação PnP
Assim como qualquer outra câmera física, é recomendável que seu driver de stub gerencie pelo menos as operações PnP de ativar e desativar o dispositivo quando a fonte subjacente for removida/conectada. Por exemplo, se a fonte de mídia personalizada estiver usando uma fonte de rede (como uma câmera IP), convém acionar a remoção de um dispositivo quando essa fonte de rede não estiver mais disponível.
Isso garante que os aplicativos escutem a adição/remoção de dispositivos por meio das APIs PnP recebam as notificações adequadas. E garante que uma fonte que não está mais disponível não possa ser enumerada.
Para controladores UMDF e KMDF, consulte a documentação da função WdfDeviceSetDeviceState.
Para drivers WMD, consulte a documentação da função IoSetDeviceInterfaceState .
DLL de fonte de mídia personalizada
A fonte de mídia personalizada é um servidor COM inproc padrão que deve implementar as seguintes interfaces:
Observação
IMFMediaSourceEx herda de IMFMediaSource e IMFMediaSource herda de IMFMediaEventGenerator.
Cada fluxo suportado dentro da fonte de mídia personalizada deve suportar as seguintes interfaces:
IMFMediaEventGenerator
IMFMediaStream2
Observação
IMFMediaStream2 herda de IMFMediaStream e IMFMediaStream herda de IMFMediaEventGenerator.
Consulte a documentação Escrevendo uma fonte de mídia personalizada sobre como criar uma fonte de mídia personalizada. O restante desta seção explica as diferenças necessárias para dar suporte à sua fonte de mídia personalizada dentro da estrutura do Frame Server.
IMFGetService
IMFGetService é uma interface obrigatória para Frame Server Custom Media Source. O IMFGetService pode retornar MF_E_UNSUPPORTED_SERVICE se a sua Fonte de Mídia Personalizada não precisar expor outras interfaces de serviço.
O exemplo a seguir mostra uma implementação IMFGetService sem interfaces de serviço de suporte:
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::GetService(
_In_ REFGUID guidService,
_In_ REFIID riid,
_Out_ LPVOID * ppvObject
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
if (!ppvObject)
{
return E_POINTER;
}
*ppvObject = NULL;
// We have no supported service, just return
// MF_E_UNSUPPORTED_SERVICE for all calls.
return MF_E_UNSUPPORTED_SERVICE;
}
IMFMediaEventGenerator
Como mostrado acima, tanto a fonte quanto os fluxos individuais dentro da fonte devem suportar sua própria interface IMFMediaEventGenerator . Todos os dados do pipeline MF e os fluxos de controle pela origem são geridos através do gerador de eventos, enviando um IMFMediaEvent específico.
Para implementar IMFMediaEventGenerator, a fonte de mídia personalizada deve usar a API MFCreateEventQueue para criar um IMFMediaEventQueue e rotear todos os métodos para IMFMediaEventGenerator para o objeto de fila:
IMFMediaEventGenerator tem os seguintes métodos:
// 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);
O código a seguir mostra a implementação recomendada da interface IMFMediaEventGenerator . A implementação Custom Media Source expõe a interface IMFMediaEventGenerator e os métodos para essa interface estão roteando as solicitações para o objeto IMFMediaEventQueue criado durante a criação/inicialização da fonte de mídia.
No código abaixo, _spEventQueue objeto é o IMFMediaEventQueue criado usando a função 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;
}
Procurando e Pausando
As fontes de mídia personalizadas suportadas pela estrutura do Frame Server não suportam operações de busca ou pausa. Sua fonte de mídia personalizada não precisa fornecer suporte para essas operações e não deve postar o evento MFSourceSeeked ou MEStreamSeeked .
IMFMediaSource::Pause deve retornar MF_E_INVALID_STATE_TRANSITION (ou MF_E_SHUTDOWN se a fonte já estiver encerrada).
IKsControl
IKsControl é a interface de controle padrão para todos os controles relacionados à câmera. Se sua fonte de mídia personalizada implementa qualquer controle de câmera, a interface IKsControl é como o pipeline roteia a E/S de controle.
Para obter mais informações, consulte os seguintes artigos da documentação do conjunto de controle:
Os controles são opcionais e, se não forem suportados, o código de erro recomendado para retornar é HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND).
O código a seguir é um exemplo de implementação IKsControl sem controles suportados:
// IKsControl methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsProperty(
_In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
_In_ ULONG ulPropertyLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned
)
{
// ERROR_SET_NOT_FOUND is the standard error code returned
// by the AV Stream driver framework when a miniport
// driver does not register a handler for a KS operation.
// We want to mimic the driver behavior here if we do not
// support controls.
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP SimpleMediaSource::KsMethod(
_In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod,
_In_ ULONG ulMethodLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pMethodData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned
)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP SimpleMediaSource::KsEvent(
_In_reads_bytes_opt_(ulEventLength) PKSEVENT pEvent,
_In_ ULONG ulEventLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pEventData,
_In_ ULONG ulDataLength,
_Out_opt_ ULONG* pBytesReturned
)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
IMFMediaStream2
Como explicado em Escrevendo uma fonte de mídia personalizada, a interface IMFMediaStream2 é fornecida para a estrutura a partir da sua fonte de mídia personalizada através de um evento de mídia MENewStream, que é postado na fila de eventos de origem durante a conclusão do método IMFMediaSource::Start:
IFACEMETHODIMP
SimpleMediaSource::Start(
_In_ IMFPresentationDescriptor *pPresentationDescriptor,
_In_opt_ const GUID *pguidTimeFormat,
_In_ const PROPVARIANT *pvarStartPos
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
DWORD count = 0;
PROPVARIANT startTime;
BOOL selected = false;
ComPtr<IMFStreamDescriptor> streamDesc;
DWORD streamIndex = 0;
if (pPresentationDescriptor == nullptr || pvarStartPos == nullptr)
{
return E_INVALIDARG;
}
else if (pguidTimeFormat != nullptr && *pguidTimeFormat != GUID_NULL)
{
return MF_E_UNSUPPORTED_TIME_FORMAT;
}
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
if (_sourceState != SourceState::Stopped)
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Started;
// This checks the passed in PresentationDescriptor matches the member of streams we
// have defined internally and that at least one stream is selected
RETURN_IF_FAILED (_ValidatePresentationDescriptor(pPresentationDescriptor));
RETURN_IF_FAILED (pPresentationDescriptor->GetStreamDescriptorCount(&count));
RETURN_IF_FAILED (InitPropVariantFromInt64(MFGetSystemTime(), &startTime));
// Send event that the source started. Include error code in case it failed.
RETURN_IF_FAILED (_spEventQueue->QueueEventParamVar(MESourceStarted,
GUID_NULL,
hr,
&startTime));
// We are hardcoding this to the first descriptor
// since this sample is a single stream sample. For
// multiple streams, we need to walk the list of streams
// and for each selected stream, send the MEUpdatedStream
// or MENewStream event along with the MEStreamStarted
// event.
RETURN_IF_FAILED (pPresentationDescriptor->GetStreamDescriptorByIndex(0,
&selected,
&streamDesc));
RETURN_IF_FAILED (streamDesc->GetStreamIdentifier(&streamIndex));
if (streamIndex >= NUM_STREAMS)
{
return MF_E_INVALIDSTREAMNUMBER;
}
if (selected)
{
ComPtr<IUnknown> spunkStream;
MediaEventType met = (_wasStreamPreviouslySelected ? MEUpdatedStream : MENewStream);
// Update our internal PresentationDescriptor
RETURN_IF_FAILED (_spPresentationDescriptor->SelectStream(streamIndex));
RETURN_IF_FAILED (_stream.Get()->SetStreamState(MF_STREAM_STATE_RUNNING));
RETURN_IF_FAILED (_stream.As(&spunkStream));
// Send the MEUpdatedStream/MENewStream to our source event
// queue.
RETURN_IF_FAILED (_spEventQueue->QueueEventParamUnk(met,
GUID_NULL,
S_OK,
spunkStream.Get()));
// But for our stream started (MEStreamStarted), we post to our
// stream event queue.
RETURN_IF_FAILED (_stream.Get()->QueueEvent(MEStreamStarted,
GUID_NULL,
S_OK,
&startTime));
}
_wasStreamPreviouslySelected = selected;
return hr;
}
Isso deve ser feito para cada fluxo selecionado através do IMFPresentationDescriptor.
Para fontes de mídia personalizadas com fluxo de vídeo, os eventos MEEndOfStream e MEEndOfPresentation não devem ser enviados.
Atributos de fluxo
Todos os fluxos de fonte de mídia personalizada devem ter o MF_DEVICESTREAM_STREAM_CATEGORY definido para ser PINNAME_VIDEO_CAPTURE. PINNAME_VIDEO_PREVIEW não é suportado para fontes de mídia personalizadas.
Observação
PINNAME_IMAGE, embora suportado, não é recomendado. Expor um fluxo com PINNAME_IMAGE requer que a fonte de mídia personalizada ofereça suporte a todos os controlos de disparo fotográfico. Consulte a seção Controles de fluxo de fotos abaixo para obter mais detalhes.
MF_DEVICESTREAM_STREAM_ID é um atributo obrigatório para todos os fluxos. Deve ser um índice baseado em 0. Assim, o primeiro fluxo tem um ID de 0, o segundo fluxo um ID de 1 e assim por diante.
A seguir está uma lista de atributos recomendados no fluxo:
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES é um atributo UINT32 que é um valor mascarado de bits do tipo de fluxo. Ele pode ser definido como qualquer um dos seguintes (embora esses tipos sejam um sinalizador de máscara de bits, é recomendável que os tipos de origem não sejam misturados, se possível):
| Tipo | Bandeira | Descrição |
|---|---|---|
| MFFrameSourceTypes_Color | 0x0001 | Fluxo de cores RGB padrão |
| MFFrameSourceTypes_Infrared | 0x0002 | Fluxo de IR |
| MFFrameSourceTypes_Depth | 0x0004 | Fluxo de profundidade |
| MFFrameSourceTypes_Image | 0x0008 | Fluxo de imagem (subtipo não vídeo, normalmente JPEG) |
| MFFrameSourceTypes_Custom | 0x0080 | Tipo de fluxo personalizado |
MF_DEVICESTREAM_FRAMESERVER_SHARED
MF_DEVICESTREAM_FRAMESERVER_SHARED é um atributo UINT32 que pode ser definido como 0 ou 1. Se definido como 1, ele marca o fluxo como sendo "compartilhável" pelo Frame Server. Isso permite que os aplicativos abram o fluxo em um modo compartilhado, mesmo quando usado por outro aplicativo.
Se esse atributo não estiver definido, o Frame Server permitirá que o primeiro fluxo não marcado seja compartilhado (se a Fonte de Mídia Personalizada tiver apenas um fluxo, esse fluxo será marcado como compartilhado).
Se esse atributo estiver definido como 0, o Frame Server bloqueará o fluxo de aplicativos compartilhados. Se a fonte de mídia personalizada marcar todos os fluxos com esse atributo definido como 0, nenhum aplicativo compartilhado poderá inicializar a fonte.
Alocação de amostras
Todos os frames de mídia devem ser produzidos como um IMFSample. Fontes de mídia personalizadas devem usar a função MFCreateSample para alocar uma instância de IMFSample e usar o método AddBuffer para adicionar buffers de mídia.
Cada IMFSample deve ter o tempo e a duração da amostra definidos. Todos os carimbos de data/hora das amostras devem ser baseados no tempo do QPC (QueryPerformanceCounter).
Recomenda-se que as fontes de mídia personalizadas usem a função MFGetSystemTime sempre que possível. Esta função atua como um invólucro para QueryPerformanceCounter e converte os ticks QPC em unidades de 100 nanossegundos.
As fontes de mídia personalizadas podem usar um relógio interno, mas todos os carimbos de data/hora devem ser correlacionados a unidades de 100 nanossegundos com base no QPC atual.
Buffer de mídia
Todos os buffers de mídia adicionados ao IMFSample devem usar as funções padrão de alocação de buffer MF. Fontes de mídia personalizadas não devem implementar as suas próprias interfaces IMFMediaBuffer nem tentar alocar buffer de mídia diretamente (por exemplo, new/malloc/VirtualAlloc, e assim por diante, não devem ser usados para dados do frame).
Use qualquer uma das seguintes APIs para alocar quadros de mídia:
MFCreateMemoryBuffer e MFCreateAlignedMemoryBuffer devem ser usados para dados de mídia alinhados não escalonados. Normalmente, estes seriam subtipos personalizados ou subtipos compactados (como H264/HEVC/MJPG).
Para tipos de mídia não compactados conhecidos (como YUY2, NV12 e assim por diante) usando a memória do sistema, é recomendável usar MFCreate2DMediaBuffer.
Para usar superfícies DX (para operações aceleradas por GPU, como renderização e/ou codificação), MFCreateDXGISurfaceBuffer deve ser usado.
MFCreateDXGISurfaceBuffer não cria a superfície DX. A superfície é criada usando o Gerenciador DXGI passado para a fonte de mídia através do método IMFMediaSourceEx::SetD3DManager .
O IMFDXGIDeviceManager::OpenDeviceHandle fornece o identificador associado ao dispositivo D3D selecionado. A interface ID3D11Device pode ser obtida usando o método IMFDXGIDeviceManager::GetVideoService .
Independentemente do tipo de buffer usado, o IMFSample criado deve ser fornecido ao pipeline por meio do evento MEMediaSample no IMFMediaEventGenerator do fluxo de mídia.
Embora seja possível usar o mesmo IMFMediaEventQueue para a fonte de mídia personalizada e a coleção subjacente de IMFMediaStream, deve-se notar que isso resultará na serialização dos eventos de origem de mídia e eventos de fluxo (que inclui o fluxo de mídia). Para fontes com vários fluxos, isso não é desejável.
O trecho de código a seguir mostra uma implementação de exemplo do fluxo de mí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;
}
Extensão de fonte de mídia personalizada para expor IMFActivate (disponível no Windows 10, versão 1809)
Além da lista acima de interfaces que devem ser suportadas para uma fonte de mídia personalizada, uma das limitações impostas pela operação de fonte de mídia personalizada dentro da arquitetura do Frame Server é que só pode haver uma instância do driver UMDF "ativado" através do pipeline.
Por exemplo, se o utilizador tiver um dispositivo físico que instala um driver de stub UMDF, além de seu pacote de driver que não seja de fluxo AV, e conectar mais de um desses dispositivos físicos a um computador, enquanto cada instância do driver UMDF receberá um nome de link simbólico exclusivo, o caminho de ativação para a fonte de mídia personalizada não terá um meio de comunicar o nome do link simbólico associado à fonte de mídia personalizada no momento da criação.
Fonte de Mídia Personalizada pode procurar pelo atributo padrão MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK no repositório de atributos da Fonte de Mídia Personalizada (o repositório de atributos retornado da Fonte de Mídia Personalizada através do método IMFMediaSourceEx::GetSourceAttributes) no momento em que o processo IMFMediaSource::Start é invocado.
No entanto, isso pode resultar em uma latência de inicialização mais alta, uma vez que isso adiará a aquisição de recursos HW para o tempo de início em vez do tempo de criação/inicialização.
Devido a isso, no Windows 10, versão 1809, fontes de mídia personalizadas podem opcionalmente expor uma interface IMFActivate .
Observação
IMFActivate herda de IMFAttributes.
IMFActivate
Se o servidor COM para a fonte de mídia personalizada suportar a interface IMFActivate , as informações de inicialização do dispositivo serão fornecidas ao servidor COM por meio dos IMFAttributes herdados pelo IMFActivate. Portanto, quando o IMFActivate::ActivateObject é invocado, o repositório de atributos do IMFActivate contém o nome do link simbólico do driver de stub UMDF e quaisquer outras definições de configuração fornecidas pelo pipeline/aplicativo no momento da criação/inicialização do código-fonte.
A fonte de mídia personalizada deve usar essa invocação de método para adquirir quaisquer recursos de hardware necessários.
Observação
Se a aquisição de recursos de hardware demorar mais de 200 milissegundos, é recomendável que o recurso de hardware seja adquirido de forma assíncrona. A ativação da fonte de mídia personalizada não deve bloquear a aquisição de recursos de hardware. Em vez disso, a operação IMFMediaSource::Start deve ser serializada em relação direta à aquisição de recursos de hardware.
Os outros dois métodos expostos por IMFActivate, DetachObject e ShutdownObject, devem retornar E_NOTIMPL.
A fonte de mídia personalizada pode optar por implementar a interface IMFActivate e IMFAttributes dentro do mesmo objeto COM que o IMFMediaSource. Se isso for feito, é recomendável que IMFMediaSourceEx::GetSourceAttributes retorne a mesma interface IMFAttributes que as do IMFActivate.
Se a Fonte de Mídia Personalizada não implementar o IMFActivate e o IMFAttributes com o mesmo objeto, a Fonte de Mídia Personalizada deverá copiar todos os atributos definidos no repositório de atributos IMFActivate para o repositório de atributos de origem da Fonte de Mídia Personalizada.
Fluxo de câmera codificado
Uma fonte de mídia personalizada pode expor tipos de mídia compactados (fluxos elementares HEVC ou H264) e o pipeline do sistema operacional suporta totalmente a origem e a configuração dos parâmetros de codificação na fonte de mídia personalizada (os parâmetros de codificação são comunicados por meio da ICodecAPI, que é roteada como uma chamada 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
);
A estrutura KSPROPERTY passada para o método IKsControl::KsProperty tem as seguintes informações:
KSPROPERTY.Set = Encoder Property GUID
KSPROPERTY.Id = 0
KSPROPERTY.Flags = (KSPROPERTY_TYPE_SET or KSPROPERTY_TYPE_GET)
Onde Encoder Property GUID é a lista de propriedades disponíveis definidas em Codec API Properties.
A carga útil da propriedade Encoder será passada através do campo pPropertyData do método KsProperty declarado acima.
Requisitos do mecanismo de captura
Enquanto as fontes codificadas são totalmente suportadas pelo Frame Server, o mecanismo de captura do lado do cliente (IMFCaptureEngine), que é usado pelo objeto Windows.Media.Capture.MediaCapture, impõe requisitos extras:
O fluxo deve ser todo codificado (HEVC ou H264) ou todo descompactado (neste contexto, MJPG é tratado como descompactado).
Deve haver pelo menos um fluxo não comprimido disponível.
Observação
Esses requisitos são adicionais aos requisitos de fonte de mídia personalizada descritos neste artigo. No entanto, os requisitos do mecanismo de captura só são impostos quando o aplicativo cliente usa a fonte de mídia personalizada por meio do IMFCaptureEngine ou da API Windows.Media.Capture.MediaCapture.
Perfis de câmera (disponível no Windows 10, versão 1803 e posterior)
O suporte ao perfil da câmera está disponível para fontes de mídia personalizadas. O mecanismo recomendado é publicar o perfil por meio do atributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION fora do atributo source (IMFMediaSourceEx::GetSourceAttributes).
O atributo MF_DEVICEMFT_SENSORPROFILE_COLLECTION é um IUnknown da interface IMFSensorProfileCollection . IMFSensorProfileCollection pode ser obtido usando a função MFCreateSensorProfileCollection :
IFACEMETHODIMP
SimpleMediaSource::GetSourceAttributes(
_COM_Outptr_ IMFAttributes** sourceAttributes
)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == sourceAttributes)
{
return E_POINTER;
}
RETURN_IF_FAILED (_CheckShutdownRequiresLock());
*sourceAttributes = nullptr;
if (_spAttributes.Get() == nullptr)
{
ComPtr<IMFSensorProfileCollection> profileCollection;
ComPtr<IMFSensorProfile> profile;
// Create our source attribute store
RETURN_IF_FAILED (MFCreateAttributes(_spAttributes.GetAddressOf(), 1));
// Create an empty profile collection
RETURN_IF_FAILED (MFCreateSensorProfileCollection(&profileCollection));
// In this example since we have just one stream, we only have one
// pin to add: Pin0
// Legacy profile is mandatory. This is to ensure non-profile
// aware applications can still function, but with degraded
// feature sets.
RETURN_IF_FAILED (MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr,
profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED (profile->AddProfileFilter(0, L"((RES==;FRT<=30,1;SUT==))"));
RETURN_IF_FAILED (profileCollection->AddProfile(profile.Get()));
// High Frame Rate profile will only allow >=60fps
RETURN_IF_FAILED (MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0, nullptr,
profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED (profile->AddProfileFilter(0, L"((RES==;FRT>=60,1;SUT==))"));
RETURN_IF_FAILED (profileCollection->AddProfile(profile.Get()));
// See the profile collection to the attribute store of the IMFTransform
RETURN_IF_FAILED (_spAttributes->SetUnknown(MF_DEVICEMFT_SENSORPROFILE_COLLECTION,
profileCollection.Get()));
}
return _spAttributes.CopyTo(sourceAttributes);
}
Perfil de autenticação facial
Se a Fonte de Mídia Personalizada for projetada para oferecer suporte ao Reconhecimento Facial do Windows Hello, é recomendável publicar um Perfil de Autenticação Facial. Os requisitos de um Perfil de Autenticação Facial são:
O Controlo DDI de Autenticação Facial deve ser suportado por um único fluxo de IR. Para obter mais informações, consulte KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.
O fluxo IR deve ser pelo menos 340 x 340 a 15 fps. O formato deve ser L8, NV12 ou MJPG marcado com compressão L8.
O fluxo RGB deve ser pelo menos 480 x 480 a 7,5 fps (este requisito só é necessário se a autenticação Multispectrum for exigida).
O Perfil de Autenticação Facial deve ter o ID de Perfil: KSCAMERAPROFILE_FaceAuth_Mode,0.
Recomendamos que o Perfil de Autenticação Facial anuncie apenas um tipo de mídia para cada um dos fluxos IR e RGB.
Controles de fluxo de fotos
Se fluxos de fotos independentes forem expostos marcando um dos MF_DEVICESTREAM_STREAM_CATEGORY do fluxo como PINNAME_IMAGE, será necessário um fluxo com categoria PINNAME_VIDEO_CAPTURE (por exemplo, expor apenas o PINNAME_IMAGE como um fluxo único não constitui uma fonte de mídia válida).
Através de IKsControl, o conjunto de propriedades PROPSETID_VIDCAP_VIDEOCONTROL deve ser suportado. Para obter mais informações, consulte Propriedades de controle de vídeo.