From 7be45a90113c880ba04aeaaa4e1e5365b42840c4 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 27 Jan 2016 17:38:54 -0800 Subject: [PATCH] Unittests for HLS media playlist EXT-X-KEY format changes [ Merge for http://go/wvgerrit/16617 ] This adds additional test coverage to verify HLS EXT-X-KEY attribute lists. b/20630275 Change-Id: I72d7aa13b9b190728a56668ab79fa5e93bfa0d8b --- .../cdm/core/include/initialization_data.h | 2 + .../cdm/core/src/initialization_data.cpp | 4 +- .../test/initialization_data_unittest.cpp | 264 +++++++++++++----- 3 files changed, 196 insertions(+), 74 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/initialization_data.h b/libwvdrmengine/cdm/core/include/initialization_data.h index 00650e20..9a0fe8ee 100644 --- a/libwvdrmengine/cdm/core/include/initialization_data.h +++ b/libwvdrmengine/cdm/core/include/initialization_data.h @@ -57,6 +57,8 @@ class InitializationData { #if defined(UNIT_TEST) FRIEND_TEST(HlsAttributeExtractionTest, ExtractAttribute); FRIEND_TEST(HlsConstructionTest, InitData); + FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriDataFormat); + FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriBase64Encode); FRIEND_TEST(HlsHexAttributeExtractionTest, ExtractHexAttribute); FRIEND_TEST(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions); FRIEND_TEST(HlsParseTest, Parse); diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index b4fcd229..e35d9f62 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -449,8 +449,10 @@ bool InitializationData::ExtractAttribute(const std::string& attribute_list, while (!found) { end_pos = attribute_list.find(',', end_pos); if (end_pos != std::string::npos && attribute_list[pos] == '\"' && - attribute_list[end_pos - 1] != '\"') + attribute_list[end_pos - 1] != '\"') { + ++end_pos; continue; + } if (end_pos == std::string::npos) end_pos = attribute_list.size() - 1; diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index df05f0c7..3dc9405a 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -4,15 +4,20 @@ #include #include "initialization_data.h" +#include "license_protocol.pb.h" #include "string_conversions.h" #include "wv_cdm_constants.h" // References: // [1] http://dashif.org/identifiers/content-protection/ // [2] http://www.w3.org/TR/encrypted-media/cenc-format.html#common-system +// [3] https://tools.ietf.org/html/draft-pantos-http-live-streaming-18 namespace wvcdm { +// Protobuf generated classes. +using video_widevine_server::sdk::WidevineCencHeader; + namespace { // Constants for JSON formatting @@ -156,7 +161,6 @@ const std::string kZeroSizedPsshBox = a2bs_hex( "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"; @@ -177,10 +181,16 @@ char kHlsTestKeyFormatVersionsSeparator = '/'; const std::string kHlsTestUriDataFormat = "data:text/plain;base64,"; const std::string kHlsTestProvider = "youtube"; const std::string kHlsTestContentId = "Tears_2015"; -const std::string kHlsTestKeyId1 = "371e135e1a985d75d198a7f41020dc23"; -const std::string kHlsTestKeyId2 = "e670d9b60ae61583e01bc9253fa19261"; -const std::string kHlsTestKeyId3 = "78094e72165df39721b8a354d6a71390"; -const std::string kHlsTestInvalidKeyId = "b8a354d6a71390"; +const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23"; +const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261"; +const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390"; +const std::string kHlsTestInvalidKeyId = "B8A354D6A71390"; +const std::string kHlsTestKeyFormatVersion1 = "1"; +const std::string kHlsTestKeyFormatVersion3 = "3"; +const std::string kHlsTestKeyFormatVersion5 = "5"; +const std::string kHlsTestKeyFormatVersion13 = "13"; +const std::string kHlsTestKeyFormatVersion21 = "21"; +const std::string kHlsTestKeyFormatVersion37 = "37"; // HLS attribute helper functions std::string QuoteString(const std::string& value) { @@ -218,10 +228,23 @@ std::string GenerateJsonInitData(const std::string& provider, return json; } +class VectorOfStrings { + public: + VectorOfStrings(const std::string& str) { vec_.push_back(str); } + VectorOfStrings& Add(const std::string& str) { + vec_.push_back(str); + return *this; + } + const std::vector Generate() { return vec_; } + + private: + std::vector vec_; +}; + std::string GenerateHlsUriData(const std::string& provider, const std::string& content_id, const std::vector& key_ids) { - std::string json = GenerateJsonInitData(content_id, provider, key_ids); + std::string json = GenerateJsonInitData(provider, content_id, key_ids); std::vector json_init_data( reinterpret_cast(json.data()), reinterpret_cast(json.data() + json.size())); @@ -229,64 +252,93 @@ std::string GenerateHlsUriData(const std::string& provider, } std::string CreateHlsAttributeList(const std::string& method, - const std::string& uri, + const std::string& provider, + const std::string& content_id, + const std::vector& key_ids, const std::string& iv, const std::string& key_format, const std::string& key_format_version) { return "EXT-X-KEY: " + HLS_METHOD_ATTRIBUTE + "=" + method + "," + - HLS_URI_ATTRIBUTE + "=" + QuoteString(uri) + "," + HLS_IV_ATTRIBUTE + - "=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" + + HLS_URI_ATTRIBUTE + "=" + + QuoteString(GenerateHlsUriData(provider, content_id, key_ids)) + "," + + HLS_IV_ATTRIBUTE + "=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" + QuoteString(key_format) + "," + HLS_KEYFORMAT_VERSIONS_ATTRIBUTE + "=" + QuoteString(key_format_version); } -// HLS Key Format Version lists for testing -std::vector kHlsTestKeyFormatVersionsSingleVersion = {"1"}; -std::vector kHlsTestKeyFormatVersionsSingleVersionExtendedLength = - {"21"}; -std::vector kHlsTestKeyFormatVersionsTwoVersions = {"1", "2"}; -std::vector kHlsTestKeyFormatVersionsThreeVersions = {"1", "2", - "5"}; -std::vector kHlsTestKeyFormatVersionsFourVersions = {"3", "13", - "19", "27"}; - // HLS attribute list for testing -const std::string kHlsAttributeList = - CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); - -const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList( - HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, kHlsKeyFormatValueOther, +const std::string kHlsAttributeList = CreateHlsAttributeList( + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); +const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList( + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, + kHlsKeyFormatValueOther, HLS_KEYFORMAT_VERSION_VALUE_1); + const std::string kHlsAttributeListKeyFormatVersionUnsupported = - CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, "2"); + CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, + kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), + kHlsIvValue, kHlsKeyFormatValue, "2"); -const std::string kHlsAttributeListMethodAes128 = - CreateHlsAttributeList(HLS_METHOD_AES_128, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); +const std::string kHlsAttributeListKeyFormatVersionMultiple = + CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, + kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), + kHlsIvValue, kHlsKeyFormatValue, "1/2/5"); -const std::string kHlsAttributeListMethodNone = - CreateHlsAttributeList(HLS_METHOD_NONE, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); +const std::string kHlsAttributeListMethodAes128 = CreateHlsAttributeList( + HLS_METHOD_AES_128, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); -const std::string kHlsAttributeListMethodInvalid = - CreateHlsAttributeList(kHlsTestValue1, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); +const std::string kHlsAttributeListMethodNone = CreateHlsAttributeList( + HLS_METHOD_NONE, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); -const std::string kHlsAttributeListInvalidUri = CreateHlsAttributeList( - HLS_METHOD_SAMPLE_AES, kHlsTestValueWithEmbeddedQuote, kHlsIvValue, +const std::string kHlsAttributeListMethodInvalid = CreateHlsAttributeList( + kHlsTestValue1, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); + +const std::string kHlsAttributeListInvalidUriNoProvider = + CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, "", kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), + kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); + +const std::string kHlsAttributeListInvalidUriNoContentId = + CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, "", + VectorOfStrings(kHlsTestKeyId1).Generate(), + kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); + +const std::string kHlsAttributeListInvalidUriNoKeyId = CreateHlsAttributeList( + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings("").Generate(), kHlsIvValue, kHlsKeyFormatValue, + HLS_KEYFORMAT_VERSION_VALUE_1); + +const std::string kHlsAttributeListValidUriThreeKeyIds = CreateHlsAttributeList( + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1) + .Add(kHlsTestKeyId2) + .Add(kHlsTestKeyId3) + .Generate(), + kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); + +const std::string kHlsAttributeListNoIv = CreateHlsAttributeList( + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestNoHexValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListInvalidIv = CreateHlsAttributeList( - HLS_METHOD_SAMPLE_AES, kHlsTestHexValueWithOddBytes, kHlsTestNoHexValue, + HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestHexValueWithOddBytes, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); -const std::string kHlsAttributeListInvalidInitData = - CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsUriValue, kHlsIvValue, - kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); - std::string InsertHlsAttributeInList(const std::string key, const std::string& value) { return kHlsAttributeList + "," + key + "=" + value + "," + kHlsTestKey2 + @@ -299,7 +351,7 @@ struct HlsInitDataVariant { : provider_(provider), content_id_(content_id), success_(success) { if (key_id.size() > 0) key_ids_.push_back(key_id); } - HlsInitDataVariant AddKeyId(const std::string& key_id) { + HlsInitDataVariant& AddKeyId(const std::string& key_id) { key_ids_.push_back(key_id); return *this; } @@ -339,6 +391,8 @@ class HlsKeyFormatVersionsExtractionTest class HlsConstructionTest : public ::testing::TestWithParam {}; +class HlsInitDataConstructionTest : public ::testing::Test {}; + class HlsParseTest : public ::testing::TestWithParam {}; class HlsTest : public ::testing::Test {}; @@ -382,11 +436,20 @@ TEST_P(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions) { INSTANTIATE_TEST_CASE_P( HlsTest, HlsKeyFormatVersionsExtractionTest, - ::testing::Values(kHlsTestKeyFormatVersionsSingleVersion, - kHlsTestKeyFormatVersionsSingleVersionExtendedLength, - kHlsTestKeyFormatVersionsTwoVersions, - kHlsTestKeyFormatVersionsThreeVersions, - kHlsTestKeyFormatVersionsFourVersions)); + ::testing::Values(VectorOfStrings(kHlsTestKeyFormatVersion1).Generate(), + VectorOfStrings(kHlsTestKeyFormatVersion21).Generate(), + VectorOfStrings(kHlsTestKeyFormatVersion1) + .Add(kHlsTestKeyFormatVersion3) + .Generate(), + VectorOfStrings(kHlsTestKeyFormatVersion1) + .Add(kHlsTestKeyFormatVersion3) + .Add(kHlsTestKeyFormatVersion13) + .Generate(), + VectorOfStrings(kHlsTestKeyFormatVersion13) + .Add(kHlsTestKeyFormatVersion5) + .Add(kHlsTestKeyFormatVersion21) + .Add(kHlsTestKeyFormatVersion37) + .Generate())); TEST_P(HlsAttributeExtractionTest, ExtractAttribute) { HlsAttributeVariant param = GetParam(); @@ -407,7 +470,10 @@ INSTANTIATE_TEST_CASE_P( HlsAttributeVariant(kHlsAttributeList, HLS_METHOD_ATTRIBUTE, HLS_METHOD_SAMPLE_AES, true), HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE, - QuoteString(kHlsUriValue), true), + QuoteString(GenerateHlsUriData( + kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate())), + true), HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvValue, true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE, @@ -499,8 +565,11 @@ TEST_P(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute) { INSTANTIATE_TEST_CASE_P( HlsTest, HlsQuotedAttributeExtractionTest, ::testing::Values( - HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE, kHlsUriValue, - true), + HlsAttributeVariant( + kHlsAttributeList, HLS_URI_ATTRIBUTE, + GenerateHlsUriData(kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate()), + true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValue, true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, @@ -521,7 +590,13 @@ TEST_P(HlsConstructionTest, InitData) { std::string value; if (param.success_) { EXPECT_TRUE(InitializationData::ConstructWidevineInitData(uri, &value)); - // EXPECT_EQ(param.value_, value); + WidevineCencHeader cenc_header; + EXPECT_TRUE(cenc_header.ParseFromString(value)); + EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR, + cenc_header.algorithm()); + EXPECT_EQ(param.key_ids_[0], b2a_hex(cenc_header.key_id(0))); + EXPECT_EQ(param.provider_, cenc_header.provider()); + EXPECT_EQ(param.content_id_, cenc_header.content_id()); } else { EXPECT_FALSE(InitializationData::ConstructWidevineInitData(uri, &value)); } @@ -529,25 +604,44 @@ TEST_P(HlsConstructionTest, InitData) { INSTANTIATE_TEST_CASE_P( HlsTest, HlsConstructionTest, - ::testing::Values(HlsInitDataVariant(std::string(), kHlsTestContentId, - kHlsTestKeyId1, false), - HlsInitDataVariant(kHlsTestProvider, std::string(), - kHlsTestKeyId1, false), - HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, - std::string(), false), - HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, - kHlsTestInvalidKeyId, false), - HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, - kHlsTestKeyId1, true), - HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, - kHlsTestKeyId1, true) - .AddKeyId(kHlsTestKeyId2) - .AddKeyId(kHlsTestKeyId3), - HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, - kHlsTestInvalidKeyId, false) - .AddKeyId(kHlsTestKeyId1))); + ::testing::Values( + HlsInitDataVariant("", kHlsTestContentId, kHlsTestKeyId1, false), + HlsInitDataVariant(kHlsTestProvider, "", kHlsTestKeyId1, false), + HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, "", false), + HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, + kHlsTestInvalidKeyId, false), + HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, + true), + HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, + true) + .AddKeyId(kHlsTestKeyId2) + .AddKeyId(kHlsTestKeyId3), + HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId, + kHlsTestInvalidKeyId, false) + .AddKeyId(kHlsTestKeyId1))); -TEST_P(HlsParseTest, DISABLED_Parse) { +TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) { + std::string json = + GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate()); + std::vector json_init_data( + reinterpret_cast(json.data()), + reinterpret_cast(json.data() + json.size())); + std::string value; + EXPECT_FALSE(InitializationData::ConstructWidevineInitData( + Base64Encode(json_init_data), &value)); +} + +TEST_F(HlsInitDataConstructionTest, InvalidUriBase64Encode) { + std::string json = + GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId, + VectorOfStrings(kHlsTestKeyId1).Generate()); + std::string value; + EXPECT_FALSE(InitializationData::ConstructWidevineInitData( + kHlsTestUriDataFormat + json, &value)); +} + +TEST_P(HlsParseTest, Parse) { HlsAttributeVariant param = GetParam(); InitializationData init_data(HLS_INIT_DATA_FORMAT, param.attribute_list_); if (param.success_) { @@ -564,6 +658,19 @@ TEST_P(HlsParseTest, DISABLED_Parse) { } else { EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method()); } + + WidevineCencHeader cenc_header; + EXPECT_TRUE(cenc_header.ParseFromString(init_data.data())); + EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR, + cenc_header.algorithm()); + if (param.key_.compare(kJsonProvider) == 0) { + EXPECT_EQ(param.value_, cenc_header.provider()); + } else if (param.key_.compare(kJsonContentId) == 0) { + EXPECT_EQ(param.value_, cenc_header.content_id()); + } else if (param.key_.compare(kJsonKeyIds) == 0) { + EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_id(0))); + } + EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv())); } else { EXPECT_TRUE(init_data.is_hls()); @@ -584,10 +691,21 @@ INSTANTIATE_TEST_CASE_P( HLS_METHOD_AES_128, true), HlsAttributeVariant(kHlsAttributeListMethodNone, HLS_METHOD_ATTRIBUTE, HLS_METHOD_NONE, true), + HlsAttributeVariant(kHlsAttributeListKeyFormatVersionMultiple, + HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, + HLS_KEYFORMAT_VERSION_VALUE_1, true), HlsAttributeVariant(kHlsAttributeListMethodInvalid, HLS_METHOD_ATTRIBUTE, kHlsTestValue1, false), - HlsAttributeVariant(kHlsAttributeListInvalidUri, HLS_URI_ATTRIBUTE, - kHlsTestValueWithEmbeddedQuote, false), + HlsAttributeVariant(kHlsAttributeListInvalidUriNoProvider, + kJsonProvider, kHlsTestProvider, false), + HlsAttributeVariant(kHlsAttributeListInvalidUriNoContentId, + kJsonContentId, kHlsTestContentId, false), + HlsAttributeVariant(kHlsAttributeListInvalidUriNoKeyId, kJsonKeyIds, + kHlsTestKeyId1, false), + HlsAttributeVariant(kHlsAttributeListValidUriThreeKeyIds, kJsonKeyIds, + kHlsTestKeyId1, true), + HlsAttributeVariant(kHlsAttributeListNoIv, HLS_IV_ATTRIBUTE, + kHlsTestNoHexValue, false), HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE, kHlsTestHexValueWithOddBytes, false)));