Merge "Modify initialization data to support HLS"
This commit is contained in:
@@ -16,23 +16,49 @@ class InitializationData {
|
||||
InitializationData(const std::string& type = std::string(),
|
||||
const CdmInitData& data = CdmInitData());
|
||||
|
||||
bool is_supported() const { return is_cenc_ || is_webm_; }
|
||||
bool is_supported() const { return is_cenc_ || is_webm_ || is_hls_; }
|
||||
bool is_cenc() const { return is_cenc_; }
|
||||
bool is_hls() const { return is_hls_; }
|
||||
bool is_webm() const { return is_webm_; }
|
||||
|
||||
bool IsEmpty() const { return data_.empty(); }
|
||||
|
||||
const std::string& type() const { return type_; }
|
||||
const CdmInitData& data() const { return data_; }
|
||||
const CdmHlsData& hls_data() const { return hls_data_; }
|
||||
|
||||
private:
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
|
||||
|
||||
bool ExtractHlsAttributes(const std::string& attribute_list,
|
||||
CdmHlsMethod* method, std::vector<uint8_t>* iv,
|
||||
std::string* uri, CdmInitData* init_data);
|
||||
|
||||
static bool ExtractQuotedAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::string* value);
|
||||
static bool ExtractHexAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::vector<uint8_t>* value);
|
||||
static bool ExtractAttribute(const std::string& attribute_list,
|
||||
const std::string& key, std::string* value);
|
||||
|
||||
// For testing only:
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(HlsAttributeExtractionTest, ExtractAttribute);
|
||||
FRIEND_TEST(HlsParseTest, Parse);
|
||||
FRIEND_TEST(HlsTest, ExtractHlsAttributes);
|
||||
FRIEND_TEST(HlsHexAttributeExtractionTest, ExtractHexAttribute);
|
||||
FRIEND_TEST(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute);
|
||||
#endif
|
||||
|
||||
std::string type_;
|
||||
CdmInitData data_;
|
||||
CdmHlsData hls_data_;
|
||||
bool is_cenc_;
|
||||
bool is_hls_;
|
||||
bool is_webm_;
|
||||
};
|
||||
|
||||
|
||||
@@ -80,8 +80,20 @@ static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
|
||||
static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
|
||||
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
|
||||
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
|
||||
static const std::string HLS_INIT_DATA_FORMAT = "hls";
|
||||
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
|
||||
|
||||
static const std::string HLS_INITDATA_ATTRIBUTE = "X-WV-INITDATA";
|
||||
static const std::string HLS_KEYFORMAT_ATTRIBUTE = "KEYFORMAT";
|
||||
static const std::string HLS_KEYFORMAT_VERSION_ATTRIBUTE = "KEYFORMATVERSION";
|
||||
static const std::string HLS_KEYFORMAT_VERSION_VALUE_1 = "1";
|
||||
static const std::string HLS_METHOD_ATTRIBUTE = "METHOD";
|
||||
static const std::string HLS_METHOD_AES_128 = "AES-128";
|
||||
static const std::string HLS_METHOD_NONE = "NONE";
|
||||
static const std::string HLS_METHOD_SAMPLE_AES = "SAMPLE-AES";
|
||||
static const std::string HLS_IV_ATTRIBUTE = "IV";
|
||||
static const std::string HLS_URI_ATTRIBUTE = "URI";
|
||||
|
||||
static const char EMPTY_ORIGIN[] = "";
|
||||
} // namespace wvcdm
|
||||
|
||||
|
||||
@@ -209,6 +209,10 @@ enum CdmResponseType {
|
||||
EMPTY_PROVISIONING_CERTIFICATE_2,
|
||||
OFFLINE_LICENSE_PROHIBITED,
|
||||
STORAGE_PROHIBITED,
|
||||
EMPTY_KEYSET_ID_ENG_5,
|
||||
SESSION_NOT_FOUND_11,
|
||||
LOAD_USAGE_INFO_FILE_ERROR,
|
||||
LOAD_USAGE_INFO_MISSING,
|
||||
};
|
||||
|
||||
enum CdmKeyStatus {
|
||||
@@ -254,6 +258,19 @@ enum CdmCertificateType {
|
||||
kCertificateX509,
|
||||
};
|
||||
|
||||
enum CdmHlsMethod {
|
||||
kHlsMethodNone,
|
||||
kHlsMethodAes128,
|
||||
kHlsMethodSampleAes,
|
||||
};
|
||||
|
||||
struct CdmHlsData {
|
||||
CdmHlsData() : method(kHlsMethodNone) {}
|
||||
CdmHlsMethod method;
|
||||
std::vector<uint8_t> iv;
|
||||
std::string uri;
|
||||
};
|
||||
|
||||
struct CdmDecryptionParameters {
|
||||
bool is_encrypted;
|
||||
bool is_secure;
|
||||
|
||||
@@ -7,26 +7,35 @@
|
||||
#include "buffer_reader.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
InitializationData::InitializationData(const std::string& type,
|
||||
const CdmInitData& data)
|
||||
: type_(type), is_cenc_(false), is_webm_(false) {
|
||||
: type_(type), is_cenc_(false), is_hls_(false), is_webm_(false) {
|
||||
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
|
||||
type == CENC_INIT_DATA_FORMAT) {
|
||||
is_cenc_ = true;
|
||||
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
|
||||
type == WEBM_INIT_DATA_FORMAT) {
|
||||
is_webm_ = true;
|
||||
} else if (type == HLS_INIT_DATA_FORMAT) {
|
||||
is_hls_ = true;
|
||||
}
|
||||
|
||||
if (is_supported()) {
|
||||
if (is_cenc()) {
|
||||
ExtractWidevinePssh(data, &data_);
|
||||
} else {
|
||||
} else if (is_webm()) {
|
||||
data_ = data;
|
||||
} else if (is_hls()) {
|
||||
CdmInitData init_data;
|
||||
if (ExtractHlsAttributes(data, &hls_data_.method, &hls_data_.iv,
|
||||
&hls_data_.uri, &init_data)) {
|
||||
ExtractWidevinePssh(init_data, &data_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,19 +70,22 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// atom size, used for skipping.
|
||||
uint64_t size;
|
||||
if (!reader.Read4Into8(&size)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom size.");
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
if (!reader.Read8(&size)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
|
||||
"size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read 64-bit "
|
||||
"atom size.");
|
||||
return false;
|
||||
}
|
||||
} else if (size == 0) {
|
||||
@@ -82,10 +94,12 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
|
||||
// "pssh"
|
||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: PSSH literal not present.");
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -94,15 +108,18 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// version
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH "
|
||||
"version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version > 1) {
|
||||
// unrecognized version - skip.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -110,25 +127,31 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
|
||||
// flags
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
|
||||
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("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
|
||||
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("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Skipping non-Widevine "
|
||||
"PSSH.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -136,11 +159,14 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// v1 has additional fields for key IDs. We can skip them.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read num key "
|
||||
"IDs.");
|
||||
return false;
|
||||
}
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,13 +174,16 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// size of PSSH data
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data "
|
||||
"size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -165,4 +194,175 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse an EXT-X-KEY tag attribute list. Verify that Widevine supports it
|
||||
// by validating KEYFORMAT and KEYFORMATVERSION attributes. Extract out
|
||||
// method, IV, URI and WV init data.
|
||||
//
|
||||
// An example of a widevine supported attribute list from an HLS media playlist
|
||||
// is,
|
||||
// "EXT-X-KEY: METHOD=SAMPLE-AES, \"
|
||||
// "URI=”http://www.youtube.com/license-service”, \"
|
||||
// "IV=0x6df49213a781e338628d0e9c812d328e, \"
|
||||
// "KEYFORMAT=”com.widevine.alpha”, \"
|
||||
// "KEYFORMATVERSION=”1”, \"
|
||||
// "X-WV-INITDATA=0x9e1a3af3de74ae3606e931fee285e3402e172732aba9a16a0e1441f1e"
|
||||
bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list,
|
||||
CdmHlsMethod* method,
|
||||
std::vector<uint8_t>* iv,
|
||||
std::string* uri,
|
||||
CdmInitData* init_data) {
|
||||
std::string value;
|
||||
if (!ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
&value)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
|
||||
"keyformat value");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.compare(0, sizeof(KEY_SYSTEM) - 1, KEY_SYSTEM) != 0) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: unrecognized HLS "
|
||||
"keyformat value: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_VERSION_ATTRIBUTE,
|
||||
&value)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
|
||||
"keyformat version");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.compare(HLS_KEYFORMAT_VERSION_VALUE_1)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS keyformat "
|
||||
"version is not supported: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractAttribute(attribute_list, HLS_METHOD_ATTRIBUTE, &value)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
|
||||
"method");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.compare(HLS_METHOD_AES_128) == 0) {
|
||||
*method = kHlsMethodAes128;
|
||||
} else if (value.compare(HLS_METHOD_SAMPLE_AES) == 0) {
|
||||
*method = kHlsMethodSampleAes;
|
||||
} else if (value.compare(HLS_METHOD_NONE) == 0) {
|
||||
*method = kHlsMethodNone;
|
||||
} else {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS method "
|
||||
"unrecognized: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractHexAttribute(attribute_list, HLS_IV_ATTRIBUTE, iv)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS IV attribute "
|
||||
"not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractQuotedAttribute(attribute_list, HLS_URI_ATTRIBUTE, uri)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS URI attribute "
|
||||
"not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> init_data_hex;
|
||||
if (!ExtractHexAttribute(attribute_list, HLS_INITDATA_ATTRIBUTE,
|
||||
&init_data_hex)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS initdata "
|
||||
"attribute not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
init_data->assign(reinterpret_cast<char*>(init_data_hex.data()),
|
||||
init_data_hex.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractQuotedAttribute(
|
||||
const std::string& attribute_list, const std::string& key,
|
||||
std::string* value) {
|
||||
bool result = ExtractAttribute(attribute_list, key, value);
|
||||
if (value->size() < 2 || value->at(0) != '\"' ||
|
||||
value->at(value->size() - 1) != '\"')
|
||||
return false;
|
||||
*value = value->substr(1, value->size() - 2);
|
||||
if (value->find('\"') != std::string::npos) return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractHexAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::vector<uint8_t>* value) {
|
||||
std::string val;
|
||||
bool result = ExtractAttribute(attribute_list, key, &val);
|
||||
if (!result || val.size() <= 2 || val.size() % 2 != 0 || val[0] != '0' ||
|
||||
((val[1] != 'x') && (val[1] != 'X')))
|
||||
return false;
|
||||
for (size_t i = 2; i < val.size(); ++i) {
|
||||
if (!isxdigit(val[i])) return false;
|
||||
}
|
||||
*value = a2b_hex(val.substr(2, val.size() - 2));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::string* value) {
|
||||
// validate the key
|
||||
for (size_t i = 0; i < key.size(); ++i)
|
||||
if (!isupper(key[i]) && !isdigit(key[i]) && key[i] != '-') return false;
|
||||
|
||||
bool found = false;
|
||||
size_t pos = 0;
|
||||
// Find the key followed by '='
|
||||
while (!found) {
|
||||
pos = attribute_list.find(key, pos);
|
||||
if (pos == std::string::npos) return false;
|
||||
pos += key.size();
|
||||
if (attribute_list[pos] != '=') continue;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (attribute_list.size() <= ++pos) return false;
|
||||
|
||||
size_t end_pos = pos;
|
||||
found = false;
|
||||
// Find the next comma outside the quote or end of string
|
||||
while (!found) {
|
||||
end_pos = attribute_list.find(',', end_pos);
|
||||
if (end_pos != std::string::npos && attribute_list[pos] == '\"' &&
|
||||
attribute_list[end_pos - 1] != '\"')
|
||||
continue;
|
||||
|
||||
if (end_pos == std::string::npos)
|
||||
end_pos = attribute_list.size() - 1;
|
||||
else
|
||||
--end_pos;
|
||||
found = true;
|
||||
}
|
||||
|
||||
*value = attribute_list.substr(pos, end_pos - pos + 1);
|
||||
|
||||
// validate the value
|
||||
for (size_t i = 0; i < value->size(); ++i)
|
||||
if (!isgraph(value->at(i))) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -139,8 +139,117 @@ const std::string kZeroSizedPsshBox = a2bs_hex(
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
// HLS test attribute key and values
|
||||
const std::string kHlsUriValue = "http://www.youtube.com/license-service";
|
||||
const std::string kHlsIvHexValue = "6DF49213A781E338628D0E9C812D328E";
|
||||
const std::string kHlsIvValue = "0x" + kHlsIvHexValue;
|
||||
const std::string kHlsKeyFormatValue = "com.widevine.alpha";
|
||||
const std::string kHlsKeyFormatValueOther = "com.example";
|
||||
const std::string kHlsInitDataHexValue = b2a_hex(kWidevineV1Pssh);
|
||||
const std::string kHlsInitDataValue = "0x" + kHlsInitDataHexValue;
|
||||
const std::string kHlsTestKey1 = "TESTKEY1";
|
||||
const std::string kHlsTestValue1 = "testvalue1";
|
||||
const std::string kHlsTestKey2 = "TESTKEY2";
|
||||
const std::string kHlsTestValue2 = "testvalue2";
|
||||
const std::string kHlsTestInvalidLowercaseKey = "testkey3";
|
||||
const std::string kHlsTestKeyWithDash = "TEST-KEY4";
|
||||
const std::string kHlsTestInvalidNonAlphanumKey = "TEST;KEY4";
|
||||
const std::string kHlsTestValueWithEmbeddedQuote = "test\"value1";
|
||||
const std::string kHlsTestEmptyHexValue = "";
|
||||
const std::string kHlsTestNoHexValue = "0x";
|
||||
const std::string kHlsTestHexValueWithOddBytes = kHlsIvHexValue + "7";
|
||||
const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7";
|
||||
|
||||
// HLS attribute helper functions
|
||||
std::string QuoteString(const std::string& value) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
|
||||
std::string CreateHlsAttributeList(const std::string& method,
|
||||
const std::string& uri,
|
||||
const std::string& iv,
|
||||
const std::string& key_format,
|
||||
const std::string& key_format_version,
|
||||
const std::string& init_data) {
|
||||
return "EXT-X-KEY: " + HLS_METHOD_ATTRIBUTE + "=" + method + "," +
|
||||
HLS_URI_ATTRIBUTE + "=" + QuoteString(uri) + "," + HLS_IV_ATTRIBUTE +
|
||||
"=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" +
|
||||
QuoteString(key_format) + "," + HLS_KEYFORMAT_VERSION_ATTRIBUTE + "=" +
|
||||
QuoteString(key_format_version) + "," + HLS_INITDATA_ATTRIBUTE + "=" +
|
||||
init_data;
|
||||
}
|
||||
|
||||
// HLS attribute list for testing
|
||||
const std::string kHlsAttributeList = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValueOther,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListKeyFormatVersionUnsupported =
|
||||
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue,
|
||||
kHlsKeyFormatValue, "2", kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListMethodAes128 = CreateHlsAttributeList(
|
||||
HLS_METHOD_AES_128, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListMethodNone = CreateHlsAttributeList(
|
||||
HLS_METHOD_NONE, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListMethodInvalid = CreateHlsAttributeList(
|
||||
kHlsTestValue1, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListInvalidUri = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestValueWithEmbeddedQuote, kHlsIvValue,
|
||||
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListInvalidIv = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestHexValueWithOddBytes, kHlsTestNoHexValue,
|
||||
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1, kHlsInitDataValue);
|
||||
|
||||
const std::string kHlsAttributeListInvalidInitData = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, kHlsTestInvalidHexValue);
|
||||
|
||||
std::string InsertHlsAttributeInList(const std::string key,
|
||||
const std::string& value) {
|
||||
return kHlsAttributeList + "," + key + "=" + value + "," + kHlsTestKey2 +
|
||||
"=" + kHlsTestValue2;
|
||||
}
|
||||
|
||||
struct HlsAttributeVariant {
|
||||
HlsAttributeVariant(const std::string& attribute_list, const std::string& key,
|
||||
const std::string& value, bool success)
|
||||
: attribute_list_(attribute_list),
|
||||
key_(key),
|
||||
value_(value),
|
||||
success_(success) {}
|
||||
const std::string attribute_list_;
|
||||
const std::string key_;
|
||||
const std::string value_;
|
||||
const bool success_;
|
||||
};
|
||||
|
||||
class InitializationDataTest : public ::testing::TestWithParam<std::string> {};
|
||||
|
||||
class HlsAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsHexAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsQuotedAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsParseTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsTest : public ::testing::Test {};
|
||||
} // namespace
|
||||
|
||||
TEST_P(InitializationDataTest, Parse) {
|
||||
@@ -148,17 +257,190 @@ TEST_P(InitializationDataTest, Parse) {
|
||||
EXPECT_FALSE(init_data.IsEmpty());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParsePssh, InitializationDataTest,
|
||||
::testing::Values(
|
||||
kWidevinePssh,
|
||||
kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh,
|
||||
kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh,
|
||||
kWidevineV1Pssh,
|
||||
kOtherBoxFirst,
|
||||
kZeroSizedPsshBox
|
||||
));
|
||||
INSTANTIATE_TEST_CASE_P(ParsePssh, InitializationDataTest,
|
||||
::testing::Values(kWidevinePssh, kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh,
|
||||
kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh,
|
||||
kWidevineV1Pssh, kOtherBoxFirst,
|
||||
kZeroSizedPsshBox));
|
||||
|
||||
TEST_P(HlsAttributeExtractionTest, ExtractAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::string value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
EXPECT_EQ(param.value_, value);
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_SAMPLE_AES, true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE,
|
||||
QuoteString(kHlsUriValue), true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvValue,
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
QuoteString(kHlsKeyFormatValue), true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSION_ATTRIBUTE,
|
||||
QuoteString(HLS_KEYFORMAT_VERSION_VALUE_1), true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_INITDATA_ATTRIBUTE,
|
||||
kHlsInitDataValue, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey2, kHlsTestValue2, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "\t",
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
" " + kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1 + " "),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "3",
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, ""),
|
||||
kHlsTestKey1, "", true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestInvalidLowercaseKey, kHlsTestValue1),
|
||||
kHlsTestInvalidLowercaseKey, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKeyWithDash,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKeyWithDash, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1),
|
||||
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1,
|
||||
false),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
|
||||
kHlsTestKey1, QuoteString(kHlsTestValue1), true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote), true)));
|
||||
|
||||
TEST_P(HlsHexAttributeExtractionTest, ExtractHexAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::vector<uint8_t> value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractHexAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
EXPECT_EQ(param.value_, b2a_hex(value));
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractHexAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsHexAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvHexValue,
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_INITDATA_ATTRIBUTE,
|
||||
kHlsInitDataHexValue, true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_INITDATA_ATTRIBUTE,
|
||||
kHlsInitDataHexValue, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestEmptyHexValue),
|
||||
kHlsTestKey1, kHlsTestEmptyHexValue, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestNoHexValue),
|
||||
kHlsTestKey1, kHlsTestNoHexValue, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestKey1, kHlsTestHexValueWithOddBytes),
|
||||
kHlsTestKey1, kHlsTestHexValueWithOddBytes, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestInvalidHexValue),
|
||||
kHlsTestKey1, kHlsTestInvalidHexValue, false)));
|
||||
|
||||
TEST_P(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::string value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractQuotedAttribute(
|
||||
param.attribute_list_, param.key_, &value));
|
||||
EXPECT_EQ(param.value_, value);
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractQuotedAttribute(
|
||||
param.attribute_list_, param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsQuotedAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE, kHlsUriValue,
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
kHlsKeyFormatValue, true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSION_ATTRIBUTE,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
|
||||
kHlsTestKey1, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
|
||||
kHlsTestKey1, kHlsTestValueWithEmbeddedQuote, false)));
|
||||
|
||||
TEST_P(HlsParseTest, Parse) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
InitializationData init_data(HLS_INIT_DATA_FORMAT, param.attribute_list_);
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(init_data.is_hls());
|
||||
EXPECT_FALSE(init_data.IsEmpty());
|
||||
if (param.key_.compare(HLS_METHOD_ATTRIBUTE) == 0) {
|
||||
if (param.value_.compare(HLS_METHOD_SAMPLE_AES) == 0) {
|
||||
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_data().method);
|
||||
} else if (param.value_.compare(HLS_METHOD_AES_128) == 0) {
|
||||
EXPECT_EQ(kHlsMethodAes128, init_data.hls_data().method);
|
||||
} else if (param.value_.compare(HLS_METHOD_NONE) == 0) {
|
||||
EXPECT_EQ(kHlsMethodNone, init_data.hls_data().method);
|
||||
}
|
||||
} else {
|
||||
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_data().method);
|
||||
}
|
||||
EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_data().iv));
|
||||
EXPECT_EQ(kHlsUriValue, init_data.hls_data().uri);
|
||||
} else {
|
||||
EXPECT_TRUE(init_data.is_hls());
|
||||
EXPECT_TRUE(init_data.IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsParseTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, "", "", true),
|
||||
HlsAttributeVariant(kHlsAttributeListKeyFormatUnknown,
|
||||
HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValueOther,
|
||||
false),
|
||||
HlsAttributeVariant(kHlsAttributeListKeyFormatVersionUnsupported,
|
||||
HLS_KEYFORMAT_VERSION_ATTRIBUTE, "2", false),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodAes128, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_AES_128, true),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodNone, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_NONE, true),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodInvalid,
|
||||
HLS_METHOD_ATTRIBUTE, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidUri, HLS_URI_ATTRIBUTE,
|
||||
kHlsTestValueWithEmbeddedQuote, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE,
|
||||
kHlsTestHexValueWithOddBytes, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidInitData,
|
||||
HLS_INITDATA_ATTRIBUTE, kHlsTestInvalidHexValue,
|
||||
false)));
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user