Modify initialization data to support HLS
[ Merge of http://go/wvgerrit/16290 ] HLS uses an EXT-X-KEY tag and attribute list in the media playlist to identify the key and method used to encrypt media segments. This allows for the attributes to be parsed and extracted. b/20630275 Change-Id: I2c4a419022f933b7b34b64dc48930f167abe65c6
This commit is contained in:
@@ -16,23 +16,49 @@ class InitializationData {
|
|||||||
InitializationData(const std::string& type = std::string(),
|
InitializationData(const std::string& type = std::string(),
|
||||||
const CdmInitData& data = CdmInitData());
|
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_cenc() const { return is_cenc_; }
|
||||||
|
bool is_hls() const { return is_hls_; }
|
||||||
bool is_webm() const { return is_webm_; }
|
bool is_webm() const { return is_webm_; }
|
||||||
|
|
||||||
bool IsEmpty() const { return data_.empty(); }
|
bool IsEmpty() const { return data_.empty(); }
|
||||||
|
|
||||||
const std::string& type() const { return type_; }
|
const std::string& type() const { return type_; }
|
||||||
const CdmInitData& data() const { return data_; }
|
const CdmInitData& data() const { return data_; }
|
||||||
|
const CdmHlsData& hls_data() const { return hls_data_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||||
// Widevine PSSH.
|
// Widevine PSSH.
|
||||||
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
|
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_;
|
std::string type_;
|
||||||
CdmInitData data_;
|
CdmInitData data_;
|
||||||
|
CdmHlsData hls_data_;
|
||||||
bool is_cenc_;
|
bool is_cenc_;
|
||||||
|
bool is_hls_;
|
||||||
bool is_webm_;
|
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_VIDEO_MIME_TYPE = "video/webm";
|
||||||
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
|
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
|
||||||
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
|
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 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[] = "";
|
static const char EMPTY_ORIGIN[] = "";
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ enum CdmResponseType {
|
|||||||
EMPTY_PROVISIONING_CERTIFICATE_2,
|
EMPTY_PROVISIONING_CERTIFICATE_2,
|
||||||
OFFLINE_LICENSE_PROHIBITED,
|
OFFLINE_LICENSE_PROHIBITED,
|
||||||
STORAGE_PROHIBITED,
|
STORAGE_PROHIBITED,
|
||||||
|
EMPTY_KEYSET_ID_ENG_5,
|
||||||
|
SESSION_NOT_FOUND_11,
|
||||||
|
LOAD_USAGE_INFO_FILE_ERROR,
|
||||||
|
LOAD_USAGE_INFO_MISSING,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CdmKeyStatus {
|
enum CdmKeyStatus {
|
||||||
@@ -254,6 +258,19 @@ enum CdmCertificateType {
|
|||||||
kCertificateX509,
|
kCertificateX509,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CdmHlsMethod {
|
||||||
|
kHlsMethodNone,
|
||||||
|
kHlsMethodAes128,
|
||||||
|
kHlsMethodSampleAes,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CdmHlsData {
|
||||||
|
CdmHlsData() : method(kHlsMethodNone) {}
|
||||||
|
CdmHlsMethod method;
|
||||||
|
std::vector<uint8_t> iv;
|
||||||
|
std::string uri;
|
||||||
|
};
|
||||||
|
|
||||||
struct CdmDecryptionParameters {
|
struct CdmDecryptionParameters {
|
||||||
bool is_encrypted;
|
bool is_encrypted;
|
||||||
bool is_secure;
|
bool is_secure;
|
||||||
|
|||||||
@@ -7,26 +7,35 @@
|
|||||||
#include "buffer_reader.h"
|
#include "buffer_reader.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
InitializationData::InitializationData(const std::string& type,
|
InitializationData::InitializationData(const std::string& type,
|
||||||
const CdmInitData& data)
|
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 ||
|
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
|
||||||
type == CENC_INIT_DATA_FORMAT) {
|
type == CENC_INIT_DATA_FORMAT) {
|
||||||
is_cenc_ = true;
|
is_cenc_ = true;
|
||||||
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
|
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
|
||||||
type == WEBM_INIT_DATA_FORMAT) {
|
type == WEBM_INIT_DATA_FORMAT) {
|
||||||
is_webm_ = true;
|
is_webm_ = true;
|
||||||
|
} else if (type == HLS_INIT_DATA_FORMAT) {
|
||||||
|
is_hls_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_supported()) {
|
if (is_supported()) {
|
||||||
if (is_cenc()) {
|
if (is_cenc()) {
|
||||||
ExtractWidevinePssh(data, &data_);
|
ExtractWidevinePssh(data, &data_);
|
||||||
} else {
|
} else if (is_webm()) {
|
||||||
data_ = data;
|
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.
|
// atom size, used for skipping.
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
if (!reader.Read4Into8(&size)) {
|
if (!reader.Read4Into8(&size)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Unable to read atom size.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> atom_type;
|
std::vector<uint8_t> atom_type;
|
||||||
if (!reader.ReadVec(&atom_type, 4)) {
|
if (!reader.ReadVec(&atom_type, 4)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Unable to read atom type.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
if (!reader.Read8(&size)) {
|
if (!reader.Read8(&size)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
|
LOGV(
|
||||||
"size.");
|
"InitializationData::ExtractWidevinePssh: Unable to read 64-bit "
|
||||||
|
"atom size.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (size == 0) {
|
} else if (size == 0) {
|
||||||
@@ -82,10 +94,12 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
|
|
||||||
// "pssh"
|
// "pssh"
|
||||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
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))) {
|
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
LOGV(
|
||||||
"the atom.");
|
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||||
|
"of the atom.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -94,15 +108,18 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
// version
|
// version
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
if (!reader.Read1(&version)) {
|
if (!reader.Read1(&version)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Unable to read PSSH "
|
||||||
|
"version.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version > 1) {
|
if (version > 1) {
|
||||||
// unrecognized version - skip.
|
// unrecognized version - skip.
|
||||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
LOGV(
|
||||||
"the atom.");
|
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||||
|
"of the atom.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -110,25 +127,31 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
|
|
||||||
// flags
|
// flags
|
||||||
if (!reader.SkipBytes(3)) {
|
if (!reader.SkipBytes(3)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Unable to skip the PSSH "
|
||||||
|
"flags.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// system id
|
// system id
|
||||||
std::vector<uint8_t> system_id;
|
std::vector<uint8_t> system_id;
|
||||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||||
// skip non-Widevine PSSH boxes.
|
// skip non-Widevine PSSH boxes.
|
||||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
LOGV(
|
||||||
"the atom.");
|
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||||
|
"of the atom.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Skipping non-Widevine "
|
||||||
|
"PSSH.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,11 +159,14 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
// v1 has additional fields for key IDs. We can skip them.
|
// v1 has additional fields for key IDs. We can skip them.
|
||||||
uint32_t num_key_ids;
|
uint32_t num_key_ids;
|
||||||
if (!reader.Read4(&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;
|
return false;
|
||||||
}
|
}
|
||||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,13 +174,16 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
// size of PSSH data
|
// size of PSSH data
|
||||||
uint32_t data_length;
|
uint32_t data_length;
|
||||||
if (!reader.Read4(&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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
output->clear();
|
output->clear();
|
||||||
if (!reader.ReadString(output, data_length)) {
|
if (!reader.ReadString(output, data_length)) {
|
||||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
|
LOGV(
|
||||||
|
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,4 +194,175 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
|||||||
return false;
|
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
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -139,8 +139,117 @@ const std::string kZeroSizedPsshBox = a2bs_hex(
|
|||||||
// data:
|
// data:
|
||||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
"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 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
|
} // namespace
|
||||||
|
|
||||||
TEST_P(InitializationDataTest, Parse) {
|
TEST_P(InitializationDataTest, Parse) {
|
||||||
@@ -148,17 +257,190 @@ TEST_P(InitializationDataTest, Parse) {
|
|||||||
EXPECT_FALSE(init_data.IsEmpty());
|
EXPECT_FALSE(init_data.IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(ParsePssh, InitializationDataTest,
|
||||||
ParsePssh, InitializationDataTest,
|
::testing::Values(kWidevinePssh, kWidevinePsshFirst,
|
||||||
::testing::Values(
|
kWidevinePsshAfterV0Pssh,
|
||||||
kWidevinePssh,
|
kWidevinePsshAfterNonZeroFlags,
|
||||||
kWidevinePsshFirst,
|
kWidevinePsshAfterV1Pssh,
|
||||||
kWidevinePsshAfterV0Pssh,
|
kWidevineV1Pssh, kOtherBoxFirst,
|
||||||
kWidevinePsshAfterNonZeroFlags,
|
kZeroSizedPsshBox));
|
||||||
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
|
} // namespace wvcdm
|
||||||
|
|||||||
Reference in New Issue
Block a user