Support Dual PSSHs
Merge from Widevine repo of http://go/wvgerrit/48842 In order to work around a limitation of some versions of OEMCrypto, the packager is going to start generating files with multiple Widevine PSSH boxes. For backwards-compatibility, the first PSSH will be a SINGLE-type PSSH while the ENTITLED_KEYS-type PSSH (if any) will come later. In order to use entitlement licenses, then, the CDM needs to change how it selects PSSHs from the init data blob. Previously, the CDM always took the first Widevine PSSH it found. Now, it must find all the Widevine PSSHs and select the appropriate PSSH for the OEMCrypto implementation. ENTITLTED_KEYS will be used on OEC v14 and later, if available, while SINGLE will be preferred on earlier OEMCrypto versions. As a side-effect of this, the CDM is now stricter about what PSSH payloads it will accept. Previously, it would blindly accept the payload of any PSSH where the wrapper was not malformed. Now, it sometimes has to actually parse the payload, and therefore PSSHs that have corrupted payloads will be rejected. This affected a few unit tests which used PSSHs that were malformed. These tests have been updated to use PSSHs that do not fail to parse. Bug: 78142219 Test: CE CDM Unit Tests Test: Android Unit Tests Test: Android Google Play & Netflix Test: tested as part of http://go/ag/4674759 Change-Id: Ia70d627a914299bfbae84b4cb46f100dc5c7a501
This commit is contained in:
@@ -17,7 +17,8 @@ class WvCdmEngineTest;
|
||||
class InitializationData {
|
||||
public:
|
||||
InitializationData(const std::string& type = std::string(),
|
||||
const CdmInitData& data = CdmInitData());
|
||||
const CdmInitData& data = CdmInitData(),
|
||||
const std::string& oec_version = std::string());
|
||||
|
||||
bool is_supported() const { return is_cenc_ || is_webm_ || is_hls_; }
|
||||
bool is_cenc() const { return is_cenc_; }
|
||||
@@ -36,9 +37,15 @@ class InitializationData {
|
||||
std::vector<video_widevine::WrappedKey> ExtractWrappedKeys() const;
|
||||
|
||||
private:
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
|
||||
bool SelectWidevinePssh(const CdmInitData& init_data,
|
||||
bool prefer_entitlements,
|
||||
CdmInitData* output);
|
||||
// Helpers used by SelectWidevinePssh().
|
||||
bool ExtractWidevinePsshs(const CdmInitData& init_data,
|
||||
std::vector<CdmInitData>* psshs);
|
||||
bool ExtractWidevinePsshData(const uint8_t* data,
|
||||
size_t length,
|
||||
CdmInitData* output);
|
||||
|
||||
bool ExtractHlsAttributes(const std::string& attribute_list,
|
||||
CdmHlsMethod* method, std::vector<uint8_t>* iv,
|
||||
@@ -58,6 +65,9 @@ class InitializationData {
|
||||
static std::vector<std::string> ExtractKeyFormatVersions(
|
||||
const std::string& key_format_versions);
|
||||
|
||||
static bool DetectEntitlementPreference(
|
||||
const std::string& oec_version_string);
|
||||
|
||||
// For testing only:
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(HlsAttributeExtractionTest, ExtractAttribute);
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "jsmn.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace {
|
||||
const char kKeyFormatVersionsSeparator = '/';
|
||||
@@ -27,6 +30,12 @@ const std::string kKeyIds = "key_ids";
|
||||
|
||||
// Being conservative, usually we expect 6 + number of Key Ids
|
||||
const int kDefaultNumJsonTokens = 128;
|
||||
|
||||
// Widevine's registered system ID.
|
||||
const uint8_t kWidevineSystemId[] = {
|
||||
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
|
||||
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -35,9 +44,13 @@ namespace wvcdm {
|
||||
using video_widevine::WidevinePsshData;
|
||||
using video_widevine::WidevinePsshData_Algorithm;
|
||||
using video_widevine::WidevinePsshData_Algorithm_AESCTR;
|
||||
using video_widevine::WidevinePsshData_Type;
|
||||
using video_widevine::WidevinePsshData_Type_ENTITLED_KEY;
|
||||
using video_widevine::WidevinePsshData_Type_SINGLE;
|
||||
|
||||
InitializationData::InitializationData(const std::string& type,
|
||||
const CdmInitData& data)
|
||||
const CdmInitData& data,
|
||||
const std::string& oec_version)
|
||||
: type_(type),
|
||||
is_cenc_(false),
|
||||
is_hls_(false),
|
||||
@@ -55,7 +68,12 @@ InitializationData::InitializationData(const std::string& type,
|
||||
|
||||
if (is_supported()) {
|
||||
if (is_cenc()) {
|
||||
ExtractWidevinePssh(data, &data_);
|
||||
bool oec_prefers_entitlements = DetectEntitlementPreference(oec_version);
|
||||
if (!SelectWidevinePssh(data, oec_prefers_entitlements, &data_)) {
|
||||
LOGE(
|
||||
"InitializationData: Unable to select a supported Widevine PSSH "
|
||||
"from the init data.");
|
||||
}
|
||||
} else if (is_webm()) {
|
||||
data_ = data;
|
||||
} else if (is_hls()) {
|
||||
@@ -97,158 +115,263 @@ std::vector<video_widevine::WrappedKey> InitializationData::ExtractWrappedKeys()
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
CdmInitData* output) {
|
||||
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
|
||||
init_data.length());
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the Widevine
|
||||
// PSSH. The blob may have multiple Widevine PSSHs. Devices that prefer
|
||||
// entitlements should choose the |ENTITLED_KEY| PSSH while other devices should
|
||||
// stick to the |SINGLE| PSSH.
|
||||
bool InitializationData::SelectWidevinePssh(const CdmInitData& init_data,
|
||||
bool prefer_entitlements,
|
||||
CdmInitData* output) {
|
||||
// Extract the data payloads from the Widevine PSSHs.
|
||||
std::vector<CdmInitData> pssh_payloads;
|
||||
if (!ExtractWidevinePsshs(init_data, &pssh_payloads)) {
|
||||
LOGE(
|
||||
"InitializationData::SelectWidevinePssh: Unable to parse concatenated "
|
||||
"PSSH boxes.");
|
||||
return false;
|
||||
}
|
||||
if (pssh_payloads.empty()) {
|
||||
LOGE(
|
||||
"InitializationData::SelectWidevinePssh: The concatenated PSSH boxes "
|
||||
"could be parsed, but no Widevine PSSH was found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Widevine's registered system ID.
|
||||
static const uint8_t kWidevineSystemId[] = {
|
||||
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
|
||||
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
|
||||
};
|
||||
// If there are multiple PSSHs to choose from and this device prefers
|
||||
// entitlements, find the first |ENTITLED_KEY| PSSH, if present, and
|
||||
// select it.
|
||||
if (prefer_entitlements && pssh_payloads.size() > 1) {
|
||||
for (size_t i = 0; i < pssh_payloads.size(); ++i) {
|
||||
WidevinePsshData pssh;
|
||||
if (!pssh.ParseFromString(pssh_payloads[i])) {
|
||||
LOGE(
|
||||
"InitializationData::SelectWidevinePssh: Unable to parse PSSH data "
|
||||
"%lu into a protobuf.", i);
|
||||
continue;
|
||||
}
|
||||
if (pssh.type() == WidevinePsshData_Type_ENTITLED_KEY) {
|
||||
*output = pssh_payloads[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Either there is only one PSSH, this device does not prefer entitlements,
|
||||
// or no entitlement PSSH was found. Regardless of how we got here, select the
|
||||
// first PSSH, which should be a |SINGLE| PSSH.
|
||||
*output = pssh_payloads[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// one PSSH box consists of:
|
||||
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
|
||||
// 4 byte atom type, "pssh".
|
||||
// (optional, if size == 1) 8 byte size of the atom, inclusive.
|
||||
// 1 byte version, value 0 or 1. (skip if larger.)
|
||||
// 3 byte flags, value 0. (ignored.)
|
||||
// 16 byte system id.
|
||||
// (optional, if version == 1) 4 byte key ID count. (K)
|
||||
// (optional, if version == 1) K * 16 byte key ID.
|
||||
// 4 byte size of PSSH data, exclusive. (N)
|
||||
// N byte PSSH data.
|
||||
|
||||
bool InitializationData::ExtractWidevinePsshs(
|
||||
const CdmInitData& init_data, std::vector<CdmInitData>* psshs) {
|
||||
if (psshs == NULL) {
|
||||
LOGE("InitializationData::ExtractWidevinePsshs: NULL psshs parameter");
|
||||
return false;
|
||||
}
|
||||
psshs->clear();
|
||||
psshs->reserve(2); // We expect 1 or 2 Widevine PSSHs
|
||||
|
||||
const uint8_t* data_start = reinterpret_cast<const uint8_t*>(init_data.data());
|
||||
BufferReader reader(data_start, init_data.length());
|
||||
|
||||
// one PSSH box consists of:
|
||||
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
|
||||
// 4 byte atom type, "pssh".
|
||||
// (optional, if size == 1) 8 byte size of the atom, inclusive.
|
||||
// 1 byte version, value 0 or 1. (skip if larger.)
|
||||
// 3 byte flags, value 0. (ignored.)
|
||||
// 16 byte system id.
|
||||
// (optional, if version == 1) 4 byte key ID count. (K)
|
||||
// (optional, if version == 1) K * 16 byte key ID.
|
||||
// 4 byte size of PSSH data, exclusive. (N)
|
||||
// N byte PSSH data.
|
||||
while (!reader.IsEOF()) {
|
||||
size_t start_pos = reader.pos();
|
||||
const size_t start_pos = reader.pos();
|
||||
|
||||
// atom size, used for skipping.
|
||||
// Atom size. Used for bounding the inner reader and knowing how far to skip
|
||||
// forward after parsing this PSSH.
|
||||
uint64_t size;
|
||||
if (!reader.Read4Into8(&size)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom size.");
|
||||
return false;
|
||||
"InitializationData::ExtractWidevinePsshs: Unable to read the "
|
||||
"32-bit atom size.");
|
||||
return false; // We cannot continue reading safely. Abort.
|
||||
}
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
|
||||
// Skip the atom type for now.
|
||||
if (!reader.SkipBytes(4)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom type.");
|
||||
return false;
|
||||
"InitializationData::ExtractWidevinePsshs: Unable to skip the "
|
||||
"atom type.");
|
||||
return false; // We cannot continue reading safely. Abort.
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
// An "atom size" of 1 means the real atom size is a 64-bit number stored
|
||||
// after the atom type.
|
||||
if (!reader.Read8(&size)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read 64-bit "
|
||||
"atom size.");
|
||||
return false;
|
||||
"InitializationData::ExtractWidevinePsshs: Unable to read the "
|
||||
"64-bit atom size.");
|
||||
return false; // We cannot continue reading safely. Abort.
|
||||
}
|
||||
} else if (size == 0) {
|
||||
// An "atom size" of 0 means the atom takes up the rest of the buffer.
|
||||
size = reader.size() - start_pos;
|
||||
}
|
||||
|
||||
// "pssh"
|
||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
||||
const size_t bytes_read = reader.pos() - start_pos;
|
||||
const size_t bytes_remaining = size - bytes_read;
|
||||
|
||||
if (!reader.HasBytes(bytes_remaining)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: PSSH literal not present.");
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
"InitializationData::ExtractWidevinePsshs: Invalid atom size. The "
|
||||
"atom claims to be larger than the remaining init data.");
|
||||
return false; // We cannot continue reading safely. Abort.
|
||||
}
|
||||
|
||||
// version
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
// Use ExtractWidevinePsshData() to check if this atom is a Widevine PSSH
|
||||
// and, if so, add its data to the list of PSSHs.
|
||||
CdmInitData data;
|
||||
if (ExtractWidevinePsshData(data_start + start_pos, size, &data)) {
|
||||
psshs->push_back(data);
|
||||
}
|
||||
|
||||
// Skip past the rest of the atom.
|
||||
if (!reader.SkipBytes(bytes_remaining)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH "
|
||||
"version.");
|
||||
return false;
|
||||
"InitializationData::LocateWidevinePsshOffsets: Unable to skip the "
|
||||
"rest of the atom.");
|
||||
return false; // We cannot continue reading safely. Abort.
|
||||
}
|
||||
|
||||
if (version > 1) {
|
||||
// unrecognized version - skip.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// flags
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the PSSH "
|
||||
"flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read system ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||
// skip non-Widevine PSSH boxes.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Skipping non-Widevine "
|
||||
"PSSH.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (version == 1) {
|
||||
// v1 has additional fields for key IDs. We can skip them.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read num key "
|
||||
"IDs.");
|
||||
return false;
|
||||
}
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// size of PSSH data
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data "
|
||||
"size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// we did not find a matching record
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checks if the given reader contains a Widevine PSSH. If so, it extracts the
|
||||
// data from the box. Returns true if a PSSH was extracted, false if there was
|
||||
// an error or if the data is not a Widevine PSSH.
|
||||
bool InitializationData::ExtractWidevinePsshData(
|
||||
const uint8_t* data, size_t length, CdmInitData* output) {
|
||||
BufferReader reader(data, length);
|
||||
|
||||
// Read the 32-bit size only so we can check if we need to expect a 64-bit
|
||||
// size.
|
||||
uint64_t size_32;
|
||||
if (!reader.Read4Into8(&size_32)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the "
|
||||
"32-bit atom size.");
|
||||
return false;
|
||||
}
|
||||
const bool has_size_64 = (size_32 == 1);
|
||||
|
||||
// Read the atom type and check that it is "pssh".
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the atom "
|
||||
"type.");
|
||||
return false;
|
||||
}
|
||||
if (memcmp(&atom_type[0], "pssh", 4) != 0) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Atom type is not PSSH.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is a 64-bit size, skip it.
|
||||
if (has_size_64) {
|
||||
if (!reader.SkipBytes(8)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to skip the "
|
||||
"64-bit atom size.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the version number and abort if it is not one we can handle.
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the PSSH "
|
||||
"version.");
|
||||
return false;
|
||||
}
|
||||
if (version > 1) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unrecognized PSSH "
|
||||
"version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the flags.
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to skip the PSSH "
|
||||
"flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the System ID and validate that it is the Widevine System ID.
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the "
|
||||
"system ID.");
|
||||
return false;
|
||||
}
|
||||
if (memcmp(&system_id[0],
|
||||
kWidevineSystemId,
|
||||
sizeof(kWidevineSystemId)) != 0) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Found a non-Widevine "
|
||||
"PSSH.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the key ID fields, if present.
|
||||
if (version == 1) {
|
||||
// Read the number of key IDs so we know how far to skip ahead.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the "
|
||||
"number of key IDs.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the key IDs.
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to skip the key "
|
||||
"IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the size of the PSSH data.
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the PSSH "
|
||||
"data size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the PSSH data.
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePsshData: Unable to read the PSSH "
|
||||
"data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse an EXT-X-KEY tag attribute list. Verify that Widevine supports it
|
||||
@@ -605,4 +728,18 @@ std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
||||
return versions;
|
||||
}
|
||||
|
||||
bool InitializationData::DetectEntitlementPreference(
|
||||
const std::string& oec_version_string) {
|
||||
if (oec_version_string.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t oec_version_int = 0;
|
||||
std::istringstream parse_int;
|
||||
parse_int.str(oec_version_string);
|
||||
parse_int >> oec_version_int;
|
||||
|
||||
return oec_version_int >= 14;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "file_store.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -23,6 +24,9 @@ using video_widevine::WidevinePsshData;
|
||||
|
||||
namespace {
|
||||
|
||||
// Import names from ::testing for convenience
|
||||
using ::testing::_;
|
||||
|
||||
// Constants for JSON formatting
|
||||
const std::string kLeftBrace = "{";
|
||||
const std::string kRightBrace = "}";
|
||||
@@ -130,7 +134,7 @@ const std::string kWidevinePsshAfterV1Pssh = a2bs_hex(
|
||||
|
||||
const std::string kWidevineV1Pssh = a2bs_hex(
|
||||
// Widevine PSSH box, v1 format
|
||||
"00000044" // atom size
|
||||
"00000066" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"01000000" // v1, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
@@ -168,7 +172,7 @@ const std::string kZeroSizedPsshBox = a2bs_hex(
|
||||
|
||||
const std::string kSubLicensePsshBox = a2bs_hex(
|
||||
// Widevine PSSH box
|
||||
"0000009f" // atom size (whole buffer)
|
||||
"0000009f" // atom size
|
||||
"70737368" // atom type="pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
@@ -179,6 +183,93 @@ const std::string kSubLicensePsshBox = a2bs_hex(
|
||||
"5f69645f30120d7375625f6c6963656e73655f305a250a147375625f73657373696f"
|
||||
"6e5f6b65795f69645f31120d7375625f6c6963656e73655f31");
|
||||
|
||||
const std::string kMultipleWidevinePsshBox = a2bs_hex(
|
||||
// first PSSH box, Widevine with single keys
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"
|
||||
|
||||
// second PSSH box, Widevine with entitled keys
|
||||
"000001fb" // atom size
|
||||
"70737368" // atom type="pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"000001db" // data size
|
||||
// data:
|
||||
"220b47726f7570563254657374381448"
|
||||
"e3dc959b065002580272580a10668093"
|
||||
"381a8c5be48a0168ce372726ac1210c8"
|
||||
"326486bb5d5c4a958f00b1111afc811a"
|
||||
"20082cd9d3aed3ebe6239d30fbcf0b22"
|
||||
"1d28cbb0360ea1295c2363973346ec00"
|
||||
"512210914781334e864c8eb7f768cf26"
|
||||
"49073872580a10f872d11d5b1052f2bd"
|
||||
"a94e60a0e383021210450897c987a85c"
|
||||
"2e9579f968554a12991a2097e603ceea"
|
||||
"f35ed8cef1029eae7a0a54701e3d6db6"
|
||||
"80e7da1de3b22a8db347fb2210b41c34"
|
||||
"29b7bb96972bbaf6587bc0ddf172580a"
|
||||
"10bac58b9fce9e5929a42a180e529f19"
|
||||
"4712103f11f22988d25659b145ce4854"
|
||||
"3e6b141a20416e22768e5a57b08d155e"
|
||||
"5210d00658056947ff06d626668bceb3"
|
||||
"5eb01c6b57221081fb2ff3fef79d332f"
|
||||
"f98be46233596972580a101261c8036d"
|
||||
"ae5c8caa968858aa0ca9cc12106d583c"
|
||||
"b37c1456519843a81cf49912221a20c2"
|
||||
"1116bb54a226e8d879a4cd41d8879920"
|
||||
"2ae85b80d83b1b4447e5d7fcad6f6a22"
|
||||
"100b27a4c3f44771d2b0c7c34c66af35"
|
||||
"b572580a10ab1c8c259c6b5967991389"
|
||||
"65bff5ac0c1210b5b4473658565d3786"
|
||||
"efaf4b85d8e6e21a203ce6a9085285c2"
|
||||
"ece0b650dc83dd7aa8ac849611a8e3f8"
|
||||
"3c8f389223c0f3621522101946f0c2a3"
|
||||
"d543101cc842bbec2d0b30");
|
||||
// These are the data payloads of the two PSSH boxes in
|
||||
// kMultipleWidevinePsshBox.
|
||||
const CdmInitData kSingleKeyWidevinePsshBoxData = a2bs_hex(
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
const CdmInitData kEntitledKeysWidevinePsshBoxData = a2bs_hex(
|
||||
"220b47726f7570563254657374381448"
|
||||
"e3dc959b065002580272580a10668093"
|
||||
"381a8c5be48a0168ce372726ac1210c8"
|
||||
"326486bb5d5c4a958f00b1111afc811a"
|
||||
"20082cd9d3aed3ebe6239d30fbcf0b22"
|
||||
"1d28cbb0360ea1295c2363973346ec00"
|
||||
"512210914781334e864c8eb7f768cf26"
|
||||
"49073872580a10f872d11d5b1052f2bd"
|
||||
"a94e60a0e383021210450897c987a85c"
|
||||
"2e9579f968554a12991a2097e603ceea"
|
||||
"f35ed8cef1029eae7a0a54701e3d6db6"
|
||||
"80e7da1de3b22a8db347fb2210b41c34"
|
||||
"29b7bb96972bbaf6587bc0ddf172580a"
|
||||
"10bac58b9fce9e5929a42a180e529f19"
|
||||
"4712103f11f22988d25659b145ce4854"
|
||||
"3e6b141a20416e22768e5a57b08d155e"
|
||||
"5210d00658056947ff06d626668bceb3"
|
||||
"5eb01c6b57221081fb2ff3fef79d332f"
|
||||
"f98be46233596972580a101261c8036d"
|
||||
"ae5c8caa968858aa0ca9cc12106d583c"
|
||||
"b37c1456519843a81cf49912221a20c2"
|
||||
"1116bb54a226e8d879a4cd41d8879920"
|
||||
"2ae85b80d83b1b4447e5d7fcad6f6a22"
|
||||
"100b27a4c3f44771d2b0c7c34c66af35"
|
||||
"b572580a10ab1c8c259c6b5967991389"
|
||||
"65bff5ac0c1210b5b4473658565d3786"
|
||||
"efaf4b85d8e6e21a203ce6a9085285c2"
|
||||
"ece0b650dc83dd7aa8ac849611a8e3f8"
|
||||
"3c8f389223c0f3621522101946f0c2a3"
|
||||
"d543101cc842bbec2d0b30");
|
||||
|
||||
// OEMCrypto Versions known to have and not have entitlement license support.
|
||||
const std::string kOemCryptoWithoutEntitlements = "13";
|
||||
const std::string kOemCryptoWithEntitlements = "14";
|
||||
|
||||
// HLS test attribute key and values
|
||||
const std::string kHlsIvHexValue = "6DF49213A781E338628D0E9C812D328E";
|
||||
const std::string kHlsIvValue = "0x" + kHlsIvHexValue;
|
||||
@@ -420,6 +511,7 @@ class HlsInitDataConstructionTest : public ::testing::Test {};
|
||||
class HlsParseTest : public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsTest : public ::testing::Test {};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(InitializationDataTest, BadType) {
|
||||
@@ -437,7 +529,22 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::Values(kWidevinePssh, kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh, kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh, kWidevineV1Pssh, kOtherBoxFirst,
|
||||
kZeroSizedPsshBox, kSubLicensePsshBox));
|
||||
kZeroSizedPsshBox, kSubLicensePsshBox,
|
||||
kMultipleWidevinePsshBox));
|
||||
|
||||
TEST_F(InitializationDataTest, HandlesMultipleWidevinePsshs) {
|
||||
InitializationData single_init_data(ISO_BMFF_VIDEO_MIME_TYPE,
|
||||
kMultipleWidevinePsshBox,
|
||||
kOemCryptoWithoutEntitlements);
|
||||
EXPECT_FALSE(single_init_data.IsEmpty());
|
||||
EXPECT_EQ(kSingleKeyWidevinePsshBoxData, single_init_data.data());
|
||||
|
||||
InitializationData entitled_init_data(ISO_BMFF_VIDEO_MIME_TYPE,
|
||||
kMultipleWidevinePsshBox,
|
||||
kOemCryptoWithEntitlements);
|
||||
EXPECT_FALSE(entitled_init_data.IsEmpty());
|
||||
EXPECT_EQ(kEntitledKeysWidevinePsshBoxData, entitled_init_data.data());
|
||||
}
|
||||
|
||||
TEST_F(InitializationDataTest, ExtractSubLicense) {
|
||||
InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kSubLicensePsshBox);
|
||||
|
||||
@@ -101,7 +101,23 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
||||
if (sts != NO_ERROR) return sts;
|
||||
cdm_by_session_id_[key_set_id] = cdm_engine;
|
||||
}
|
||||
InitializationData initialization_data(init_data_type, init_data);
|
||||
|
||||
const SecurityLevel requested_security_level =
|
||||
property_set->security_level().compare(
|
||||
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
|
||||
? wvcdm::kLevel3
|
||||
: wvcdm::kLevelDefault;
|
||||
|
||||
std::string oec_version;
|
||||
sts = cdm_engine_.QueryStatus(requested_security_level,
|
||||
QUERY_KEY_OEMCRYPTO_API_VERSION,
|
||||
&oec_version);
|
||||
if (sts != NO_ERROR) {
|
||||
return sts;
|
||||
}
|
||||
InitializationData initialization_data(init_data_type, init_data,
|
||||
oec_version);
|
||||
|
||||
M_TIME(sts = cdm_engine->GenerateKeyRequest(session_id, key_set_id,
|
||||
initialization_data, license_type,
|
||||
app_parameters, key_request),
|
||||
|
||||
Reference in New Issue
Block a user