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
This commit is contained in:
Rahul Frias
2016-01-27 17:38:54 -08:00
parent 8ecc290a23
commit 7be45a9011
3 changed files with 196 additions and 74 deletions

View File

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

View File

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

View File

@@ -4,15 +4,20 @@
#include <gtest/gtest.h>
#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<std::string> Generate() { return vec_; }
private:
std::vector<std::string> vec_;
};
std::string GenerateHlsUriData(const std::string& provider,
const std::string& content_id,
const std::vector<std::string>& key_ids) {
std::string json = GenerateJsonInitData(content_id, provider, key_ids);
std::string json = GenerateJsonInitData(provider, content_id, key_ids);
std::vector<uint8_t> json_init_data(
reinterpret_cast<const uint8_t*>(json.data()),
reinterpret_cast<const uint8_t*>(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<std::string>& 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<std::string> kHlsTestKeyFormatVersionsSingleVersion = {"1"};
std::vector<std::string> kHlsTestKeyFormatVersionsSingleVersionExtendedLength =
{"21"};
std::vector<std::string> kHlsTestKeyFormatVersionsTwoVersions = {"1", "2"};
std::vector<std::string> kHlsTestKeyFormatVersionsThreeVersions = {"1", "2",
"5"};
std::vector<std::string> 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<HlsInitDataVariant> {};
class HlsInitDataConstructionTest : public ::testing::Test {};
class HlsParseTest : public ::testing::TestWithParam<HlsAttributeVariant> {};
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<uint8_t> json_init_data(
reinterpret_cast<const uint8_t*>(json.data()),
reinterpret_cast<const uint8_t*>(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)));