다음을 통해 공유


프레임 서버 사용자 지정 미디어 원본

이 항목에서는 프레임 서버 아키텍처 내에서 사용자 지정 미디어 원본의 구현에 대한 정보를 제공합니다.

AV 스트림 및 사용자 지정 미디어 원본 옵션

프레임 서버 아키텍처 내에서 비디오 캡처 스트림 지원을 제공하는 방법을 결정할 때 AV Stream 및 사용자 지정 미디어 원본이라는 두 가지 기본 옵션이 있습니다.

AV Stream 모델은 AV Stream 미니포트 드라이버(커널 모드 드라이버)를 사용하는 표준 카메라 드라이버 모델입니다. 일반적으로 AV Stream 드라이버는 MIPI 기반 드라이버와 USB 비디오 클래스 드라이버의 두 가지 기본 범주로 분류됩니다.

사용자 지정 미디어 원본 옵션의 경우 드라이버 모델은 완전히 사용자 지정(독점)일 수도 있으며, 기존이 아닌 카메라 원본(예: 파일 또는 네트워크 원본)을 기반으로 할 수도 있습니다.

AV 스트림 드라이버

AV Stream Driver 접근 방식의 기본 이점은 PnP 및 전원 관리/장치 관리 AV Stream 프레임워크에서 이미 처리된다는 것입니다.

그러나 기본 원본은 하드웨어와 인터페이스하기 위해 커널 모드 드라이버가 있는 물리적 디바이스여야 한다는 의미이기도 합니다. UVC 디바이스의 경우 Windows UVC 1.5 클래스 드라이버가 받은 편지함으로 제공되므로 디바이스는 펌웨어를 구현하기만 하면 됩니다.

MIPI 기반 디바이스의 경우 공급업체는 자체 AV Stream 미니포트 드라이버를 구현해야 합니다.

사용자 지정 미디어 원본

디바이스 드라이버를 이미 사용할 수 있는 원본(AV Stream 미니포트 드라이버 아님) 또는 기존이 아닌 카메라 캡처를 사용하는 원본의 경우 AV Stream Driver가 실행 가능하지 않을 수 있습니다. 예를 들어 네트워크를 통해 연결된 IP 카메라는 AV 스트림 드라이버 모델에 맞지 않습니다.

이러한 상황에서는 프레임 서버 모델을 사용하는 사용자 지정 미디어 원본이 대안이 될 수 있습니다.

기능 사용자 지정 미디어 원본 AV 스트림 드라이버
PnP 및 전원 관리 원본 및/또는 스텁 드라이버에서 구현해야 합니다. AV Stream 프레임워크에서 제공
사용자 모드 플러그 인 사용할 수 없습니다. 사용자 지정 미디어 원본은 OEM/IHV 특정 사용자 모드 논리를 통합합니다. 레거시 구현을 위한 DMFT, 플랫폼 DMFT 및 MFT0
센서 그룹 지원됨 지원 여부
카메라 프로필 V2 지원됨 지원됨
카메라 프로필 V1 지원되지 않음 지원됨

사용자 지정 미디어 원본 요구 사항

Windows 카메라 프레임 서버(프레임 서버라고 함) 서비스가 도입되면서 사용자 지정 미디어 원본을 통해 이 작업을 수행할 수 있습니다. 이렇게 하려면 두 가지 기본 구성 요소가 필요합니다.

  • 카메라 디바이스 인터페이스를 등록/사용하도록 설계된 스텁 드라이버가 있는 드라이버 패키지입니다.

  • 사용자 지정 미디어 원본을 호스트하는 COM DLL입니다.

첫 번째 요구 사항은 다음 두 가지 용도로 필요합니다.

  • 사용자 지정 미디어 원본이 신뢰할 수 있는 프로세스를 통해 설치되었는지 확인하는 검사 프로세스입니다(드라이버 패키지에는 WHQL 인증이 필요).

  • 표준 PnP 열거형 및 "카메라"의 검색을 지원합니다.

보안

프레임 서버용 사용자 지정 미디어 원본은 다음과 같은 방식으로 보안 측면에서 일반 사용자 지정 미디어 원본과 다릅니다.

  • 프레임 서버 사용자 지정 미디어 원본은 로컬 서비스로 실행됩니다(로컬 시스템과 혼동하지 않음; 로컬 서비스는 Windows 컴퓨터에서 매우 낮은 권한의 계정입니다.)

  • 프레임 서버 사용자 지정 미디어 원본은 세션 0(시스템 서비스 세션)에서 실행되며 사용자 데스크톱과 상호 작용할 수 없습니다.

이러한 제약 조건을 감안할 때 프레임 서버 사용자 지정 미디어 원본은 파일 시스템이나 레지스트리의 보호된 부분에 액세스하려고 시도해서는 안 됩니다. 일반적으로 읽기 액세스는 허용되지만 쓰기 액세스는 허용되지 않습니다.

성능

프레임 서버 모델의 일부로, 프레임 서버에서 사용자 지정 미디어 원본을 인스턴스화하는 방법에는 두 가지가 있습니다.

  • 센서 그룹 생성/게시 중.

  • "카메라" 활성화 중

센서 그룹 생성은 일반적으로 디바이스 설치 및/또는 전원 주기 중에 수행됩니다. 이를 감안할 때 사용자 지정 미디어 원본은 만드는 동안 중요한 처리를 피하고 IMFMediaSource::Start 함수에 대한 이러한 작업을 연기할 것을 강력히 권장합니다. 센서 그룹 생성은 사용자 지정 미디어 원본을 시작하려고 시도하지 않고 사용 가능한 다양한 스트림/미디어 유형 및 원본/스트림 특성 정보를 쿼리하기만 하면 됩니다.

스텁 드라이버

드라이버 패키지와 스텁 드라이버에는 두 가지 최소 요구 사항이 있습니다.

스텁 드라이버는 WDF(UMDF 또는 KMDF) 또는 WDM 드라이버 모델을 사용하여 작성할 수 있습니다.

드라이버 요구 사항은 다음과 같습니다.

  • 열거할 수 있도록 KSCATEGORY_VIDEO_CAMERA 범주 아래에 "카메라"(사용자 지정 미디어 원본) 디바이스 인터페이스를 등록합니다.

참고

레거시 DirectShow 애플리케이션에서 열거를 허용하려면 드라이버가 KSCATEGORY_VIDEO 및 KSCATEGORY_CAPTURE 등록해야 합니다.

  • 사용자 지정 미디어 원본 COM 개체의 CoCreate 가능 CLSID를 선언하는 디바이스 인터페이스 노드 아래에 레지스트리 항목을 추가합니다(드라이버 INF DDInstall.Interface 섹션에서 AddReg 지시문 사용). CustomCaptureSourceClsid 레지스트리 값 이름을 사용하여 추가해야 합니다.

이렇게 하면 애플리케이션에서 "카메라" 원본을 검색할 수 있으며 활성화되면 Frame Server 서비스에 활성화 호출을 가로채서 CoCreated Custom Media Source로 다시 라우팅하도록 알립니다.

샘플 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

위의 사용자 지정 미디어 원본은 표준 RGB 카메라를 검색하는 UWP 및 비 UWP 앱에서 "카메라"를 검색할 수 있도록 KSCATEGORY_VIDEO ,KSCATEGORY_CAPTURE 및 KSCATEGORY_VIDEO_CAMERA 등록합니다.

사용자 지정 미디어 원본이 RGB가 아닌 스트림(IR, 깊이 등)도 노출하는 경우 필요에 따라 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

사용자 지정 미디어 원본은 다음 인터페이스를 구현해야 하는 표준 inproc COM 서버입니다.

참고

IMFMediaSourceExIMFMediaSource 에서 상속되고 IMFMediaSourceIMFMediaEventGenerator에서 상속됩니다.

사용자 지정 미디어 원본 내에서 지원되는 각 스트림은 다음 인터페이스를 지원해야 합니다.

참고

IMFMediaStream2IMFMediaStream 에서 상속되고 IMFMediaStreamIMFMediaEventGenerator에서 상속됩니다.

사용자 지정 미디어 원본을 만드는 방법은 사용자 지정 미디어 원본 작성 설명서를 참조하세요. 이 섹션의 나머지 부분에서는 프레임 서버 프레임워크 내에서 사용자 지정 미디어 원본을 지원하는 데 필요한 차이점을 설명합니다.

IMFGetService

IMFGetService 는 프레임 서버 사용자 지정 미디어 원본에 대한 필수 인터페이스입니다. 사용자 지정 미디어 원본이 추가 서비스 인터페이스를 노출할 필요가 없는 경우 IMFGetServiceMF_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 인터페이스를 지원해야 합니다. 원본의 전체 MF 파이프라인 데이터 및 제어 흐름은 특정 IMFMediaEvent를 전송하여 이벤트 생성기를 통해 관리됩니다.

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

검색 및 일시 중지

프레임 서버 프레임워크를 통해 지원되는 사용자 지정 미디어 원본은 검색 또는 일시 중지 작업을 지원하지 않습니다. 사용자 지정 미디어 원본은 이러한 작업에 대한 지원을 제공할 필요가 없으며 MFSourceSeeked 또는 MEStreamSeeked 이벤트를 게시해서는 안 됩니다.

IMFMediaSource::P auseMF_E_INVALID_STATE_TRANSITION 반환해야 합니다(또는 원본이 이미 종료된 경우 MF_E_SHUTDOWN ).

IKsControl

IKsControl 은 모든 카메라 관련 컨트롤에 대한 표준 컨트롤 인터페이스입니다. 사용자 지정 미디어 원본이 카메라 컨트롤을 구현하는 경우 IKsControl 인터페이스는 파이프라인이 컨트롤 I/O를 라우팅하는 방법입니다.

자세한 내용은 다음 컨트롤 집합 설명서 topics 참조하세요.

컨트롤은 선택 사항이며 지원되지 않는 경우 반환할 권장 오류 코드는 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

사용자 지정 미디어 원본 작성에 설명된 대로 IMFMediaStream2 인터페이스는 IMFMediaSource::Start 메서드를 완료하는 동안 원본 이벤트 큐에 게시된 MENewStream 미디어 이벤트를 통해 사용자 지정 미디어 원본의 프레임 작업에 제공됩니다.

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를 통해 선택한 각 스트림에 대해 이 작업을 수행해야 합니다.

비디오 스트림이 있는 사용자 지정 미디어 원본의 경우 MEEndOfStreamMEEndOfPresentation 이벤트를 보내지 않아야 합니다.

스트림 특성

모든 사용자 지정 미디어 원본 스트림에는 MF_DEVICESTREAM_STREAM_CATEGORY PINNAME_VIDEO_CAPTURE 설정해야 합니다. PINNAME_VIDEO_PREVIEW 사용자 지정 미디어 원본에 대해 지원되지 않습니다.

참고

지원되는 동안에는 PINNAME_IMAGE 권장되지 않습니다. PINNAME_IMAGE 사용하여 스트림을 노출하려면 사용자 지정 미디어 원본이 모든 사진 트리거 컨트롤을 지원해야 합니다. 자세한 내용은 아래 의 사진 스트림 컨트롤 섹션을 참조하세요.

MF_DEVICESTREAM_STREAM_ID 모든 스트림에 대한 필수 특성입니다. 0 기반 인덱스여야 합니다. 따라서 첫 번째 스트림의 ID는 0이고, 두 번째 스트림의 ID는 1입니다.

다음은 스트림에서 권장되는 특성 목록입니다.

MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES

MF_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_SHARED

MF_DEVICESTREAM_FRAMESERVER_SHARED 0 또는 1로 설정할 수 있는 UINT32 특성입니다. 1로 설정하면 프레임 서버에서 스트림을 "공유할 수 있는" 것으로 표시합니다. 이렇게 하면 다른 앱에서 사용하는 경우에도 애플리케이션이 공유 모드로 스트림을 열 수 있습니다.

이 특성을 설정하지 않으면 프레임 서버에서 표시되지 않은 첫 번째 스트림을 공유할 수 있습니다(사용자 지정 미디어 원본에 스트림이 하나만 있는 경우 해당 스트림은 공유로 표시됨).

이 특성이 0으로 설정된 경우 프레임 서버는 공유 앱에서 스트림을 차단합니다. 사용자 지정 미디어 원본이 이 특성이 0으로 설정된 모든 스트림을 표시하는 경우 공유 애플리케이션이 원본을 초기화할 수 없습니다.

샘플 할당

모든 미디어 프레임은 IMFSample로 생성되어야 합니다. 사용자 지정 미디어 원본은 MFCreateSample 함수를 사용하여 IMFSample의 instance 할당하고 AddBuffer 메서드를 사용하여 미디어 버퍼를 추가해야 합니다.

IMFSample 에는 샘플 시간 및 샘플 기간이 설정되어 있어야 합니다. 모든 샘플 타임스탬프는 QPC 시간(QueryPerformanceCounter)을 기반으로 해야 합니다.

사용자 지정 미디어 원본은 가능한 경우 MFGetSystemTime 함수를 사용하는 것이 좋습니다. 이 함수는 QueryPerformanceCounter 에 대한 래퍼이며 QPC 틱을 100나노초 단위로 변환합니다.

사용자 지정 미디어 원본은 내부 클록을 사용할 수 있지만 모든 타임스탬프는 현재 QPC에 따라 100나노초 단위와 상관 관계가 있어야 합니다.

미디어 버퍼

IMFSample에 추가된 모든 미디어 버퍼는 표준 MF 버퍼 할당 함수를 사용해야 합니다. 사용자 지정 미디어 원본은 자체 IMFMediaBuffer 인터페이스를 구현하거나 미디어 버퍼를 직접 할당하려고 시도해서는 안 됩니다(예: new/malloc/VirtualAlloc 등은 프레임 데이터에 사용하면 안 됩니다).

다음 API를 사용하여 미디어 프레임을 할당합니다.

MFCreateMemoryBufferMFCreateAlignedMemoryBuffer 는 비보폭 맞춤 미디어 데이터에 사용해야 합니다. 일반적으로 사용자 지정 하위 형식 또는 압축된 하위 형식(예: H264/HEVC/MJPG)입니다.

시스템 메모리를 사용하는 알려진 압축되지 않은 미디어 유형(예: YUY2, NV12 등)의 경우 MFCreate2DMediaBuffer를 사용하는 것이 좋습니다.

DX 표면(렌더링 및/또는 인코딩과 같은 GPU 가속 작업의 경우)을 사용하려면 MFCreateDXGISurfaceBuffer 를 사용해야 합니다.

MFCreateDXGISurfaceBuffer 는 DX 표면을 만들지 않습니다. 표면은 IMFMediaSourceEx::SetD3DManager 메서드를 통해 미디어 원본에 전달된 DXGI 관리자를 사용하여 만들어집니다.

IMFDXGIDeviceManager::OpenDeviceHandle은 선택한 D3D 디바이스와 연결된 핸들을 제공합니다. 그런 다음, IMFDXGIDeviceManager::GetVideoService 메서드를 사용하여 ID3D11Device 인터페이스를 가져올 수 있습니다.

사용되는 버퍼 유형에 관계없이 생성된 IMFSample은 미디어 스트림의 IMFMediaEventGenerator에 있는 MEMediaSample 이벤트를 통해 파이프라인에 제공되어야 합니다.

사용자 지정 미디어 원본과 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 드라이버의 instance 하나만 있을 수 있다는 것입니다.

예를 들어 비 AV Stream 드라이버 패키지 외에도 UMDF 스텁 드라이버를 설치하는 물리적 디바이스가 있고 이러한 물리적 디바이스 중 하나 이상을 컴퓨터에 연결하는 경우 UMDF 드라이버의 각 instance 고유한 기호 링크 이름을 가져오는 반면 사용자 지정 미디어 원본의 활성화 경로에는 사용자 지정 미디어 원본과 연결된 기호 링크 이름을 전달할 수 있는 수단이 없습니다. 생성 시간입니다.

사용자 지정 미디어 원본은 IMFMediaSource::Start가 호출될 때 사용자 지정 미디어 원본의 특성 저장소(IMFMediaSourceEx::GetSourceAttributes 메서드를 통해 사용자 지정 미디어 원본에서 반환된 특성 저장소)에서 표준 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK 특성을 찾을 수 있습니다.

그러나 이로 인해 생성/초기화 시간이 아닌 HW 리소스 획득에서 시작 시간이 지연되므로 시작 대기 시간이 더 짧아질 수 있습니다.

이 때문에 Windows 10, 버전 1809 사용자 지정 미디어 원본은 필요에 따라 IMFActivate 인터페이스를 노출할 수 있습니다.

참고

IMFActivateIMFAttributes에서 상속됩니다.

IMFActivate

사용자 지정 미디어 원본의 COM 서버가 IMFActivate 인터페이스를 지원하는 경우 디바이스 초기화 정보는 IMFActivate에서 상속된 IMFAttributes를 통해 COM 서버에 제공됩니다. 따라서 IMFActivate::ActivateObject 가 호출되면 IMFActivate 의 특성 저장소에는 UMDF 스텁 드라이버의 기호 링크 이름과 원본 생성/초기화 시 파이프라인/애플리케이션에서 제공하는 추가 구성 설정이 포함됩니다.

사용자 지정 미디어 원본은 이 메서드 호출을 사용하여 필요한 하드웨어 리소스를 획득해야 합니다.

참고

하드웨어 리소스 획득이 200밀리초보다 큰 경우 하드웨어 리소스를 비동기적으로 획득하는 것이 좋습니다. 사용자 지정 미디어 원본의 활성화는 하드웨어 리소스 획득을 차단해서는 안 됩니다. 대신 IMFMediaSource::Start 작업은 하드웨어 리소스 획득에 대해 직렬화되어야 합니다.

IMFActivate, DetachObjectShutdownObject에서 노출하는 두 가지 추가 메서드는 E_NOTIMPL 반환해야 합니다.

사용자 지정 미디어 원본은 IMFMediaSource와 동일한 COM 개체 내에서 IMFActivateIMFAttributes 인터페이스를 구현하도록 선택할 수 있습니다. 이 작업을 수행하면 IMFMediaSourceEx::GetSourceAttributes에서 IMFActivate와 동일한 IMFAttributes 인터페이스를 반환하는 것이 좋습니다.

사용자 지정 미디어 원본이 동일한 개체를 사용하여 IMFActivateIMFAttributes를 구현하지 않는 경우 사용자 지정 미디어 원본은 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)

여기서 Encoder 속성 GUID는 코덱 API 속성에 정의된 사용 가능한 속성 목록입니다.

인코더 속성의 페이로드는 위에 선언된 KsProperty 메서드의 pPropertyData 필드를 통해 전달됩니다.

캡처 엔진 요구 사항

인코딩된 원본은 프레임 서버에서 완전히 지원되지만 Windows.Media.Capture.MediaCapture 개체에서 사용되는 클라이언트 쪽 캡처 엔진(IMFCaptureEngine)은 다음과 같은 추가 요구 사항을 적용합니다.

  • 스트림은 모두 인코딩(HEVC 또는 H264) 또는 압축되지 않은 모든 항목이어야 합니다(이 컨텍스트에서 MJPG는 압축되지 않은 것으로 처리됨).

  • 하나 이상의 압축되지 않은 스트림을 사용할 수 있어야 합니다.

참고

이러한 요구 사항은 이 항목에 설명된 사용자 지정 미디어 원본 요구 사항에 추가됩니다. 그러나 캡처 엔진 요구 사항은 클라이언트 애플리케이션이 IMFCaptureEngine 또는 Windows.Media.Capture.MediaCapture API를 통해 사용자 지정 미디어 원본을 사용하는 경우에만 적용됩니다.

카메라 프로필(Windows 10 버전 1803 이상에서 사용 가능)

카메라 프로필 지원은 사용자 지정 미디어 원본에 사용할 수 있습니다. 권장되는 메커니즘은 원본 특성(IMFMediaSourceEx::GetSourceAttributes)에서 MF_DEVICEMFT_SENSORPROFILE_COLLECTION 특성을 통해 프로필을 게시하는 것입니다.

MF_DEVICEMFT_SENSORPROFILE_COLLECTION 특성은 IMFSensorProfileCollection 인터페이스의 IUnknown입니다. IMFSensorProfileCollectionMFCreateSensorProfileCollection 함수를 사용하여 가져올 수 있습니다.

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 얼굴 인식을 지원하도록 설계된 경우 얼굴 인증 프로필을 게시하는 것이 좋습니다. Face Authentication 프로필의 요구 사항은 다음과 같습니다.

  • Face Authentication DDI 컨트롤은 단일 IR 스트림에서 지원되어야 합니다. 자세한 내용은 KSPROPERTY_CAMERACONTROL_EXTENDED_FACEAUTH_MODE.

  • IR 스트림은 15fps에서 340 x 340 이상이어야 합니다. 형식은 L8, NV12 또는 L8 압축으로 표시된 MJPG여야 합니다.

  • RGB 스트림은 7.5fps에서 480 x 480 이상이어야 합니다(다중 스펙트럼 인증이 적용되는 경우에만 필요).

  • 얼굴 인증 프로필의 프로필 ID는 KSCAMERAPROFILE_FaceAuth_Mode,0이어야 합니다.

얼굴 인증 프로필은 각 IR 및 RGB 스트림에 대해 하나의 미디어 유형만 보급하는 것이 좋습니다.

사진 스트림 컨트롤

스트림의MF_DEVICESTREAM_STREAM_CATEGORY 중 하나를 PINNAME_IMAGE 표시하여 독립적인 사진 스트림을 노출하는 경우 스트림 범주가 PINNAME_VIDEO_CAPTURE 스트림이 필요합니다(예: PINNAME_IMAGE 노출하는 단일 스트림은 유효한 미디어 원본이 아님).

IKsControl을 통해 PROPSETID_VIDCAP_VIDEOCONTROL 속성 집합을 지원해야 합니다. 자세한 내용은 비디오 컨트롤 속성을 참조하세요.