Freigeben über


Codec-Verdienst

Ab Windows 7 kann einem Media Foundation-Codec ein Wert zugewiesen werden. Wenn Codecs aufgezählt werden, werden Codecs mit höherem Verdienst gegenüber Codecs mit niedrigerer Leistung bevorzugt. Codecs mit jedem Leistungswert werden gegenüber Codecs ohne eine zugewiesene Leistung bevorzugt. Ausführliche Informationen zur Codec-Enumeration finden Sie unter MFTEnumEx.

Verdienstwerte werden von Microsoft zugewiesen. Derzeit sind nur Hardwarecodecs berechtigt, einen Leistungswert zu erhalten. Der Codec-Anbieter wird auch ein digitales Zertifikat ausgestellt, das verwendet wird, um den Leistungswert 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 die Bereitstellung einer Reihe von Informationsdateien an Microsoft.

Codec-Verdienst funktioniert wie folgt:

  1. Der Codecanbieter implementiert eine der folgenden:
    • Ein AVStream-Minitreiber. Media Foundation stellt einen standardmäßigen Proxy-MFT für AVStream-Treiber bereit. Dies ist die empfohlene Option.
    • Eine Media Foundation-Transformation (Media Foundation Transform, MFT), die als Proxy für die Hardware fungiert. Weitere Informationen finden Sie unter Hardware-MFTs.
  2. Der Wert des Codecs wird in der Registrierung gespeichert, um schnell nachschlagen zu können.
  3. Die MFTEnumEx-Funktion sortiert Codecs in der Reihenfolge der Verdienste. Codecs mit Verdienstwerten werden in der Liste hinter lokal registrierten Codecs angezeigt (siehe MFTRegisterLocal), aber vor anderen Codecs.
  4. Wenn die MFT erstellt wird, wird der Nutzen des Codecs mithilfe der Output Protection Manager-API () ü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-Eigenschaftensatz.

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

Diagramm mit zwei Prozessen: eine führt durch media foundation Proxy mft und avstream Treiber, die andere über benutzerdefinierte Proxy mft

Benutzerdefinierter Proxy-MFT

Wenn Sie einen Proxy-MFT für den Hardwarecodec bereitstellen, implementieren Sie den Codec-Leistungswert wie folgt:

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

  2. Fügen Sie das attribut MFT_CODEC_MERIT_Attribute der Registrierung 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 die MFT zu registrieren. Übergeben Sie den Attributspeicher im pAttributes--Parameter.
  3. Die Anwendung ruft MFTEnumExauf. Diese Funktion gibt ein Array IMFActivate Zeiger zurück, eines für jeden Codec, der den Enumerationskriterien entspricht.

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

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

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

Der folgende Code zeigt, wie Sie den Verdienstwert der Registrierung hinzufügen, wenn Sie die 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-Vorteile implementiert werden. Eine allgemeinere Erläuterung der OPM-API finden Sie unter Output Protection Manager.

Anmerkung

Der hier gezeigte Code enthält keine Verschleierung oder andere Sicherheitsmechanismen. Es soll die grundlegende Implementierung des OPM-Handshakes und der Statusanforderung 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. Gibt 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 Statusanforderung. 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 einen Nachrichtenauthentifizierungscode (MAC) mithilfe des OMAC-1-Algorithmus berechnen; siehe Berechnen des OMAC-1-Werts.

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

Die IOPMVideoOutput::COPPCompatibleGetInformation und IOPMVideoOutput::Configure Methoden sind nicht erforderlich, damit diese Methoden E_NOTIMPLzurü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-