Codec Merit

Ab Windows 7 kann einem Media Foundation-Codec ein Verdienstwert zugewiesen werden. Wenn Codecs aufgelistet werden, werden Codecs mit höherem Wert gegenüber Codecs mit geringerem Verdienst bevorzugt. Codecs mit einem beliebigen Wert werden gegenüber Codecs ohne zugewiesene Leistung bevorzugt. Ausführliche Informationen zur Codecaufzählung finden Sie unter MFTEnumEx.

Merit-Werte werden von Microsoft zugewiesen. Derzeit sind nur Hardwarecodecs berechtigt, einen Leistungswert zu erhalten. Dem Codecanbieter wird auch ein digitales Zertifikat ausgestellt, das verwendet wird, um den Wert des Codecs zu überprüfen. Um ein Zertifikat zu erhalten, senden Sie eine E-Mail-Anforderung an wmla@microsoft.com. Der Prozess zum Abrufen eines Zertifikats umfasst das Signieren einer Lizenz und das Bereitstellen einer Reihe von Informationsdateien für Microsoft.

Codec merit funktioniert wie folgt:

  1. Der Codecanbieter implementiert eine der folgenden Komponenten:
    • Ein AVStream-Minitreiber. Media Foundation stellt einen Standardproxy-MFT für AVStream-Treiber bereit. Dies ist die empfohlene Option.
    • Eine Media Foundation-Transformation (MFT), die als Proxy für die Hardware fungiert. Weitere Informationen finden Sie unter Hardware-MFTs.
  2. Der Wert des Codecs wird für die schnelle Suche in der Registrierung gespeichert.
  3. Die MFTEnumEx-Funktion sortiert Codecs nach Wert. Codecs mit Merit-Werten werden in der Liste hinter lokal registrierten Codecs angezeigt (siehe MFTRegisterLocal), aber vor anderen Codecs.
  4. Wenn der MFT erstellt wird, wird der Nutzen des Codecs mithilfe der OPM-API ( Output Protection Manager ) überprüft.
  5. Für einen Proxy-MFT implementiert der Codec die IOPMVideoOutput-Schnittstelle . Für einen AVStream-Treiber implementiert der Codec den KSPROPSETID_OPMVideoOutput-Eigenschaftssatz.

Das folgende Diagramm zeigt, wie die Leistung in beiden Fällen überprüft wird:

Diagramm mit zwei Prozessen: einer führt über Media Foundation-Proxy mft und avstream-Treiber, der andere über benutzerdefinierte Proxy-MFT

Benutzerdefinierte Proxy-MFT

Wenn Sie einen Proxy-MFT für den Hardwarecodec bereitstellen, implementieren Sie den Wert für codec merit wie folgt:

  1. Implementieren Sie die IOPMVideoOutput-Schnittstelle im MFT. Beispielcode wird im nächsten Abschnitt dieses Themas gezeigt.

  2. Fügen Sie der Registrierung das attribut MFT_CODEC_MERIT_Attribute wie folgt hinzu:

    1. Rufen Sie MFCreateAttributes auf, um einen neuen Attributspeicher zu erstellen.
    2. Rufen Sie IMFAttributes::SetUINT32 auf, um das attribut MFT_CODEC_MERIT_Attribute festzulegen. Der Wert des Attributs ist der zugewiesene Wert des Codecs.
    3. Rufen Sie MFTRegister auf, um MFT zu registrieren. Übergeben Sie den Attributspeicher im pAttributes-Parameter .
  3. Die Anwendung ruft MFTEnumEx auf. Diese Funktion gibt ein Array von IMFActivate-Zeigern zurück, eines für jeden Codec, der den Enumerationskriterien entspricht.

  4. Die Anwendung ruft IMFActivate::ActivateObject auf, um das MFT zu erstellen.

  5. Die ActivateObject-Methode ruft die MFGetMFTMerit-Funktion auf, um das Zertifikat und den Wert des Werts zu überprüfen.

  6. Die MFGetMFTMerit-Funktion ruft IOPMVideoOutput::GetInformation auf und sendet eine OPM_GET_CODEC_INFO status Anforderung. Diese status Anforderung gibt den dem Codec zugewiesenen Wert zurück. Wenn dieser Wert nicht mit dem Registrierungswert übereinstimmt, schlägt ActivateObject möglicherweise fehl.

Der folgende Code zeigt, wie Sie den Wert "Merit" der Registrierung hinzufügen, wenn Sie MFT registrieren:

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

Implementieren von IOPMVideoOutput für Codec Merit

Der folgende Code zeigt, wie IoPMVideoOutput für codec merit implementiert wird. Eine allgemeinere Erläuterung der OPM-API finden Sie unter Output Protection Manager.

Hinweis

Der hier gezeigte Code weist keine Verschleierung oder andere Sicherheitsmechanismen auf. Es soll die grundlegende Implementierung des OPM-Handshakes und status-Anforderung zeigen.

 

In diesem Beispiel wird davon ausgegangen, dass g_TestCert ein Bytearray ist, das die Zertifikatkette des Codecs enthält, und g_PrivateKey ein Bytearray ist, das den privaten Schlüssel aus dem Zertifikat enthält:

// 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)

Generieren Sie in der IOPMVideoOutput::StartInitialization-Methode eine Zufallszahl für den Handshake. Geben Sie diese Nummer und das Zertifikat an den Aufrufer zurück:

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

Die IOPMVideoOutput::FinishInitialization-Methode schließt den OPM-Handshake ab:

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

Implementieren Sie in der IOPMVideoOutput::GetInformation-Methode die OPM_GET_CODEC_INFO status Anforderung. Die Eingabedaten sind eine OPM_GET_CODEC_INFO_PARAMETERS Struktur, die die CLSID Ihres MFT enthält. Die Ausgabedaten sind eine OPM_GET_CODEC_INFO_INFORMATION Struktur, die den Codec-Wert enthält.

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

Die GetInformation-Methode muss mithilfe des OMAC-1-Algorithmus einen Nachrichtenauthentifizierungscode (Message Authentication Code, MAC) berechnen. Siehe Berechnen des OMAC-1-Werts.

Es ist nicht erforderlich, andere OPM-status-Anforderungen zu unterstützen.

Die Methoden IOPMVideoOutput::COPPCompatibleGetInformation und IOPMVideoOutput::Configure sind für codec merit nicht erforderlich, sodass diese Methoden E_NOTIMPL zurückgeben können.

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-Transformationen

Schreiben eines benutzerdefinierten MFT