IMFActivate::Activateint object does not support无法识别的外部命令?


项目
06/15/2023
本主题提供有关在帧服务器体系结构中实现自定义媒体源的信息。AV 流和自定义媒体源选项在决定如何在帧服务器体系结构中提供视频捕获流支持时,有两个main选项:AV 流和自定义媒体源。AV Stream 模型是使用 AV Stream 微型端口驱动程序的标准相机驱动程序模型, (内核模式驱动程序) 。 通常,AV 流驱动程序分为两个main类别:基于 MIPI 的驱动程序和 USB 视频类驱动程序。对于“自定义媒体源”选项,驱动程序模型可能完全自定义 (专有) ,也可以基于非传统相机源 (,例如文件或网络源) 。AV 流驱动程序AV 流驱动程序方法main优点是 PnP 和电源管理/设备管理已由 AV 流框架处理。但是,这也意味着基础源必须是具有内核模式驱动程序的物理设备才能与硬件进行交互。 对于 UVC 设备,收件箱中提供了 Windows UVC 1.5 类驱动程序,因此设备只需实现其固件即可。对于基于 MIPI 的设备,供应商需要实现自己的 AV Stream 微型端口驱动程序。自定义媒体源对于设备驱动程序已 (但不是 AV Stream 微型端口驱动程序) 或使用非传统相机捕获的源,AV 流驱动程序可能不可行。 例如,通过网络连接的 IP 相机不适合 AV 流驱动程序模型。在这种情况下,使用帧服务器模型的自定义媒体源是替代方法。功能
自定义媒体源
AV 流驱动程序
PnP 和电源管理
必须由源和/或存根驱动程序实现
由 AV 流框架提供
用户模式插件
不可用。 自定义媒体源包含特定于 OEM/IHV 的用户模式逻辑。
用于旧版实现的 DMFT、平台 DMFT 和 MFT0
传感器组
支持
支持
相机配置文件 V2
支持
支持
相机配置文件 V1
不支持
支持
自定义媒体源要求通过引入 Windows 相机 Frame Server (称为 Frame Server) 服务,这可以通过自定义媒体源来实现。 这需要两个main组件:第一个要求用于两个目的:安全性帧服务器的自定义媒体源在安全性方面与通用自定义媒体源的区别如下:鉴于这些约束,帧服务器自定义媒体源不得尝试访问文件系统或注册表的受保护部分。 通常,允许读取访问,但不允许写入访问。性能作为帧服务器模型的一部分,框架服务器将如何实例化自定义媒体源有两种情况:在传感器组生成/发布期间。
在“相机”激活期间
传感器组生成通常在设备安装和/或电源周期期间完成。 鉴于此,我们强烈建议自定义媒体源在创建期间避免任何重大处理,并将任何此类活动推迟到 IMFMediaSource::Start 函数。 传感器组生成不会尝试启动自定义媒体源,只需查询各种可用的流/媒体类型和源/流属性信息。存根驱动程序驱动程序包和存根驱动程序有两个最低要求。可以使用 WDF (UMDF 或 KMDF) 或 WDM 驱动程序模型编写存根驱动程序。驱动程序要求包括:在“ KSCATEGORY_VIDEO_CAMERA”类别 下 (自定义媒体源) 设备接口注册“相机”,以便可以枚举它。
在设备接口节点下添加注册表项, (使用驱动程序 INF DDInstall.Interface 部分中的 AddReg 指令) ,该指令声明自定义媒体源 COM 对象的 CoCreate-able CLSID。 必须使用以下注册表值名称添加此名称: CustomCaptureSourceClsid。
这允许应用程序发现“相机”源,并在激活时通知帧服务器服务截获激活调用并将其重新路由到 CoCreated 自定义媒体源。示例 INF以下示例演示自定义媒体源存根驱动程序的典型 INF:;/*++
;
;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
上述自定义媒体源在 KSCATEGORY_VIDEO、 KSCATEGORY_CAPTURE和 KSCATEGORY_VIDEO_CAMERA 下注册,以确保搜索标准 RGB 相机的任何 UWP 和非 UWP 应用都能够发现“相机”。如果自定义媒体源还公开了非 RGB 流 (IR、Depth 等) 则还可以选择在 KSCATEGORY_SENSOR_CAMERA下注册。注意大多数基于 USB 的网络摄像头都会公开 YUY2 和 MJPG 格式。 由于此行为,许多旧版 DirectShow 应用程序编写时假设 YUY2/MJPG 可用。 为了确保与此类应用程序的兼容性,建议在需要旧版应用兼容性的情况下,从自定义媒体源提供 YUY2 媒体类型。存根驱动程序实现除了 INF,驱动程序存根还必须注册并启用相机设备接口。 这通常在 DRIVER_ADD_DEVICE 操作期间完成。请参阅基于 WDM 的驱动程序 的DRIVER_ADD_DEVICE 回调函数和 UMDF/KMDF 驱动程序的 WdfDriverCreate 函数。下面是处理此操作的 UMDF 驱动程序存根的代码截图: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;
}
PnP 操作与任何其他物理相机一样,建议存根驱动程序在删除/附加基础源时至少管理启用和禁用设备的 PnP 操作。 例如,如果自定义媒体源使用网络源 ((例如 IP 摄像机) ),则可能需要在网络源不再可用时触发设备删除。这可确保应用程序通过 PnP API 侦听设备添加/删除获取正确的通知。 并确保无法枚举不再可用的源。有关 UMDF 和 KMDF 驱动程序,请参阅 WdfDeviceSetDeviceState 函数文档。有关 WMD 驱动程序,请参阅 IoSetDeviceInterfaceState 函数文档。自定义媒体源 DLL自定义媒体源是标准过程 COM 服务器,必须实现以下接口:IMFMediaEventGenerator
IMFMediaSource
IMFMediaSourceEx
IKsControl
IMFGetService
注意IMFMediaSourceEx 继承自 IMFMediaSource , IMFMediaSource 继承自 IMFMediaEventGenerator。自定义媒体源中的每个受支持的流都必须支持以下接口:注意IMFMediaStream2 继承自 IMFMediaStream , 而 IMFMediaStream 继承自 IMFMediaEventGenerator。请参阅 编写自定义媒体源 文档,了解如何创建自定义媒体源。 本部分的其余部分将介绍在框架服务器框架中支持自定义媒体源所需的差异。IMFGetServiceIMFGetService 是帧服务器自定义媒体源的必需接口。 如果自定义媒体源不需要公开任何其他服务接口,IMFGetService 可能会返回MF_E_UNSUPPORTED_SERVICE。以下示例演示没有支持服务接口的 IMFGetService 实现:_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如上所示,源和源中的单个流都必须支持其自己的 IMFMediaEventGenerator 接口。 通过发送特定的 IMFMediaEvent,通过事件生成器管理来自源的整个 MF 管道数据和控制流。若要实现 IMFMediaEventGenerator,自定义媒体源必须使用 MFCreateEventQueue API 创建 IMFMediaEventQueue ,并将 IMFMediaEventGenerator 的所有方法路由到队列对象:IMFMediaEventGenerator 具有以下方法:// 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);
以下代码显示了 IMFMediaEventGenerator 接口的建议实现。 自定义媒体源实现将公开 IMFMediaEventGenerator 接口,该接口的方法将请求路由到媒体源创建/初始化期间创建的 IMFMediaEventQueue 对象。在下面的代码中,_spEventQueue对象是使用 MFCreateEventQueue 函数创建的 IMFMediaEventQueue:// 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;
}
正在查找和暂停框架服务器框架支持的自定义媒体源不支持 Seek 或 Pause 操作。 自定义媒体源不需要为这些操作提供支持,并且不得发布 MFSourceSeeked 或 MEStreamSeeked 事件。IMFMediaSource::P ause 应返回 MF_E_INVALID_STATE_TRANSITION (或 MF_E_SHUTDOWN (如果源已关闭) )。IKsControlIKsControl 是所有相机相关控件的标准控件接口。 如果自定义媒体源实现任何相机控件, 则 IKsControl 接口是管道路由控件 I/O 的方式。有关详细信息,请参阅以下控件集文档主题:PROPSETID_VIDCAP_CAMERACONTROL
PROPSETID_VIDCAP_VIDEOPROCAMP
KSPROPERTYSETID_ExtendedCameraControl
控件是可选的,如果不支持,建议返回的错误代码为 HRESULT_FROM_WIN32 (ERROR_SET_NOT_FOUND) 。以下代码是没有受支持的控件的示例 IKsControl 实现:// 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如 编写自定义媒体源中所述, 在 IMFMediaSource ::Start 方法完成期间,通过发布到源事件队列的 MENewStream 媒体事件,向自定义媒体源中的帧工作提供 IMFMediaStream2 接口: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;
}
必须针对通过 IMFPresentationDescriptor 选择的每个流执行此操作。对于具有视频流的自定义媒体源,不应发送 MEEndOfStream 和 MEEndOfPresentation 事件。流属性必须将所有自定义媒体源流设置为PINNAME_VIDEO_CAPTURE MF_DEVICESTREAM_STREAM_CATEGORY。 自定义 媒体源不支持PINNAME_VIDEO_PREVIEW。注意PINNAME_IMAGE虽然受支持,但不建议这样做。 使用 PINNAME_IMAGE 公开流需要自定义媒体源支持所有照片触发器控件。 有关更多详细信息,请参阅下面的 照片流控件 部分。MF_DEVICESTREAM_STREAM_ID 是所有流的必需属性。 它应该是从 0 开始的索引。 因此,第一个流的 ID 为 0,第二个流的 ID 为 1,依此。下面是流上建议的属性列表:MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES
MF_DEVICESTREAM_FRAMESERVER_SHARED
MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPESMF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES 是 UINT32 属性,它是流类型的位掩码值。 可以将其设置为以下任一 (虽然这些类型是位掩码标志,但如果可能) ,则建议不要混合源类型:类型
标志
描述
MFFrameSourceTypes_Color
0x0001
标准 RGB 颜色流
MFFrameSourceTypes_Infrared
0x0002
IR 流
MFFrameSourceTypes_Depth
0x0004
深度流
MFFrameSourceTypes_Image
0x0008
图像流 (非视频子类型,通常为 JPEG)
MFFrameSourceTypes_Custom
0x0080
自定义流类型
MF_DEVICESTREAM_FRAMESERVER_SHAREDMF_DEVICESTREAM_FRAMESERVER_SHARED 是 UINT32 属性,可设置为 0 或 1。 如果设置为 1,则会将流标记为帧服务器“可共享”。 这样,即使其他应用使用,应用程序也能在共享模式下打开流。如果未设置此属性,则如果自定义媒体源只有一个流,则帧服务器将允许共享第一个未标记的流 (该流将被标记为共享) 。如果此属性设置为 0,则帧服务器将阻止共享应用中的流。 如果自定义媒体源将此属性设置为 0 的所有流标记,则任何共享应用程序都无法初始化源。示例分配所有媒体帧都必须作为 IMFSample 生成。 自定义媒体源必须使用 MFCreateSample 函数来分配 IMFSample 的实例,并使用 AddBuffer 方法添加媒体缓冲区。每个 IMFSample 都必须设置采样时间和采样持续时间。 所有示例时间戳都必须基于 QPC 时间 (QueryPerformanceCounter) 。建议自定义媒体源尽可能使用 MFGetSystemTime 函数。 此函数是 QueryPerformanceCounter 的包装器,可将 QPC 时钟周期转换为 100 纳秒单位。自定义媒体源可能使用内部时钟,但所有时间戳都必须关联到基于当前 QPC 的 100 纳秒单位。媒体缓冲区添加到 IMFSample 的所有媒体缓冲区都必须使用标准 MF 缓冲区分配函数。 自定义媒体源不得实现自己的 IMFMediaBuffer 接口或尝试直接 (分配媒体缓冲区,例如 new/malloc/VirtualAlloc 等,不得用于帧数据) 。使用以下任一 API 分配媒体帧:MFCreateMemoryBuffer
MFCreateAlignedMemoryBuffer
MFCreate2DMediaBuffer
MFCreateDXGISurfaceBuffer
MFCreateMemoryBuffer 和 MFCreateAlignedMemoryBuffer 应用于非步幅对齐媒体数据。 这些子类型通常是自定义子类型或压缩子类型 (,例如 H264/HEVC/MJPG) 。对于使用系统内存) 的已知未压缩媒体类型 ((例如 YUY2、NV12 等),建议使用 MFCreate2DMediaBuffer。若要将 DX 表面 (用于 GPU 加速操作(例如呈现和/或编码) ),应使用 MFCreateDXGISurfaceBuffer 。MFCreateDXGISurfaceBuffer 不会创建 DX 表面。 使用通过 IMFMediaSourceEx::SetD3DManager 方法传递到媒体源的 DXGI 管理器创建图面。IMFDXGIDeviceManager::OpenDeviceHandle 将提供与所选 D3D 设备关联的句柄。 然后,可以使用 IMFDXGIDeviceManager::GetVideoService 方法获取 ID3D11Device 接口。无论使用哪种类型的缓冲区,都必须通过媒体流的 IMFMediaEventGenerator 上的 MEMediaSample 事件将创建的 IMFSample 提供给管道。虽然可以对自定义媒体源和 IMFMediaStream 的基础集合使用相同的 IMFMediaEventQueue,但应注意,这样做将导致媒体源事件和流事件 (序列化,其中包括媒体流) 。 对于具有多个流的源,这是不可取的。以下代码截图显示了媒体流的示例实现: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;
}
用于公开 IMFActivate (的自定义媒体源扩展,Windows 10 版本 1809) 除了上述自定义媒体源必须支持的接口列表外,帧服务器体系结构中自定义媒体源操作施加的限制之一是,只有一个 UMDF 驱动程序实例可以通过管道“激活”。例如,如果你有一个物理设备,该设备除了安装 UMDF 存根驱动程序的非 AV 流驱动程序包外,还安装了一个 UMDF 存根驱动程序,并且你将其中多个物理设备附加到一台计算机,而 UMDF 驱动程序的每个实例都将获得唯一的符号链接名称,则自定义媒体源的激活路径将无法传达与自定义媒体源关联的符号链接名称,创建时间。自定义媒体源可以在自定义媒体源的属性存储中查找标准 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK 属性, (调用 IMFMediaSource::Start 时通过 IMFMediaSourceEx::GetSourceAttributes 方法) 从自定义媒体 源返回的属性存储 。但是,这可能会导致更高的启动延迟,因为这会将 HW 资源获取延迟推迟到开始时间,而不是创建/初始化时间。因此,在 Windows 10 版本 1809 中,自定义媒体源可以选择公开 IMFActivate 接口。IMFActivate如果自定义媒体源的 COM 服务器支持 IMFActivate 接口,则设备初始化信息将通过 IMFActivate 继承的 IMFAttributes 提供给 COM 服务器。 因此,当调用 IMFActivate::ActivateObject 时, IMFActivate 的属性存储将包含 UMDF 存根驱动程序的符号链接名称,以及管道/应用程序在源创建/初始化时提供的任何其他配置设置。自定义媒体源应使用此方法调用来获取所需的任何硬件资源。注意如果获取硬件资源的时间超过 200 毫秒,建议异步获取硬件资源。 自定义媒体源的激活不应阻止硬件资源获取。 相反, IMFMediaSource::Start 操作应针对硬件资源获取进行序列化。IMFActivate 公开的两个附加方法 DetachObject 和 ShutdownObject 必须返回E_NOTIMPL。自定义媒体源可以选择在与 IMFMediaSource 相同的 COM 对象中实现 IMFActivate 和 IMFAttributes 接口。 如果完成此操作,建议 IMFMediaSourceEx::GetSourceAttributes 返回与来自 IMFActivate 的接口相同的 IMFAttributes 接口。如果自定义媒体源未使用同一对象实现 IMFActivate 和 IMFAttributes ,则自定义媒体源必须将 在 IMFActivate 属性存储上设置的所有属性复制到自定义媒体源的源属性存储中。编码的相机流自定义媒体源可能会 (HEVC 或 H264 基本流) 公开压缩媒体类型,并且 OS 管道完全支持自定义媒体源上的编码参数的源和配置, (编码参数通过 ICodecAPI 进行通信,后者作为 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
);
传递到 IKsControl::KsProperty 方法的 KSPROPERTY 结构将包含以下信息:KSPROPERTY.Set = Encoder Property GUID
KSPROPERTY.Id = 0
KSPROPERTY.Flags = (KSPROPERTY_TYPE_SET or KSPROPERTY_TYPE_GET)
其中,编码器属性 GUID 是在 编解码器 API 属性中定义的可用属性的列表。编码器属性的有效负载将通过上面声明的 KsProperty 方法的 pPropertyData 字段传入。捕获引擎要求虽然帧服务器完全支持编码源,但 Windows.Media.Capture.MediaCapture 对象使用的客户端捕获引擎 (IMFCaptureEngine) 施加了其他要求:注意这些要求是本主题中概述的自定义媒体源要求的补充。 但是,仅当客户端应用程序通过 IMFCaptureEngine 或 Windows.Media.Capture.MediaCapture API 使用自定义媒体源时,才会强制执行捕获引擎要求。相机配置文件 (Windows 10版本 1803 及更高版本) 相机配置文件支持适用于自定义媒体源。 建议的机制是通过 MF_DEVICEMFT_SENSORPROFILE_COLLECTION 属性从 IMFMediaSourceEx::GetSourceAttributes) 的源属性 (发布配置文件。MF_DEVICEMFT_SENSORPROFILE_COLLECTION属性是 IMFSensorProfileCollection 接口的 IUnknown。 可以使用 MFCreateSensorProfileCollection 函数获取 IMFSensorProfileCollection: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);
}
人脸身份验证配置文件如果自定义媒体源旨在支持Windows Hello人脸识别,则建议发布人脸身份验证配置文件。 人脸身份验证配置文件的要求如下:必须在单个 IR 流上支持人脸身份验证 DDI 控制。 有关详细信息,请参阅 KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE。
IR 流必须以 15 fps 的速度至少为 340 x 340。 格式必须为 L8、NV12 或标记为 L8 压缩的 MJPG。
RGB 流必须至少为 480 x 480(7.5 fps) (仅在) 强制执行多spectrum 身份验证时才需要。
人脸身份验证配置文件的配置文件 ID 必须为:KSCAMERAPROFILE_FaceAuth_Mode,0。
我们建议人脸身份验证配置文件仅针对每个 IR 和 RGB 流播发一种媒体类型。照片流控件如果通过将流中的一个MF_DEVICESTREAM_STREAM_CATEGORY 标记为 PINNAME_IMAGE来公开独立照片流,则需要 (流类别为 PINNAME_VIDEO_CAPTURE 的流,例如,仅公开 PINNAME_IMAGE 的单个流不是有效的媒体源) 。通过 IKsControl,必须支持 PROPSETID_VIDCAP_VIDEOCONTROL 属性集。 有关详细信息,请参阅 视频控件属性。}

我要回帖

更多关于 int object does not support 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信