Commit d2362c2c authored by John Rummell's avatar John Rummell Committed by Commit Bot

[EME] Add ability to query for encryption scheme support

Update navigator.requestMediaKeySystemAccess() to support querying for
encryption scheme support. This is based on
https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.

BUG=657957
TEST=new layout and browser tests pass

Change-Id: I5ba39861bab673a61dd65c12fe82d6ba3bd63ddb
Reviewed-on: https://chromium-review.googlesource.com/959459Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarNate Chapin <japhet@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Commit-Queue: John Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567105}
parent d6dd162b
......@@ -61,9 +61,11 @@ const char kSuccessResult[] = "success";
const char kUnsupportedResult[] =
"Unsupported keySystem or supportedConfigurations.";
const char kUnexpectedResult[] = "unexpected result";
const char kTypeErrorResult[] = "TypeError";
#define EXPECT_SUCCESS(test) EXPECT_EQ(kSuccessResult, test)
#define EXPECT_UNSUPPORTED(test) EXPECT_EQ(kUnsupportedResult, test)
#define EXPECT_TYPEERROR(test) EXPECT_EQ(kTypeErrorResult, test)
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#define EXPECT_PROPRIETARY EXPECT_SUCCESS
......@@ -171,14 +173,16 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
return clear_key_exclusive_video_common_codecs_;
}
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
test_launcher_utils::RemoveCommandLineSwitch(
default_command_line, switches::kDisableComponentUpdate, command_line);
}
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
command_line->AppendSwitchASCII("enable-blink-features",
"EncryptedMediaEncryptionSchemeQuery");
}
void SetUpOnMainThread() override {
// Load the test page needed so that checkKeySystemWithMediaMimeType()
......@@ -201,29 +205,40 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
return content_type;
}
// Format: {contentType: |content_type|, robustness: |robustness|}, or
// {contentType: |content_type|} if |robustness| is null.
// Format: {contentType: |content_type|, encryptionScheme:
// |encryption_scheme|, robustness: |robustness|}. encryptionScheme and
// robustness will not be included if |encryption_scheme| or |robustness|
// is null, respectively.
static std::string MakeMediaCapability(const std::string& content_type,
const char* robustness) {
if (!robustness)
return base::StringPrintf("{contentType: '%s'}", content_type.c_str());
return base::StringPrintf("{contentType: '%s', robustness: '%s'}",
content_type.c_str(), robustness);
const char* robustness,
const char* encryption_scheme) {
std::string capability =
base::StringPrintf("{contentType: '%s'", content_type.c_str());
if (encryption_scheme) {
base::StringAppendF(&capability, ", encryptionScheme: '%s'",
encryption_scheme);
}
if (robustness) {
base::StringAppendF(&capability, ", robustness: '%s'", robustness);
}
base::StringAppendF(&capability, "}");
return capability;
}
static std::string MakeMediaCapabilities(const std::string& mime_type,
const CodecVector& codecs,
const char* robustness) {
const char* robustness,
const char* encryption_scheme) {
std::string capabilities("[");
if (codecs.empty()) {
capabilities += MakeMediaCapability(
MakeContentType(mime_type, std::string()), robustness);
capabilities +=
MakeMediaCapability(MakeContentType(mime_type, std::string()),
robustness, encryption_scheme);
} else {
for (auto codec : codecs) {
capabilities +=
MakeMediaCapability(MakeContentType(mime_type, codec), robustness) +
",";
capabilities += MakeMediaCapability(MakeContentType(mime_type, codec),
robustness, encryption_scheme) +
",";
}
// Remove trailing comma.
capabilities.erase(capabilities.length() - 1);
......@@ -238,6 +253,7 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
base::ASCIIToUTF16(kSuccessResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnsupportedResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnexpectedResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kTypeErrorResult));
EXPECT_TRUE(content::ExecuteScript(contents, command));
base::string16 result = title_watcher.WaitAndGetTitle();
return base::UTF16ToASCII(result);
......@@ -265,7 +281,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
const std::string& mime_type,
const CodecVector& codecs,
SessionType session_type = SessionType::kTemporary,
const char* robustness = nullptr) {
const char* robustness = nullptr,
const char* encryption_scheme = nullptr) {
// Choose the appropriate init data type for the sub type.
size_t pos = mime_type.find('/');
DCHECK(pos > 0);
......@@ -280,7 +297,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
bool is_audio = mime_type.compare(0, 5, "audio") == 0;
DCHECK(is_audio || mime_type.compare(0, 5, "video") == 0);
auto capabilities = MakeMediaCapabilities(mime_type, codecs, robustness);
auto capabilities =
MakeMediaCapabilities(mime_type, codecs, robustness, encryption_scheme);
auto audio_capabilities = is_audio ? capabilities : "null";
auto video_capabilities = !is_audio ? capabilities : "null";
auto session_type_string = GetSessionTypeString(session_type);
......@@ -315,6 +333,20 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
robustness);
}
std::string IsAudioEncryptionSchemeSupported(const std::string& key_system,
const char* encryption_scheme) {
return IsSupportedByKeySystem(key_system, kAudioWebMMimeType,
audio_webm_codecs(), SessionType::kTemporary,
nullptr, encryption_scheme);
}
std::string IsVideoEncryptionSchemeSupported(const std::string& key_system,
const char* encryption_scheme) {
return IsSupportedByKeySystem(key_system, kVideoWebMMimeType,
video_webm_codecs(), SessionType::kTemporary,
nullptr, encryption_scheme);
}
private:
const CodecVector no_codecs_;
CodecVector audio_webm_codecs_;
......@@ -587,6 +619,24 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Robustness) {
EXPECT_UNSUPPORTED(IsAudioRobustnessSupported(kClearKey, "SW_SECURE_CRYPTO"));
}
IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest,
EncryptionScheme) {
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, nullptr));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, "cenc"));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, "cbcs"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, nullptr));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, "cenc"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, "cbcs"));
// Invalid encryption schemes will be rejected. However, invalid values
// generate a TypeError (The provided value '...' is not a valid enum value
// of type EncryptionScheme), which is not handled by the test page.
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kClearKey, "Invalid"));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kClearKey, "Invalid"));
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kClearKey, ""));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kClearKey, ""));
}
//
// External Clear Key
//
......@@ -774,6 +824,26 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
IsAudioRobustnessSupported(kExternalClearKey, "SW_SECURE_CRYPTO"));
}
IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
EncryptionScheme) {
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, nullptr));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, "cenc"));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, "cbcs"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, nullptr));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, "cenc"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, "cbcs"));
// Invalid encryption schemes will be rejected. However, invalid values
// generate a TypeError (The provided value '...' is not a valid enum value
// of type EncryptionScheme), which is not handled by the test page.
EXPECT_TYPEERROR(
IsAudioEncryptionSchemeSupported(kExternalClearKey, "Invalid"));
EXPECT_TYPEERROR(
IsVideoEncryptionSchemeSupported(kExternalClearKey, "Invalid"));
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kExternalClearKey, ""));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kExternalClearKey, ""));
}
// External Clear Key is disabled by default.
IN_PROC_BROWSER_TEST_F(
EncryptedMediaSupportedTypesExternalClearKeyNotEnabledTest,
......
......@@ -29,6 +29,8 @@ namespace media {
using EmeFeatureRequirement =
blink::WebMediaKeySystemConfiguration::Requirement;
using EmeEncryptionScheme =
blink::WebMediaKeySystemMediaCapability::EncryptionScheme;
namespace {
......@@ -360,6 +362,31 @@ bool KeySystemConfigSelector::IsSupportedContentType(
return true;
}
bool KeySystemConfigSelector::IsSupportedEncryptionScheme(
const std::string& key_system,
const EmeEncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
// "A missing or null value indicates that any encryption scheme is
// acceptable."
// "Even if the application does not specify an encryption scheme,
// MediaKeySystemAccess.getConfiguration() must fill in a supported
// value."
// As Chrome has always supported 'cenc', assume this if encryption
// scheme is not specified.
case EmeEncryptionScheme::kNotSpecified:
case EmeEncryptionScheme::kCenc:
return key_systems_->IsEncryptionSchemeSupported(key_system,
EncryptionMode::kCenc);
case EmeEncryptionScheme::kCbcs:
return key_systems_->IsEncryptionSchemeSupported(key_system,
EncryptionMode::kCbcs);
}
NOTREACHED();
return false;
}
bool KeySystemConfigSelector::GetSupportedCapabilities(
const std::string& key_system,
EmeMediaType media_type,
......@@ -427,6 +454,14 @@ bool KeySystemConfigSelector::GetSupportedCapabilities(
continue;
}
// Check for encryption scheme support.
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
if (!IsSupportedEncryptionScheme(key_system,
capability.encryption_scheme)) {
DVLOG(3) << "Encryption scheme is not supported.";
continue;
}
// 3.13.1. Add requested media capability to supported media capabilities.
supported_media_capabilities->push_back(capability);
......
......@@ -15,12 +15,12 @@
#include "base/memory/weak_ptr.h"
#include "media/base/eme_constants.h"
#include "media/blink/media_blink_export.h"
#include "third_party/blink/public/platform/web_media_key_system_media_capability.h"
#include "third_party/blink/public/platform/web_vector.h"
namespace blink {
struct WebMediaKeySystemConfiguration;
struct WebMediaKeySystemMediaCapability;
class WebString;
} // namespace blink
......@@ -91,6 +91,11 @@ class MEDIA_BLINK_EXPORT KeySystemConfigSelector {
const std::string& codecs,
ConfigState* config_state);
bool IsSupportedEncryptionScheme(
const std::string& key_system,
const blink::WebMediaKeySystemMediaCapability::EncryptionScheme
encryption_scheme);
const KeySystems* key_systems_;
MediaPermission* media_permission_;
......
......@@ -29,6 +29,7 @@ using blink::WebMediaKeySystemConfiguration;
using blink::WebMediaKeySystemMediaCapability;
using blink::WebString;
using MediaKeysRequirement = WebMediaKeySystemConfiguration::Requirement;
using EncryptionScheme = WebMediaKeySystemMediaCapability::EncryptionScheme;
// Key system strings. Clear Key support is hardcoded in KeySystemConfigSelector
// so kClearKeyKeySystem is the real key system string. The rest key system
......@@ -70,6 +71,26 @@ const char kExtendedVideoCodecStripped[] = "video_extended_codec";
// in IsSupportedMediaType() when |use_aes_decryptor| is true.
const char kUnsupportedByAesDecryptorCodec[] = "unsupported_by_aes_decryptor";
// Encryption schemes. For testing 'cenc' is supported, while 'cbcs' is not.
// Note that WebMediaKeySystemMediaCapability defaults to kNotSpecified,
// which is treated as 'cenc' by KeySystemConfigSelector.
constexpr EncryptionScheme kSupportedEncryptionScheme = EncryptionScheme::kCenc;
constexpr EncryptionScheme kUnSupportedEncryptionScheme =
EncryptionScheme::kCbcs;
EncryptionMode ConvertEncryptionScheme(EncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
case EncryptionScheme::kNotSpecified:
case EncryptionScheme::kCenc:
return EncryptionMode::kCenc;
case EncryptionScheme::kCbcs:
return EncryptionMode::kCbcs;
}
NOTREACHED();
return EncryptionMode::kUnencrypted;
}
WebString MakeCodecs(const std::string& a, const std::string& b) {
return WebString::FromUTF8(a + "," + b);
}
......@@ -196,9 +217,8 @@ class FakeKeySystems : public KeySystems {
bool IsEncryptionSchemeSupported(
const std::string& key_system,
EncryptionMode encryption_scheme) const override {
// TODO(crbug.com/658026): Implement this once value passed from blink.
NOTREACHED();
return false;
return encryption_scheme ==
ConvertEncryptionScheme(kSupportedEncryptionScheme);
}
EmeConfigRule GetContentTypeConfigRule(
......@@ -1043,6 +1063,39 @@ TEST_F(KeySystemConfigSelectorTest,
EXPECT_EQ(MediaKeysRequirement::kNotAllowed, config_.distinctive_identifier);
}
TEST_F(KeySystemConfigSelectorTest,
VideoCapabilities_EncryptionScheme_Supported) {
std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1);
video_capabilities[0].content_type = "a";
video_capabilities[0].mime_type = kSupportedVideoContainer;
video_capabilities[0].codecs = kSupportedVideoCodec;
video_capabilities[0].encryption_scheme = kSupportedEncryptionScheme;
blink::WebMediaKeySystemConfiguration config = EmptyConfiguration();
config.video_capabilities = video_capabilities;
configs_.push_back(config);
SelectConfigReturnsConfig();
ASSERT_EQ(1u, config_.video_capabilities.size());
EXPECT_EQ(kSupportedEncryptionScheme,
config_.video_capabilities[0].encryption_scheme);
}
TEST_F(KeySystemConfigSelectorTest,
VideoCapabilities_EncryptionScheme_Unsupported) {
std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1);
video_capabilities[0].content_type = "a";
video_capabilities[0].mime_type = kSupportedVideoContainer;
video_capabilities[0].codecs = kSupportedVideoCodec;
video_capabilities[0].encryption_scheme = kUnSupportedEncryptionScheme;
blink::WebMediaKeySystemConfiguration config = EmptyConfiguration();
config.video_capabilities = video_capabilities;
configs_.push_back(config);
SelectConfigReturnsError();
}
// --- HW Secure Codecs and Robustness ---
TEST_F(KeySystemConfigSelectorTest, HwSecureCodec_RequireHwSecureCodec) {
......
......@@ -4,7 +4,7 @@
// Since promises run asynchronously, use the pages title to keep track
// of the result.
function setResultInTitle(title) {
if (title == "" || title == "success" ||
if (title == "" || title == "success" || title == "TypeError" ||
title == "Unsupported keySystem or supportedConfigurations.") {
document.title = title;
} else {
......@@ -82,7 +82,9 @@
}
setResultInTitle("success");
})
.catch(function(err) { setResultInTitle(err.message); });
.catch(function(err) {
setResultInTitle((err.name == 'TypeError') ? err.name : err.message);
});
};
function checkKeySystemWithMediaMimeType(keySystem, initDataType,
......
<!DOCTYPE html>
<html>
<head>
<title>Test Encryption Scheme</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
// Checks that calling navigator.requestMediaKeySystemAccess()
// with |configuration| selects a configuration where the first
// entry in audioCapabilities and videoCapabilities contains
// encryptionScheme of |expectedAudioScheme| or
// |expectedVideoScheme|, respectively. If |expectedAudioScheme| or
// |expectedVideoScheme| is undefined, then no corresponding
// capability should have been selected.
function expectSupport(configuration, expectedAudioScheme, expectedVideoScheme, testName) {
promise_test(async test => {
var access = await navigator.requestMediaKeySystemAccess('org.w3.clearkey', configuration);
var result = access.getConfiguration();
if (result.audioCapabilities.length > 0) {
assert_equals(result.audioCapabilities[0].encryptionScheme, expectedAudioScheme, 'audio scheme');
} else {
assert_equals(undefined, expectedAudioScheme, 'expected audio scheme missing');
}
if (result.videoCapabilities.length > 0) {
assert_equals(result.videoCapabilities[0].encryptionScheme, expectedVideoScheme, 'video scheme');
} else {
assert_equals(undefined, expectedVideoScheme, 'expected video scheme missing');
}
}, testName);
}
// Verifies that calling navigator.requestMediaKeySystemAccess()
// with |configuration| throws a TypeError.
function expectTypeError(configuration, testName) {
promise_test(async test => {
try {
await navigator.requestMediaKeySystemAccess('org.w3.clearkey', configuration);
assert_unreached('Unexpected requestMediaKeySystemAccess() success.');
} catch(error) {
// Error must be a TypeError.
assert_equals(error.name, 'TypeError', 'unexpected error');
}
}, testName);
}
// Creates an audioCapabilities sequence with one entry, including
// |encryption_scheme| if specified.
function getAudioCapabilities(encryptionScheme) {
var capability = [{contentType: 'audio/webm; codecs="opus"'}];
if (encryptionScheme !== undefined) {
capability[0].encryptionScheme = encryptionScheme;
}
return capability;
}
// Creates an videoCapabilities sequence with one entry, including
// |encryption_scheme| if specified.
function getVideoCapabilities(encryptionScheme) {
var capability = [{contentType: 'video/webm; codecs="vp8"'}];
if (encryptionScheme !== undefined) {
capability[0].encryptionScheme = encryptionScheme;
}
return capability;
}
// Takes a set of capabilities (passed as arguments) and combines
// them into a MediaKeySystemConfiguration dictionary. If multiple
// arguments define the same property, the last one overrides the
// previous entry.
function buildConfiguration() {
var configuration = { initDataTypes : [ 'webm' ] };
for (var i = 0; i < arguments.length; i++) {
for (var arg in arguments[i]) {
if (arg == 'audio') {
configuration['audioCapabilities'] =
getAudioCapabilities(arguments[i][arg]);
} else if (arg == 'video') {
configuration['videoCapabilities'] =
getVideoCapabilities(arguments[i][arg]);
}
}
}
return configuration;
}
// Test "cenc".
expectSupport([buildConfiguration({audio: 'cenc'})],
'cenc', undefined, 'Audio supports cenc');
expectSupport([buildConfiguration({video: 'cenc'})],
undefined, 'cenc', 'Video supports cenc');
expectSupport([buildConfiguration({audio: 'cenc'}, {video: 'cenc'})],
'cenc', 'cenc', 'Audio and Video supports cenc');
// Test "cbcs".
expectSupport([buildConfiguration({audio: 'cbcs'})],
'cbcs', undefined, 'Audio supports cbcs');
expectSupport([buildConfiguration({video: 'cbcs'})],
undefined, 'cbcs', 'Video supports cbcs');
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: 'cbcs'})],
'cbcs', 'cbcs', 'Audio and Video supports cbcs');
// Test "invalid", which should fail.
expectTypeError([buildConfiguration({audio: 'invalid'})],
'Audio fails with invalid scheme');
expectTypeError([buildConfiguration({video: 'invalid'})],
'Video fails with invalid scheme');
expectTypeError([buildConfiguration({audio: 'invalid'}, {video: 'invalid'})],
'Audio and Video fails with invalid scheme');
expectTypeError([buildConfiguration({audio: 'cenc'}, {video: 'invalid'})],
'Audio valid, Video invalid scheme');
expectTypeError([buildConfiguration({audio: 'invalid'}, {video: 'cbcs'})],
'Audio invalid scheme, Video valid');
// Test without encryptionScheme, which should default to "cenc".
expectSupport([buildConfiguration({audio: null})],
'cenc', undefined, 'Audio handles missing encryptionScheme');
expectSupport([buildConfiguration({video: null})],
undefined, 'cenc', 'Video handles missing encryptionScheme');
expectSupport([buildConfiguration({audio: null}, {video: null})],
'cenc', 'cenc', 'Audio and Video handles missing encryptionScheme');
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: null})],
'cbcs', 'cenc', 'Audio valid and Video missing');
expectSupport([buildConfiguration({audio: null}, {video: 'cbcs'})],
'cenc', 'cbcs', 'Audio missing and Video valid');
// Test with "", which is invalid.
expectTypeError([buildConfiguration({audio: ''})],
'Audio handles empty encryptionScheme');
expectTypeError([buildConfiguration({video: ''})],
'Video handles empty encryptionScheme');
expectTypeError([buildConfiguration({audio: ''}, {video: ''})],
'Audio and Video handles empty encryptionScheme');
expectTypeError([buildConfiguration({audio: 'cbcs'}, {video: ''})],
'Audio valid and Video empty');
expectTypeError([buildConfiguration({audio: ''}, {video: 'cbcs'})],
'Audio empty and Video valid');
// Test mixed combinations.
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: 'cenc'})],
'cbcs', 'cenc', 'Audio supports cbcs and Video supports cenc');
expectSupport([buildConfiguration({audio: 'cenc'}, {video: 'cbcs'})],
'cenc', 'cbcs', 'Audio supports cenc and Video supports cbcs');
// Test multiple configurations.
expectSupport([buildConfiguration({audio: 'cenc'}),
buildConfiguration({audio: 'cbcs'})],
'cenc', undefined, 'Multiple audio entries supports cenc');
expectSupport([buildConfiguration({audio: 'cbcs'}),
buildConfiguration({audio: 'cenc'})],
'cbcs', undefined, 'Multiple audio entries supports cbcs');
expectSupport([buildConfiguration({video: 'cenc'}),
buildConfiguration({video: 'cbcs'})],
undefined, 'cenc', 'Multiple video entries supports cenc');
expectSupport([buildConfiguration({video: 'cbcs'}),
buildConfiguration({video: 'cenc'})],
undefined, 'cbcs', 'Multiple video entries supports cbcs');
// Test mixed multiple configurations.
expectSupport([buildConfiguration({audio: 'cenc'}),
buildConfiguration({video: 'cbcs'})],
'cenc', undefined, 'Mixed entries supports cenc');
expectSupport([buildConfiguration({video: 'cbcs'}),
buildConfiguration({audio: 'cenc'})],
undefined, 'cbcs', 'Mixed entries supports cbcs');
// Test invalid values.
expectTypeError([buildConfiguration({audio: 3})],
'Audio fails with numeric scheme');
expectTypeError([buildConfiguration({audio: [1, 2, 3]})],
'Audio fails with array scheme');
expectTypeError([buildConfiguration({audio: {test: 'test'}})],
'Audio fails with object scheme');
</script>
</body>
</html>
......@@ -10,12 +10,19 @@
namespace blink {
struct WebMediaKeySystemMediaCapability {
enum class EncryptionScheme {
kNotSpecified,
kCenc,
kCbcs,
};
WebMediaKeySystemMediaCapability() = default;
WebString content_type;
WebString mime_type;
WebString codecs;
WebString robustness;
EncryptionScheme encryption_scheme = EncryptionScheme::kNotSpecified;
};
} // namespace blink
......
......@@ -73,6 +73,28 @@ static Vector<String> ConvertInitDataTypes(
return result;
}
static String ConvertEncryptionScheme(
WebMediaKeySystemMediaCapability::EncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
// "A missing or null value indicates that any encryption scheme is
// acceptable."
// "Even if the application does not specify an encryption scheme,
// MediaKeySystemAccess.getConfiguration() must fill in a supported
// value."
// As Chrome has always supported 'cenc', assume this if encryption
// scheme is not specified.
case WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified:
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc:
return "cenc";
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs:
return "cbcs";
}
NOTREACHED();
return "";
}
static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities(
const WebVector<WebMediaKeySystemMediaCapability>& capabilities) {
HeapVector<MediaKeySystemMediaCapability> result(capabilities.size());
......@@ -80,6 +102,8 @@ static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities(
MediaKeySystemMediaCapability capability;
capability.setContentType(capabilities[i].content_type);
capability.setRobustness(capabilities[i].robustness);
capability.setEncryptionScheme(
ConvertEncryptionScheme(capabilities[i].encryption_scheme));
result[i] = capability;
}
return result;
......
......@@ -4,7 +4,10 @@
// https://w3c.github.io/encrypted-media/#idl-def-MediaKeySystemMediaCapability
enum EncryptionScheme { "cenc", "cbcs" };
dictionary MediaKeySystemMediaCapability {
DOMString contentType = "";
DOMString robustness = "";
[RuntimeEnabled=EncryptedMediaEncryptionSchemeQuery] EncryptionScheme? encryptionScheme = null;
};
......@@ -50,6 +50,17 @@ static WebVector<WebEncryptedMediaInitDataType> ConvertInitDataTypes(
return result;
}
static WebMediaKeySystemMediaCapability::EncryptionScheme
ConvertEncryptionScheme(const String& encryption_scheme) {
if (encryption_scheme == "cenc")
return WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc;
if (encryption_scheme == "cbcs")
return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs;
NOTREACHED();
return WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
}
static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
const HeapVector<MediaKeySystemMediaCapability>& capabilities) {
WebVector<WebMediaKeySystemMediaCapability> result(capabilities.size());
......@@ -70,6 +81,17 @@ static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
result[i].codecs = type.ParameterValueForName("codecs");
}
result[i].robustness = capabilities[i].robustness();
// From
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md
// "Asking for "any" encryption scheme is unrealistic. Defining null as
// "any scheme" is convenient for backward compatibility, though.
// Applications which ignore this feature by leaving encryptionScheme null
// get the same user agent behavior they did before this feature existed."
result[i].encryption_scheme =
capabilities[i].hasEncryptionScheme()
? ConvertEncryptionScheme(capabilities[i].encryptionScheme())
: WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
}
return result;
}
......
......@@ -399,6 +399,10 @@
name: "EmbedderCSPEnforcement",
status: "stable",
},
{
name: "EncryptedMediaEncryptionSchemeQuery",
status: "test",
},
{
name: "EncryptedMediaHdcpPolicyCheck",
origin_trial_feature_name: "EncryptedMediaHdcpPolicyCheck",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment