Bug 1898588 - part1 : add encryptionSchemes in MFCDMMediaCapability in order to support scheme per type r=jolin

Currently our implementation is the scheme support per key system,
not per content type. As each content type can have different supported
scheme, eg. type A only supports cenc, but type B only supports cbcs,
only having scheme per key system can't return a precise result.

Differential Revision: https://phabricator.services.mozilla.com/D211642
This commit is contained in:
alwu
2024-05-28 21:10:38 +00:00
parent 2044fa992e
commit f1d0a686b0
7 changed files with 149 additions and 88 deletions

View File

@@ -597,15 +597,26 @@ void MediaRawDataWriter::PopFront(size_t aSize) {
const char* CryptoSchemeToString(const CryptoScheme& aScheme) {
switch (aScheme) {
case CryptoScheme::None:
return "None";
return "none";
case CryptoScheme::Cenc:
return "Cenc";
return "cenc";
case CryptoScheme::Cbcs:
return "Cbcs";
return "cbcs";
default:
MOZ_ASSERT_UNREACHABLE();
return "";
}
}
CryptoScheme StringToCryptoScheme(const nsAString& aString) {
if (aString.EqualsLiteral("cenc")) {
return CryptoScheme::Cenc;
}
if (aString.EqualsLiteral("cbcs")) {
return CryptoScheme::Cbcs;
}
// TODO : support cbcs-1-9?
return CryptoScheme::None;
}
} // namespace mozilla

View File

@@ -13,6 +13,7 @@
# include "SharedBuffer.h"
# include "TimeUnits.h"
# include "mozilla/CheckedInt.h"
# include "mozilla/EnumSet.h"
# include "mozilla/Maybe.h"
# include "mozilla/PodOperations.h"
# include "mozilla/RefPtr.h"
@@ -586,8 +587,10 @@ enum class CryptoScheme : uint8_t {
Cenc,
Cbcs,
};
using CryptoSchemeSet = EnumSet<CryptoScheme, uint8_t>;
const char* CryptoSchemeToString(const CryptoScheme& aScheme);
CryptoScheme StringToCryptoScheme(const nsAString& aString);
class CryptoTrack {
public:

View File

@@ -92,12 +92,18 @@ WMFCDMCapabilites::GetCapabilities(
NS_ConvertUTF16toUTF8(capabilities.keySystem()).get(),
capabilities.isHardwareDecryption());
for (const auto& v : capabilities.videoCapabilities()) {
EME_LOG("capabilities: video=%s",
NS_ConvertUTF16toUTF8(v.contentType()).get());
for (const auto& scheme : v.encryptionSchemes()) {
EME_LOG("capabilities: video=%s, scheme=%s",
NS_ConvertUTF16toUTF8(v.contentType()).get(),
CryptoSchemeToString(scheme));
}
}
for (const auto& a : capabilities.audioCapabilities()) {
EME_LOG("capabilities: audio=%s",
NS_ConvertUTF16toUTF8(a.contentType()).get());
for (const auto& scheme : a.encryptionSchemes()) {
EME_LOG("capabilities: audio=%s, scheme=%s",
NS_ConvertUTF16toUTF8(a.contentType()).get(),
CryptoSchemeToString(scheme));
}
}
for (const auto& v : capabilities.encryptionSchemes()) {
EME_LOG("capabilities: encryptionScheme=%s",

View File

@@ -6,6 +6,7 @@
#include "WMFCDMProxy.h"
#include "MediaData.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
@@ -122,12 +123,16 @@ WMFCDMProxy::GenerateMFCDMMediaCapabilities(
EME_LOG("WMFCDMProxy::Init %p, robustness=%s", this,
NS_ConvertUTF16toUTF8(capabilities.mRobustness).get());
outCapabilites.AppendElement(MFCDMMediaCapability{
capabilities.mContentType, capabilities.mRobustness});
capabilities.mContentType,
{StringToCryptoScheme(capabilities.mEncryptionScheme)},
capabilities.mRobustness});
} else {
EME_LOG("WMFCDMProxy::Init %p, force to robustness=%s", this,
NS_ConvertUTF16toUTF8(*forcedRobustness).get());
outCapabilites.AppendElement(
MFCDMMediaCapability{capabilities.mContentType, *forcedRobustness});
outCapabilites.AppendElement(MFCDMMediaCapability{
capabilities.mContentType,
{StringToCryptoScheme(capabilities.mEncryptionScheme)},
*forcedRobustness});
}
}
return outCapabilites;

View File

@@ -848,27 +848,115 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
KeySystemConfig::EME_CODEC_VP9, KeySystemConfig::EME_CODEC_HEVC,
KeySystemConfig::EME_CODEC_AV1});
// Collect schemes supported by all video codecs.
static nsTArray<CryptoScheme> kSchemes({
CryptoScheme::Cenc,
CryptoScheme::Cbcs,
});
// Remember supported video codecs.
// It will be used when collecting audio codec and encryption scheme
// support.
// TODO : scheme part should be removed later
nsTArray<KeySystemConfig::EMECodecString> supportedVideoCodecs;
for (const auto& codec : kVideoCodecs) {
if (codec == KeySystemConfig::EME_CODEC_HEVC &&
!StaticPrefs::media_wmf_hevc_enabled()) {
continue;
if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
for (const auto& codec : kVideoCodecs) {
if (codec == KeySystemConfig::EME_CODEC_HEVC &&
!StaticPrefs::media_wmf_hevc_enabled()) {
continue;
}
CryptoSchemeSet supportedScheme;
for (const auto& scheme : kSchemes) {
nsAutoString additionalFeature(u"encryption-type=");
// If we don't specify 'encryption-iv-size', it would use 8 bytes IV as
// default [1]. If it's not supported, then we will try 16 bytes later.
// Since PlayReady 4.0 [2], 8 and 16 bytes IV are both supported. But
// We're not sure if Widevine supports both or not.
// [1]
// https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
// [2]
// https://learn.microsoft.com/en-us/playready/packaging/content-encryption-modes#initialization-vectors-ivs
if (scheme == CryptoScheme::Cenc) {
additionalFeature.AppendLiteral(u"cenc-clearlead,");
} else {
additionalFeature.AppendLiteral(u"cbcs-clearlead,");
}
bool rv = FactorySupports(factory, aKeySystem,
convertCodecToFourCC(codec), nsCString(""),
additionalFeature, isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 8 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
if (rv) {
supportedScheme += scheme;
break;
}
// Try 16 bytes IV.
additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
rv = FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
nsCString(""), additionalFeature,
isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
if (rv) {
supportedScheme += scheme;
break;
}
}
// Add a capability if supported scheme exists
if (!supportedScheme.isEmpty()) {
MFCDMMediaCapability* c =
aCapabilitiesOut.videoCapabilities().AppendElement();
c->contentType() = NS_ConvertUTF8toUTF16(codec);
c->robustness() =
GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
if (supportedScheme.contains(CryptoScheme::Cenc)) {
c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
}
if (supportedScheme.contains(CryptoScheme::Cbcs)) {
c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
}
supportedVideoCodecs.AppendElement(codec);
}
}
if (FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
KeySystemConfig::EMECodecString(""), nsString(u""),
isHardwareDecryption)) {
MFCDMMediaCapability* c =
aCapabilitiesOut.videoCapabilities().AppendElement();
c->contentType() = NS_ConvertUTF8toUTF16(codec);
c->robustness() =
GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
MFCDM_PARENT_SLOG("%s: +video:%s", __func__, codec.get());
supportedVideoCodecs.AppendElement(codec);
} else {
// Non clearlead situation for video codecs
for (const auto& codec : kVideoCodecs) {
if (codec == KeySystemConfig::EME_CODEC_HEVC &&
!StaticPrefs::media_wmf_hevc_enabled()) {
continue;
}
if (FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
KeySystemConfig::EMECodecString(""), nsString(u""),
isHardwareDecryption)) {
MFCDMMediaCapability* c =
aCapabilitiesOut.videoCapabilities().AppendElement();
c->contentType() = NS_ConvertUTF8toUTF16(codec);
c->robustness() =
GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
// 'If value is unspecified, default value of "cenc" is used.' See
// https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
// Check cbcs scheme support
if (FactorySupports(
factory, aKeySystem, convertCodecToFourCC(codec),
KeySystemConfig::EMECodecString(""),
nsString(u"encryption-type=cbcs,encryption-iv-size=16,"),
isHardwareDecryption)) {
c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
}
supportedVideoCodecs.AppendElement(codec);
}
}
}
if (supportedVideoCodecs.IsEmpty()) {
// Return a capabilities with no codec supported.
return;
@@ -894,6 +982,7 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
c->contentType() = NS_ConvertUTF8toUTF16(codec);
c->robustness() = GetRobustnessStringForKeySystem(
aKeySystem, false /* aIsHWSecure */, false /* isVideo */);
c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
MFCDM_PARENT_SLOG("%s: +audio:%s", __func__, codec.get());
}
}
@@ -923,66 +1012,6 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
MFCDM_PARENT_SLOG("%s: +scheme:cbcs", __func__);
}
// For key system requires clearlead, every codec needs to have clear support.
// If not, then we will remove the codec from supported codec.
if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
nsTArray<KeySystemConfig::EMECodecString> noClearLeadCodecs;
for (const auto& codec : supportedVideoCodecs) {
bool foundSupportedScheme = false;
for (const auto& scheme : aCapabilitiesOut.encryptionSchemes()) {
nsAutoString additionalFeature(u"encryption-type=");
// If we don't specify 'encryption-iv-size', it would use 8 bytes IV as
// default [1]. If it's not supported, then we will try 16 bytes later.
// Since PlayReady 4.0 [2], 8 and 16 bytes IV are both supported. But
// We're not sure if Widevine supports both or not.
// [1]
// https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
// [2]
// https://learn.microsoft.com/en-us/playready/packaging/content-encryption-modes#initialization-vectors-ivs
if (scheme == CryptoScheme::Cenc) {
additionalFeature.AppendLiteral(u"cenc-clearlead,");
} else {
additionalFeature.AppendLiteral(u"cbcs-clearlead,");
}
bool rv = FactorySupports(factory, aKeySystem,
convertCodecToFourCC(codec), nsCString(""),
additionalFeature, isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 8 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
if (rv) {
foundSupportedScheme = true;
break;
}
// Try 16 bytes IV.
additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
rv = FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
nsCString(""), additionalFeature,
isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
if (rv) {
foundSupportedScheme = true;
break;
}
}
// Failed on all schemes, add the codec to the list and remove it later.
if (!foundSupportedScheme) {
noClearLeadCodecs.AppendElement(codec);
}
}
for (const auto& codec : noClearLeadCodecs) {
MFCDM_PARENT_SLOG("%s: -video:%s", __func__, codec.get());
aCapabilitiesOut.videoCapabilities().RemoveElementsBy(
[&codec](const MFCDMMediaCapability& aCapbilities) {
return aCapbilities.contentType() == NS_ConvertUTF8toUTF16(codec);
});
supportedVideoCodecs.RemoveElement(codec);
}
}
// Only perform HDCP if necessary, "The hdcp query (item 4) has a
// computationally expensive first invocation cost". See
// https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex

View File

@@ -46,6 +46,7 @@ struct MFCDMKeyExpiration {
// For GetCapabilities()
struct MFCDMMediaCapability {
nsString contentType;
CryptoScheme[] encryptionSchemes;
nsString robustness;
};

View File

@@ -222,12 +222,18 @@ void UtilityAudioDecoderChild::GetKeySystemCapabilities(
EME_LOG("Received capabilities for %s",
NS_ConvertUTF16toUTF8(capabilities.keySystem()).get());
for (const auto& v : capabilities.videoCapabilities()) {
EME_LOG(" capabilities: video=%s",
NS_ConvertUTF16toUTF8(v.contentType()).get());
for (const auto& scheme : v.encryptionSchemes()) {
EME_LOG(" capabilities: video=%s, scheme=%s",
NS_ConvertUTF16toUTF8(v.contentType()).get(),
CryptoSchemeToString(scheme));
}
}
for (const auto& a : capabilities.audioCapabilities()) {
EME_LOG(" capabilities: audio=%s",
NS_ConvertUTF16toUTF8(a.contentType()).get());
for (const auto& scheme : a.encryptionSchemes()) {
EME_LOG(" capabilities: audio=%s, scheme=%s",
NS_ConvertUTF16toUTF8(a.contentType()).get(),
CryptoSchemeToString(scheme));
}
}
for (const auto& e : capabilities.encryptionSchemes()) {
EME_LOG(" capabilities: encryptionScheme=%s",