次の方法で共有


コーデックメリット

Windows 7 以降では、Media Foundation コーデックに メリット 値を割り当てることができます。 コーデックを列挙する場合は、メリットの低いコーデックよりもメリットの高いコーデックが優先されます。 メリット値を持つコーデックは、割り当てられたメリットのないコーデックよりも優先されます。 コーデック列挙の詳細については、MFTEnumExを参照してください。

メリットの値は、Microsoft によって割り当てられます。 現時点では、ハードウェア コーデックのみがメリット値を受け取る資格があります。 コーデック ベンダーには、コーデックのメリット値を検証するために使用されるデジタル証明書も発行されます。 証明書を取得するには、wmla@microsoft.comに電子メール要求を送信します。 証明書を取得するプロセスには、ライセンスに署名し、一連の情報ファイルを Microsoft に提供することが含まれます。

コーデックのメリットは次のように機能します。

  1. コーデック ベンダーは、次のいずれかを実装します。
    • AVStream ミニ ドライバー。 Media Foundation は、AVStream ドライバー用の標準プロキシ MFT を提供します。 これは推奨されるオプションです。
    • ハードウェアへのプロキシとして機能する Media Foundation 変換 (MFT)。 詳細については、ハードウェア MFT を参照してください。
  2. コーデックのメリット値は、迅速な検索のためにレジストリに格納されます。
  3. MFTEnumEx 関数は、メリットの順にコーデックを並べ替えます。 メリット値を持つコーデックは、ローカルに登録されたコーデック (MFTRegisterLocalを参照) の背後にある一覧に表示されますが、他のコーデックよりも先に表示されます。
  4. MFT が作成されると、Output Protection Manager (OPM) API を使用してコーデックのメリットが検証されます。
  5. プロキシ MFT の場合、コーデックは IOPMVideoOutput インターフェイスを実装します。 AVStream ドライバーの場合、コーデックは KSPROPSETID_OPMVideoOutput プロパティ セットを実装します。

次の図は、どちらの場合もメリットがどのように検証されるかを示しています。

2 つのプロセスを示す図:1 つはメディア基盤プロキシ mft と avstream ドライバーを通じて、もう 1 つはカスタム プロキシ mft

カスタム プロキシ MFT

ハードウェア コーデックのプロキシ MFT を指定する場合は、次のようにコーデックメリット値を実装します。

  1. IOPMVideoOutput インターフェイスを MFT に実装します。 コード例は、このトピックの次のセクションで示します。

  2. 次のように、MFT_CODEC_MERIT_Attribute 属性をレジストリに追加します。

    1. MFCreateAttributes呼び出して、新しい属性ストアを作成します。
    2. IMFAttributes::SetUINT32呼び出して、MFT_CODEC_MERIT_Attribute 属性を設定します。 属性の値は、コーデックに割り当てられたメリットです。
    3. MFTRegister呼び出して、MFT を登録します。 pAttributes パラメーターに属性ストアを渡します。
  3. アプリケーションは MFTEnumEx呼び出します。 この関数は、列挙条件 一致するコーデックごとに 1 つずつ、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 Transforms

カスタム MFT の書き込みの