Codec Merit
Mulai windows 7, codec Media Foundation dapat diberi nilai merit . Ketika codec dijumlahkan, codec dengan manfaat yang lebih tinggi lebih disukai daripada codec dengan manfaat yang lebih rendah. Codec dengan nilai manfaat apa pun lebih disukai daripada codec tanpa manfaat yang ditetapkan. Untuk detail tentang enumerasi codec, lihat MFTEnumEx.
Nilai merit ditetapkan oleh Microsoft. Saat ini, hanya codec perangkat keras yang memenuhi syarat untuk menerima nilai manfaat. Vendor codec juga mengeluarkan sertifikat digital, yang digunakan untuk memverifikasi nilai manfaat codec. Untuk mendapatkan sertifikat, kirim permintaan email ke wmla@microsoft.com. Proses untuk mendapatkan sertifikat termasuk menandatangani lisensi dan menyediakan sekumpulan file informasi ke Microsoft.
Manfaat codec berfungsi sebagai berikut:
- Vendor codec mengimplementasikan salah satu hal berikut:
- Driver mini AVStream. Media Foundation menyediakan MFT proksi standar untuk driver AVStream. Ini adalah opsi yang direkomendasikan.
- Transformasi Media Foundation (MFT) yang bertindak sebagai proksi ke perangkat keras. Untuk informasi selengkapnya, lihat MFTs Perangkat Keras.
- Nilai manfaat codec disimpan dalam registri untuk pencarian cepat.
- Fungsi MFTEnumEx mengurutkan codec dalam urutan merit. Codec dengan nilai merit muncul dalam daftar di belakang codec yang terdaftar secara lokal (lihat MFTRegisterLocal), tetapi di depan codec lainnya.
- Saat MFT dibuat, manfaat codec diverifikasi menggunakan API Output Protection Manager (OPM).
- Untuk MFT proksi, codec mengimplementasikan antarmuka IOPMVideoOutput . Untuk driver AVStream, codec mengimplementasikan kumpulan properti KSPROPSETID_OPMVideoOutput.
Diagram berikut menunjukkan bagaimana manfaat diverifikasi dalam kedua kasus:
MFT Proksi Kustom
Jika Anda menyediakan MFT proksi untuk codec perangkat keras, terapkan nilai merit codec sebagai berikut:
Terapkan antarmuka IOPMVideoOutput di MFT. Contoh kode ditampilkan di bagian berikutnya dari topik ini.
Tambahkan atribut MFT_CODEC_MERIT_Attribute ke registri, sebagai berikut:
- Panggil MFCreateAttributes untuk membuat penyimpanan atribut baru.
- Panggil IMFAttributes::SetUINT32 untuk mengatur atribut MFT_CODEC_MERIT_Attribute . Nilai atribut adalah manfaat yang ditetapkan codec.
- Panggil MFTRegister untuk mendaftarkan MFT. Teruskan penyimpanan atribut di parameter pAttributes .
Aplikasi ini memanggil MFTEnumEx. Fungsi ini mengembalikan array penunjuk IMFActivate , satu untuk setiap codec yang cocok dengan kriteria enumerasi.
Aplikasi memanggil IMFActivate::ActivateObject untuk membuat MFT.
Metode ActivateObject memanggil fungsi MFGetMFTMerit untuk memverifikasi sertifikat dan nilai merit.
Fungsi MFGetMFTMerit memanggil IOPMVideoOutput::GetInformation dan mengirim permintaan status OPM_GET_CODEC_INFO . Permintaan status ini mengembalikan nilai merit codec yang ditetapkan. Jika nilai ini tidak cocok dengan nilai registri, ActivateObject mungkin gagal.
Kode berikut menunjukkan cara menambahkan nilai manfaat ke registri saat Anda mendaftarkan 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;
}
Menerapkan IOPMVideoOutput untuk Codec Merit
Kode berikut menunjukkan cara mengimplementasikan IOPMVideoOutput untuk merit codec. Untuk diskusi yang lebih umum tentang OPM API, lihat Manajer Perlindungan Output.
Catatan
Kode yang ditunjukkan di sini tidak memiliki obfuscation atau mekanisme keamanan lainnya. Hal ini dimaksudkan untuk menunjukkan implementasi dasar permintaan jabat tangan dan status OPM.
Contoh ini mengasumsikan bahwa g_TestCert adalah array byte yang berisi rantai sertifikat codec, dan g_PrivateKey adalah array byte yang berisi kunci privat dari sertifikat:
// 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)
Dalam metode IOPMVideoOutput::StartInitialization , hasilkan angka acak untuk jabat tangan. Kembalikan nomor ini dan sertifikat ke pemanggil:
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;
}
Metode IOPMVideoOutput::FinishInitialization menyelesaikan jabat tangan 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;
}
Dalam metode IOPMVideoOutput::GetInformation , terapkan permintaan status OPM_GET_CODEC_INFO . Data input adalah struktur OPM_GET_CODEC_INFO_PARAMETERS yang berisi CLSID MFT Anda. Data output adalah struktur OPM_GET_CODEC_INFO_INFORMATION yang berisi manfaat 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;
}
Metode GetInformation harus menghitung Kode Autentikasi Pesan (MAC) menggunakan algoritma OMAC-1; lihat Menghitung Nilai OMAC-1.
Tidak perlu mendukung permintaan status OPM lainnya.
Metode IOPMVideoOutput::COPPCompatibleGetInformation dan IOPMVideoOutput::Configure tidak diperlukan untuk merit codec, sehingga metode ini dapat mengembalikan 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;
}
Topik terkait