Заслуга кодека
Начиная с Windows 7 кодек Media Foundation можно назначить значение. При перечислении кодеков кодеки с более высокими преимуществами предпочтительнее кодеков с более низкой заслугой. Кодеки с любым значением заслуг предпочтительнее кодеков без назначенной заслуги. Дополнительные сведения о перечислении кодека см. в MFTEnumEx.
Значения заслуг назначаются корпорацией Майкрософт. В настоящее время только аппаратные кодеки могут получить значение заслуги. Поставщик кодека также выдает цифровой сертификат, который используется для проверки значения заслуг кодека. Чтобы получить сертификат, отправьте запрос электронной почты в wmla@microsoft.com. Процесс получения сертификата включает подписание лицензии и предоставление набора информационных файлов корпорации Майкрософт.
Заслуги кодека работают следующим образом:
- Поставщик кодека реализует одно из следующих действий:
- Мини-драйвер AVStream. Media Foundation предоставляет стандартный MFT прокси-сервера для драйверов AVStream. Это рекомендуемый вариант.
- Преобразование Media Foundation (MFT), которое выступает в качестве прокси-сервера для оборудования. Дополнительные сведения см. в аппаратных MFT.
- Значение заслуг кодека хранится в реестре для быстрого поиска.
- Функция MFTEnumEx сортирует кодеки в порядке заслуг. Кодеки с значениями заслуг отображаются в списке за локально зарегистрированными кодеками (см. MFTRegisterLocal), но перед другими кодеками.
- При создании MFT значение кодека проверяется с помощью API Output Protection Manager (OPM).
- Для прокси-сервера MFT кодек реализует интерфейс IOPMVideoOutput. Для драйвера AVStream кодек реализует набор свойств KSPROPSETID_OPMVideoOutput.
На следующей схеме показано, как проверяется значение в обоих случаях:
схема
Пользовательский прокси-сервер MFT
Если вы предоставляете прокси-сервер MFT для аппаратного кодека, реализуйте значение преимущества кодека следующим образом:
Реализуйте интерфейс IOPMVideoOutput в MFT. Пример кода показан в следующем разделе этого раздела.
Добавьте атрибут MFT_CODEC_MERIT_Attribute в реестр следующим образом:
- Вызовите MFCreateAttributes для создания нового хранилища атрибутов.
- Вызовите IMFAttributes::SetUINT32, чтобы задать атрибут MFT_CODEC_MERIT_Attribute. Значение атрибута является назначением кодека.
- Вызовите MFTRegister для регистрации MFT. Передайте хранилище атрибутов в параметре pAttributes.
Приложение вызывает MFTEnumEx. Эта функция возвращает массив мвфактивации указателей, по одному для каждого кодека, соответствующего критериям перечисления.
Приложение вызывает IMFActivate::ActivateObject для создания MFT.
Метод ActivateObject вызывает функцию MFGetMFTMerit для проверки сертификата и значения заслуг.
Функция MFGetMFTMerit вызывает вызовы IOPMVideoOutput::GetInformation и отправляет запрос состояния OPM_GET_CODEC_INFO. Этот запрос состояния возвращает назначенное значение заслуг кодека. Если это значение не соответствует значению реестра, ActivateObject может завершиться ошибкой.
В следующем коде показано, как добавить значение заслуги в реестр при регистрации 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;
}
Реализация IOPMVideoOutput для Codec Merit
В следующем коде показано, как реализовать IOPMVideoOutput для заслуг кодека. Более общие сведения об API OPM см. в разделе Output Protection Manager.
Заметка
Приведенный здесь код не имеет запутывания или других механизмов безопасности. Оно предназначено для отображения базовой реализации подтверждения OPM и запроса состояния.
В этом примере предполагается, что g_TestCert представляет собой массив байтов, содержащий цепочку сертификатов кодека, а g_PrivateKey — массив байтов, содержащий закрытый ключ из сертификата:
// 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)
В методе IOPMVideoOutput::StartInitialization создайте случайное число для подтверждения. Верните это число и сертификат вызывающему объекту:
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;
}
Метод IOPMVideoOutput::FinishInitialization завершает подтверждение 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;
}
В методе IOPMVideoOutput::GetInformation реализуйте запрос состояния OPM_GET_CODEC_INFO. Входные данные — это OPM_GET_CODEC_INFO_PARAMETERS структура, содержащая CLSID MFT. Выходные данные — это OPM_GET_CODEC_INFO_INFORMATION структура, содержащая заслуги кодека.
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;
}
Метод GetInformation должен вычислить код проверки подлинности сообщений (MAC) с помощью алгоритма OMAC-1; см. вычисления значения OMAC-1.
Не требуется поддерживать другие запросы о состоянии OPM.
Методы IOPMVideoOutput::COPPCompatibleGetInformation и методы IOPMVideoOutput::Configure не требуются для обеспечения заслуг кодека, поэтому эти методы могут возвращать 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;
}
Связанные разделы