Source release 14.0.0

This commit is contained in:
John W. Bruce
2018-05-16 17:35:40 -07:00
parent 31381a1311
commit 3ab70cec4e
2053 changed files with 1585838 additions and 4614 deletions

View File

@@ -4,13 +4,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 = '/';
@@ -25,6 +28,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 {
@@ -33,9 +42,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),
@@ -53,7 +66,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()) {
@@ -67,172 +85,284 @@ InitializationData::InitializationData(const std::string& type,
// Parse the pssh data and return the embedded key data if it exists.
std::vector<video_widevine::SubLicense>
InitializationData::ExtractEmbeddedKeys() const {
InitializationData::ExtractSublicenseKeys() const {
std::vector<video_widevine::SubLicense> keys;
// TODO(jfore): The pssh has changed in ways that are not compatible with
//sublicenses. Restructure or remove sublicense support including this method.
return keys;
}
std::vector<video_widevine::WidevinePsshData_EntitledKey>
InitializationData::ExtractWrappedKeys() const {
std::vector<video_widevine::WidevinePsshData_EntitledKey> keys;
WidevinePsshData cenc_header;
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
cenc_header.sub_licenses().size() == 0)
cenc_header.entitled_keys().size() == 0)
return keys;
keys.reserve(cenc_header.sub_licenses().size());
for (int i = 0; i < cenc_header.sub_licenses().size(); ++i) {
keys.push_back(cenc_header.sub_licenses(i));
keys.reserve(cenc_header.entitled_keys().size());
for (int i = 0; i < cenc_header.entitled_keys().size(); ++i) {
keys.push_back(cenc_header.entitled_keys(i));
}
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
@@ -483,7 +613,7 @@ bool InitializationData::ConstructWidevineInitData(
// have not yet been pushed to production. Set until then.
cenc_header.set_algorithm(WidevinePsshData_Algorithm_AESCTR);
for (size_t i = 0; i < key_ids.size(); ++i) {
cenc_header.add_key_id(key_ids[i]);
cenc_header.add_key_ids(key_ids[i]);
}
cenc_header.set_provider(provider);
cenc_header.set_content_id(content_id);
@@ -589,19 +719,18 @@ std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
return versions;
}
// Extract the key id of the group master key used to generate sublicense data.
// Returns an empty string if not defined.
const std::string InitializationData::ExtractGroupMasterKeyId() const {
if (!is_cenc_) {
return "";
bool InitializationData::DetectEntitlementPreference(
const std::string& oec_version_string) {
if (oec_version_string.empty()) {
return false;
}
WidevinePsshData cenc_header;
if (!cenc_header.ParseFromString(data_)) {
return "";
}
uint32_t oec_version_int = 0;
std::istringstream parse_int;
parse_int.str(oec_version_string);
parse_int >> oec_version_int;
return cenc_header.group_master_key_id();
return oec_version_int >= 14;
}
} // namespace wvcdm