Sdílet prostřednictvím


Kodek Zaslouchaný

Od Systému Windows 7 může být kodek Media Foundation přiřazen si zaslouží hodnotu. Pokud jsou kodeky výčty, upřednostňují se u kodeků s nižšími přednostmi kodeky s vyššími výhody. Kodeky s libovolnou hodnotou zásluhnou přednost před kodeky bez přiřazené výhody. Podrobnosti o výčtu kodeků naleznete v tématu MFTEnumEx.

Hodnoty merit jsou přiřazeny Microsoftem. V současné době mají nárok pouze hardwarové kodeky na získání hodnoty za přínos. Dodavatel kodeku také vydává digitální certifikát, který slouží k ověření hodnoty zaslouchaný kodek. Pokud chcete získat certifikát, odešlete e-mailovou žádost wmla@microsoft.com. Proces získání certifikátu zahrnuje podepsání licence a poskytnutí sady informačních souborů společnosti Microsoft.

Zaslouchaný kodek funguje takto:

  1. Dodavatel kodeku implementuje jednu z následujících možností:
    • Mini-ovladač AVStream. Media Foundation poskytuje standardní proxy MFT pro ovladače AVStream. Toto je doporučená možnost.
    • Transformace Media Foundation (MFT), která funguje jako proxy server hardwaru. Další informace naleznete v tématu Hardwarové MFT.
  2. Hodnota přínosu kodeku je uložena v registru pro rychlé vyhledávání.
  3. Funkce MFTEnumEx řadí kodeky v pořadí od výhody. Kodeky s hodnotami merit se zobrazují v seznamu místně registrovaných kodeků (viz MFTRegisterLocal), ale před jinými kodeky.
  4. Po vytvoření MFT se výhody kodeku ověří pomocí rozhraní API Output Protection Manageru (OPM).
  5. Pro proxy MFT kodek implementuje rozhraní IOPMVideoOutput. U ovladače AVStream implementuje kodek sadu vlastností KSPROPSETID_OPMVideoOutput.

Následující diagram znázorňuje, jak jsou výhody ověřeny v obou případech:

diagram znázorňující dva procesy: jeden vede prostřednictvím proxy serveru media Foundation mft a ovladače avstream, druhý prostřednictvím vlastního proxy mft

Vlastní proxy MFT

Pokud zadáte proxy MFT pro hardwarový kodek, implementujte hodnotu zaslouchaný kodek následujícím způsobem:

  1. Implementujte rozhraní IOPMVideoOutput v MFT. Ukázkový kód se zobrazí v další části tohoto tématu.

  2. Do registru přidejte atribut MFT_CODEC_MERIT_Attribute následujícím způsobem:

    1. Volání MFCreateAttributes vytvořit nové úložiště atributů.
    2. Volání MMFAttributes::SetUINT32 nastavit atribut MFT_CODEC_MERIT_Attribute. Hodnota atributu je přiřazené kodeku.
    3. Pokud chcete zaregistrovat MFT, zavolejte MFTRegister. Předejte úložiště atributů v parametru pAttributes.
  3. Aplikace volá MFTEnumEx. Tato funkce vrátí pole POINTERActivate ukazatele, jedno pro každý kodek, který odpovídá kritériím výčtu.

  4. Aplikace volá MMFActivate::ActivateObject k vytvoření MFT.

  5. Metoda ActivateObject volá funkci MFGetMFTMerit k ověření certifikátu a hodnoty výhody.

  6. Funkce MFGetMFTMerit volá IOPMVideoOutput::GetInformation a odešle žádost o stav OPM_GET_CODEC_INFO. Tato žádost o stav vrátí hodnotu přiřazené zaslouchacího kodeku. Pokud tato hodnota neodpovídá hodnotě registru, ActivateObject může selhat.

Následující kód ukazuje, jak přidat hodnotu výhody do registru při registraci 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;
}

Implementace IOPMVideoOutput pro kodek Merit

Následující kód ukazuje, jak implementovat IOPMVideoOutput pro výhody kodeku. Obecnější diskuzi o rozhraní OPM API najdete v tématu output Protection Manager.

Poznámka

Zde uvedený kód neobsahuje žádné obfuskace ani jiné mechanismy zabezpečení. Účelem je ukázat základní implementaci žádosti o handshake a stav OPM.

 

Tento příklad předpokládá, že g_TestCert je bajtové pole, které obsahuje řetěz certifikátů kodeku, a g_PrivateKey je pole bajtů, které obsahuje privátní klíč z certifikátu:

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

V IOPMVideoOutput::StartInitialization metoda vygenerujte náhodné číslo pro metodu handshake. Vrátí toto číslo a certifikát volajícímu:

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

Metoda IOPMVideoOutput::FinishInitialization dokončí metodu handshake 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;
}

V IOPMVideoOutput::GetInformation metoda implementujte požadavek na stav OPM_GET_CODEC_INFO. Vstupní data jsou OPM_GET_CODEC_INFO_PARAMETERS struktura, která obsahuje CLSID vašeho MFT. Výstupní data jsou OPM_GET_CODEC_INFO_INFORMATION struktura, která obsahuje výhody kodeku.

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

Metoda GetInformation musí vypočítat ověřovací kód zprávy (MAC) pomocí algoritmu OMAC-1; viz Výpočet hodnoty OMAC-1.

Není nutné podporovat žádné další žádosti o stav OPM.

IOPMVideoOutput::COPPCompatibleGetInformation a IOPMVideoOutput::Konfigurace metod pro výhody kodeku, takže tyto metody mohou vracet 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;
}

transformace Media Foundation

psaní vlastního MFT