Share via


코덱 장점

Windows 7부터 Media Foundation 코덱에 장점 값을 할당할 수 있습니다. 코덱이 열거되면 장점이 높은 코덱은 장점이 낮은 코덱보다 선호됩니다. 장점 값이 있는 코덱은 할당된 장점이 없는 코덱보다 선호됩니다. 코덱 열거형에 대한 자세한 내용은 MFTEnumEx를 참조하세요.

장점 값은 Microsoft에서 할당합니다. 현재 하드웨어 코덱만 장점 값을 받을 수 있습니다. 코덱 공급업체에는 코덱의 장점 값을 확인하는 데 사용되는 디지털 인증서도 발급됩니다. 인증서를 가져오려면 에 이메일 요청을 wmla@microsoft.com보냅니다. 인증서를 가져오는 프로세스에는 라이선스 서명 및 Microsoft에 정보 파일 집합 제공이 포함됩니다.

코덱 장점은 다음과 같이 작동합니다.

  1. 코덱 공급업체는 다음 중 하나를 구현합니다.
    • AVStream 미니 드라이버. Media Foundation은 AVStream 드라이버에 대한 표준 프록시 MFT를 제공합니다. 이 옵션이 권장 옵션입니다.
    • 하드웨어에 대한 프록시 역할을 하는 MFT(Media Foundation 변환)입니다. 자세한 내용은 하드웨어 MFT를 참조하세요.
  2. 코덱의 장점 값은 빠른 조회를 위해 레지스트리에 저장됩니다.
  3. MFTEnumEx 함수는 코덱을 장점 순서대로 정렬합니다. 장점 값이 있는 코덱은 로컬로 등록된 코덱 뒤에 있는 목록에 나타나지만( MFTRegisterLocal 참조) 다른 코덱보다 앞서 표시됩니다.
  4. MFT를 만들 때 코덱의 장점은 OPM( 출력 보호 관리자 ) API를 사용하여 확인됩니다.
  5. 프록시 MFT의 경우 코덱은 IOPMVideoOutput 인터페이스를 구현합니다. AVStream 드라이버의 경우 코덱은 KSPROPSETID_OPMVideoOutput 속성 집합을 구현합니다.

다음 다이어그램은 두 경우 모두 장점을 확인하는 방법을 보여 줍니다.

두 프로세스를 보여 주는 다이어그램: 하나는 미디어 파운데이션 프록시 mft 및 avstream 드라이버를 통해, 다른 하나는 사용자 지정 프록시 mft를 통해

사용자 지정 프록시 MFT

하드웨어 코덱에 대한 프록시 MFT를 제공하는 경우 다음과 같이 코덱 장점 값을 구현합니다.

  1. MFT에서 IOPMVideoOutput 인터페이스를 구현합니다. 예제 코드는 이 항목의 다음 섹션에 나와 있습니다.

  2. 다음과 같이 레지스트리에 MFT_CODEC_MERIT_Attribute 특성을 추가합니다.

    1. MFCreateAttributes를 호출하여 새 특성 저장소를 만듭니다.
    2. IMFAttributes::SetUINT32를 호출하여 MFT_CODEC_MERIT_Attribute 특성을 설정합니다. 특성 값은 코덱의 할당된 장점입니다.
    3. MFTRegister를 호출하여 MFT를 등록합니다. pAttributes 매개 변수에 특성 저장소를 전달합니다.
  3. 애플리케이션은 MFTEnumEx를 호출합니다. 이 함수는 열거형 조건과 일치하는 각 코덱에 대해 하나씩 IMFActivate 포인터 배열을 반환합니다.

  4. 애플리케이션은 IMFActivate::ActivateObject 를 호출하여 MFT를 만듭니다.

  5. ActivateObject 메서드는 MFGetMFTMerit 함수를 호출하여 인증서 및 장점 값을 확인합니다.

  6. MFGetMFTMerit 함수는 IOPMVideoOutput::GetInformation을 호출하고 OPM_GET_CODEC_INFO 상태 요청을 보냅니다. 이 상태 요청은 코덱의 할당된 장점 값을 반환합니다. 이 값이 레지스트리 값과 일치하지 않으면 ActivateObject 가 실패할 수 있습니다.

다음 코드는 MFT를 등록할 때 레지스트리에 장점 값을 추가하는 방법을 보여줍니다.

// Shows how to register an MFT with a merit value.

HRESULT RegisterMFTWithMerit()
{
    // The following media types would apply to an H.264 decoder, 
    // and are used here as an example.

    MFT_REGISTER_TYPE_INFO aDecoderInputTypes[] = 
    {
        { MFMediaType_Video, MFVideoFormat_H264 },
    };

    MFT_REGISTER_TYPE_INFO aDecoderOutputTypes[] = 
    {
        { MFMediaType_Video, MFVideoFormat_RGB32 }
    };
    
    // Create an attribute store to hold the merit attribute.
    HRESULT hr = S_OK;
    IMFAttributes *pAttributes = NULL;

    hr = MFCreateAttributes(&pAttributes, 1);

    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MFT_CODEC_MERIT_Attribute, 
            DECODER_MERIT   // Use the codec's assigned merit value.
            );
    }

    // Register the decoder for MFTEnum(Ex).
    if (SUCCEEDED(hr))
    {
        hr = MFTRegister(
            CLSID_MPEG1SampleDecoder,                   // CLSID
            MFT_CATEGORY_VIDEO_DECODER,                 // Category
            const_cast<LPWSTR>(SZ_DECODER_NAME),        // Friendly name
            0,                                          // Flags
            ARRAYSIZE(aDecoderInputTypes),              // Number of input types
            aDecoderInputTypes,                         // Input types
            ARRAYSIZE(aDecoderOutputTypes),             // Number of output types
            aDecoderOutputTypes,                        // Output types
            pAttributes                                 // Attributes 
            );
    }

    SafeRelease(&pAttributes);
    return hr;
}

코덱 장점에 대한 IOPMVideoOutput 구현

다음 코드에서는 코덱 장점을 위해 IOPMVideoOutput 을 구현하는 방법을 보여 있습니다. OPM API에 대한 보다 일반적인 설명은 출력 보호 관리자를 참조하세요.

참고

여기에 표시된 코드에는 난독 처리 또는 기타 보안 메커니즘이 없습니다. OPM 핸드셰이크 및 상태 요청의 기본 구현을 표시하기 위한 것입니다.

 

이 예제에서는 g_TestCert 코덱의 인증서 체인을 포함하는 바이트 배열이고 g_PrivateKey 인증서의 프라이빗 키를 포함하는 바이트 배열이라고 가정합니다.

// Byte array that contains the codec's certificate.

const BYTE g_TestCert[] =
{
    // ... (certificate not shown)
// Byte array that contains the private key from the certificate.

const BYTE g_PrivateKey[] = 
{
    // .... (key not shown)

IOPMVideoOutput::StartInitialization 메서드에서 핸드셰이크에 대한 난수를 생성합니다. 호출자에게 이 번호와 인증서를 반환합니다.

STDMETHODIMP CodecMerit::StartInitialization(
    OPM_RANDOM_NUMBER *prnRandomNumber,
    BYTE **ppbCertificate,
    ULONG *pulCertificateLength
    )
{
    HRESULT hr = S_OK;

    DWORD cbCertificate = sizeof(g_TestCert);
    const BYTE *pCertificate = g_TestCert;

    // Generate the random number for the OPM handshake.
    hr = BCryptGenRandom(
        NULL,  
        (PUCHAR)&m_RandomNumber, 
        sizeof(m_RandomNumber),
        BCRYPT_USE_SYSTEM_PREFERRED_RNG
        );

    // Allocate the buffer to copy the certificate.
    if (SUCCEEDED(hr))
    {
        *ppbCertificate = (PBYTE)CoTaskMemAlloc(cbCertificate);

        if (*ppbCertificate == NULL) 
        {
            hr = E_OUTOFMEMORY;
        }
    }

    // Copy the certificate and the random number.
    if (SUCCEEDED(hr))
    {
        *pulCertificateLength = cbCertificate;
        CopyMemory(*ppbCertificate, pCertificate, cbCertificate);
        *prnRandomNumber = m_RandomNumber;
    }
    return hr;
}

IOPMVideoOutput::FinishInitialization 메서드는 OPM 핸드셰이크를 완료합니다.

STDMETHODIMP CodecMerit::FinishInitialization(
    const OPM_ENCRYPTED_INITIALIZATION_PARAMETERS *pParameters
    )
{
    HRESULT hr = S_OK;
    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    BCRYPT_OAEP_PADDING_INFO paddingInfo = {0};
    DWORD DecryptedLength = 0;
    PBYTE pbDecryptedParams = NULL;

    // The caller should pass the following structure in
    // pParameters:

    typedef struct {
        GUID  guidCOPPRandom;   // Our random number.
        GUID  guidKDI;          // AES signing key.
        DWORD StatusSeqStart;   // Status sequence number.
        DWORD CommandSeqStart;  // Command sequence number.
    } InitParams;

    paddingInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM;

    //  Decrypt the input using the decoder's private key.

    hr = BCryptOpenAlgorithmProvider(
        &hAlg,
        BCRYPT_RSA_ALGORITHM,
        MS_PRIMITIVE_PROVIDER,
        0
        );

    //  Import the private key.
    if (SUCCEEDED(hr))
    {
        hr = BCryptImportKeyPair(
            hAlg,
            NULL,
            BCRYPT_RSAPRIVATE_BLOB,
            &hKey,
            (PUCHAR)g_PrivateKey, //pbData,
            sizeof(g_PrivateKey), //cbData,
            0
            );
    }

    //  Decrypt the input data.

    if (SUCCEEDED(hr))
    {
        hr = BCryptDecrypt(
            hKey,
            (PBYTE)pParameters,
            OPM_ENCRYPTED_INITIALIZATION_PARAMETERS_SIZE,
            &paddingInfo,
            NULL,
            0,
            NULL,
            0,
            &DecryptedLength,
            BCRYPT_PAD_OAEP
            );
    }

    if (SUCCEEDED(hr))
    {
        pbDecryptedParams = new (std::nothrow) BYTE[DecryptedLength];

        if (pbDecryptedParams == NULL) 
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
         hr = BCryptDecrypt(
             hKey,
             (PBYTE)pParameters,
             OPM_ENCRYPTED_INITIALIZATION_PARAMETERS_SIZE,
             &paddingInfo,
             NULL,
             0,
             pbDecryptedParams,
             DecryptedLength,
             &DecryptedLength,
             BCRYPT_PAD_OAEP
             );
    }

    if (SUCCEEDED(hr))
    {
        InitParams *Params = (InitParams *)pbDecryptedParams;
        
        //  Check the random number.
        if (0 != memcmp(&m_RandomNumber, &Params->guidCOPPRandom, sizeof(m_RandomNumber)))
        {
            hr = E_ACCESSDENIED;
        } 
        else 
        {
            //  Save the key and the sequence numbers.

            CopyMemory(m_AESKey.abRandomNumber, &Params->guidKDI, sizeof(m_AESKey));
            m_StatusSequenceNumber = Params->StatusSeqStart;
            m_CommandSequenceNumber = Params->CommandSeqStart;
        }
    }

    //  Clean up.

    if (hKey)
    {
        BCryptDestroyKey(hKey);
    }
    if (hAlg)
    {
        BCryptCloseAlgorithmProvider(hAlg, 0);
    }
    delete [] pbDecryptedParams;

    return hr;
}

IOPMVideoOutput::GetInformation 메서드에서 OPM_GET_CODEC_INFO 상태 요청을 구현합니다. 입력 데이터는 MFT의 CLSID를 포함하는 OPM_GET_CODEC_INFO_PARAMETERS 구조체입니다. 출력 데이터는 코덱 장점을 포함하는 OPM_GET_CODEC_INFO_INFORMATION 구조체입니다.

STDMETHODIMP CodecMerit::GetInformation( 
    const OPM_GET_INFO_PARAMETERS *Parameters,
    OPM_REQUESTED_INFORMATION *pRequest
    )
{

    HRESULT hr = S_OK;
    OPM_GET_CODEC_INFO_PARAMETERS *CodecInfoParameters;

    //  Check the MAC.
    OPM_OMAC Tag = { 0 };

    hr = ComputeOMAC(
        m_AESKey, 
        (PBYTE)Parameters + OPM_OMAC_SIZE, 
        sizeof(OPM_GET_INFO_PARAMETERS) - OPM_OMAC_SIZE, 
        &Tag
        );

    if (SUCCEEDED(hr))
    {
        if (0 != memcmp(Tag.abOMAC, &Parameters->omac, OPM_OMAC_SIZE))
        {
            hr = E_ACCESSDENIED;
        }
    }

    // Validate the status sequence number. This must be consecutive
    // from the previous sequence number.

    if (SUCCEEDED(hr))
    {
        if (Parameters->ulSequenceNumber != m_StatusSequenceNumber++)
        {
            hr = E_ACCESSDENIED;
        }
    }

    //  Check the status request.

    if (SUCCEEDED(hr))
    {
        if (Parameters->guidInformation != OPM_GET_CODEC_INFO) 
        {
            hr = E_NOTIMPL;
        }
    }

    //  Check parameters.

    if (SUCCEEDED(hr))
    {
        CodecInfoParameters = (OPM_GET_CODEC_INFO_PARAMETERS *)Parameters->abParameters;

        if (Parameters->cbParametersSize > OPM_GET_INFORMATION_PARAMETERS_SIZE ||
            Parameters->cbParametersSize < sizeof(ULONG) ||
            Parameters->cbParametersSize - sizeof(ULONG) != CodecInfoParameters->cbVerifier
            ) 
        {
            hr = E_INVALIDARG;
        }
    }

    //  The input data should consist of the CLSID of the decoder.

    if (SUCCEEDED(hr))
    {
        CodecInfoParameters = (OPM_GET_CODEC_INFO_PARAMETERS *)Parameters->abParameters;
    
        if (CodecInfoParameters->cbVerifier != sizeof(CLSID) ||
            0 != memcmp(&CLSID_MPEG1SampleDecoder,
                        CodecInfoParameters->Verifier,
                        CodecInfoParameters->cbVerifier)) 
        {
            hr = E_ACCESSDENIED;
        }
    }

    if (SUCCEEDED(hr))
    {
        // Now return the decoder merit to the caller.

        ZeroMemory(pRequest, sizeof(OPM_REQUESTED_INFORMATION));

        OPM_GET_CODEC_INFO_INFORMATION *pInfo = 
            (OPM_GET_CODEC_INFO_INFORMATION *)pRequest->abRequestedInformation;

        pInfo->Merit = DECODER_MERIT;
        pInfo->rnRandomNumber = Parameters->rnRandomNumber;

        pRequest->cbRequestedInformationSize = sizeof(OPM_GET_CODEC_INFO_INFORMATION);

        //  Sign it with the key.

        hr = ComputeOMAC(
            m_AESKey, 
            (PBYTE)pRequest + OPM_OMAC_SIZE, 
            sizeof(OPM_REQUESTED_INFORMATION) - OPM_OMAC_SIZE, 
            &pRequest->omac
            );
    }

    return hr;
}

GetInformation 메서드는 OMAC-1 알고리즘을 사용하여 MAC(메시지 인증 코드)을 계산해야 합니다. OMAC-1 값 계산을 참조하세요.

다른 OPM 상태 요청을 지원할 필요는 없습니다.

IOPMVideoOutput::COPPCompatibleGetInformationIOPMVideoOutput::Configure 메서드는 코덱 장점에 필요하지 않으므로 이러한 메서드는 E_NOTIMPL 반환할 수 있습니다.

STDMETHODIMP CodecMerit::COPPCompatibleGetInformation( 
    const OPM_COPP_COMPATIBLE_GET_INFO_PARAMETERS *pParameters,
    OPM_REQUESTED_INFORMATION *pRequestedInformation)
{
    return E_NOTIMPL;
}

STDMETHODIMP CodecMerit::Configure( 
    const OPM_CONFIGURE_PARAMETERS *pParameters,
    ULONG ulAdditionalParametersSize,
    const BYTE *pbAdditionalParameters)
{
    return E_NOTIMPL;
}

Media Foundation 변환

사용자 지정 MFT 작성