Merge "Modify initialization data to support HLS"

This commit is contained in:
Rahul Frias
2016-01-12 20:51:12 +00:00
committed by Android (Google) Code Review
5 changed files with 571 additions and 34 deletions

View File

@@ -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_;
};

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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