Share via


Codec Merit

À compter de Windows 7, une valeur de mérite peut être attribuée à un codec Media Foundation. Lorsque les codecs sont énumérés, les codecs présentant un mérite plus élevé sont préférés aux codecs dont le mérite est inférieur. Les codecs avec n’importe quelle valeur de mérite sont préférés aux codecs sans mérite affecté. Pour plus d’informations sur l’énumération de codecs, consultez MFTEnumEx.

Les valeurs de mérite sont attribuées par Microsoft. Actuellement, seuls les codecs matériels sont éligibles pour recevoir une valeur de mérite. Le fournisseur du codec se fait également délivrer un certificat numérique, qui est utilisé pour vérifier la valeur de mérite du codec. Pour obtenir un certificat, envoyez une demande par e-mail à wmla@microsoft.com. Le processus d’obtention d’un certificat comprend la signature d’une licence et la fourniture d’un ensemble de fichiers d’informations à Microsoft.

Le mérite du codec fonctionne comme suit :

  1. Le fournisseur de codecs implémente l’une des opérations suivantes :
    • Un mini-pilote AVStream. Media Foundation fournit un proxy MFT standard pour les pilotes AVStream. C'est l'option recommandée.
    • Une transformation Media Foundation (MFT) qui joue le rôle de proxy pour le matériel. Pour plus d’informations, consultez MfT matériels.
  2. La valeur de mérite du codec est stockée dans le Registre pour une recherche rapide.
  3. La fonction MFTEnumEx trie les codecs par ordre de mérite. Les codecs avec des valeurs de mérite apparaissent dans la liste derrière les codecs enregistrés localement (voir MFTRegisterLocal), mais devant les autres codecs.
  4. Lorsque le MFT est créé, le mérite du codec est vérifié à l’aide de l’API Output Protection Manager (OPM).
  5. Pour un proxy MFT, le codec implémente l’interface IOPMVideoOutput . Pour un pilote AVStream, le codec implémente le jeu de propriétés KSPROPSETID_OPMVideoOutput.

Le diagramme suivant montre comment le mérite est vérifié dans les deux cas :

diagramme montrant deux processus : l’un conduit via le proxy media foundation mft et le pilote avstream, l’autre via le proxy personnalisé mft

Proxy personnalisé MFT

Si vous fournissez un proxy MFT pour le codec matériel, implémentez la valeur de mérite du codec comme suit :

  1. Implémentez l’interface IOPMVideoOutput dans MFT. L’exemple de code est présenté dans la section suivante de cette rubrique.

  2. Ajoutez l’attribut MFT_CODEC_MERIT_Attribute au Registre, comme suit :

    1. Appelez MFCreateAttributes pour créer un magasin d’attributs.
    2. Appelez IMFAttributes::SetUINT32 pour définir l’attribut MFT_CODEC_MERIT_Attribute . La valeur de l’attribut est le mérite attribué au codec.
    3. Appelez MFTRegister pour inscrire le MFT. Transmettez le magasin d’attributs dans le paramètre pAttributes .
  3. L’application appelle MFTEnumEx. Cette fonction retourne un tableau de pointeurs IMFActivate , un pour chaque codec qui correspond aux critères d’énumération.

  4. L’application appelle IMFActivate::ActivateObject pour créer le MFT.

  5. La méthode ActivateObject appelle la fonction MFGetMFTMerit pour vérifier le certificat et la valeur de mérite.

  6. La fonction MFGetMFTMerit appelle IOPMVideoOutput::GetInformation et envoie une requête OPM_GET_CODEC_INFO status. Cette requête status retourne la valeur de mérite attribuée au codec. Si cette valeur ne correspond pas à la valeur de Registre, ActivateObject peut échouer.

Le code suivant montre comment ajouter la valeur de mérite au registre lorsque vous inscrivez le 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;
}

Implémentation d’IOPMVideoOutput pour codec Merit

Le code suivant montre comment implémenter IOPMVideoOutput pour le mérite du codec. Pour une présentation plus générale de l’API OPM, consultez Gestionnaire de protection de sortie.

Notes

Le code présenté ici n’a pas d’obfuscation ou d’autres mécanismes de sécurité. Il est destiné à montrer l’implémentation de base de l’établissement d’une liaison opm et status demande.

 

Cet exemple suppose que g_TestCert est un tableau d’octets qui contient la chaîne de certificats du codec, et g_PrivateKey est un tableau d’octets qui contient la clé privée du certificat :

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

Dans la méthode IOPMVideoOutput::StartInitialization , générez un nombre aléatoire pour la négociation. Retournez ce numéro et le certificat à l’appelant :

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

La méthode IOPMVideoOutput::FinishInitialization termine l’établissement d’une liaison 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;
}

Dans la méthode IOPMVideoOutput::GetInformation, implémentez la requête status OPM_GET_CODEC_INFO. Les données d’entrée sont une structure OPM_GET_CODEC_INFO_PARAMETERS qui contient le CLSID de votre MFT. Les données de sortie sont une structure OPM_GET_CODEC_INFO_INFORMATION qui contient le mérite du codec.

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

La méthode GetInformation doit calculer un code d’authentification de message (MAC) à l’aide de l’algorithme OMAC-1 ; consultez Calcul de la valeur OMAC-1.

Il n’est pas nécessaire de prendre en charge d’autres demandes de status opm.

Les méthodes IOPMVideoOutput::COPPCompatibleGetInformation et IOPMVideoOutput::Configure ne sont pas requises pour le mérite du codec, de sorte que ces méthodes peuvent retourner 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;
}

Transformations Media Foundation

Écriture d’un MFT personnalisé