Use Jsmn to parse json data
[ Merge of http://go/wvgerrit/16628 ] Jsmn will replace a local method that parsed json init data. Added a fix to include all key Ids in the WidevineCencHeader rather than just the first. Also modified the content_id to reflect that it is a base64 encoded value. b/20630275 Change-Id: I7080c8cea21be4dea09a4905a96b4cc03e584c1d
This commit is contained in:
@@ -85,6 +85,7 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
libcdm_utils \
|
||||
libcrypto_static \
|
||||
libjsmn \
|
||||
libwvlevel3 \
|
||||
libwvdrmcryptoplugin \
|
||||
libwvdrmdrmplugin \
|
||||
|
||||
@@ -12,7 +12,8 @@ LOCAL_C_INCLUDES := \
|
||||
vendor/widevine/libwvdrmengine/third_party/stringencoders/src
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
external/protobuf/src
|
||||
external/jsmn \
|
||||
external/protobuf/src \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
|
||||
|
||||
|
||||
@@ -47,8 +47,6 @@ class InitializationData {
|
||||
std::vector<uint8_t>* value);
|
||||
static bool ExtractAttribute(const std::string& attribute_list,
|
||||
const std::string& key, std::string* value);
|
||||
static bool ExtractJsonValue(const std::string& json,
|
||||
const std::string& key, std::string* value);
|
||||
|
||||
static std::vector<std::string> ExtractKeyFormatVersions(
|
||||
const std::string& key_format_versions);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "jsmn.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
@@ -23,6 +24,9 @@ const std::string kBase64String = "base64,";
|
||||
const std::string kProvider = "provider";
|
||||
const std::string kContentId = "content_id";
|
||||
const std::string kKeyIds = "key_ids";
|
||||
|
||||
// Being conservative, usually we expect 6 + number of Key Ids
|
||||
const int kDefaultNumJsonTokens = 128;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -319,7 +323,7 @@ bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list,
|
||||
//
|
||||
// {
|
||||
// "provider":"mlbamhbo",
|
||||
// "content_id":"2015_Tears",
|
||||
// "content_id":"MjAxNV9UZWFycw==",
|
||||
// "key_ids":
|
||||
// [
|
||||
// "371e135e1a985d75d198a7f41020dc23"
|
||||
@@ -352,44 +356,110 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
std::string json_string((const char*)(&json_init_data[0]),
|
||||
json_init_data.size());
|
||||
|
||||
// Parse json data
|
||||
// Parse the Json string using jsmn
|
||||
int result = 0;
|
||||
jsmn_parser parser;
|
||||
jsmntok_t tokens[kDefaultNumJsonTokens];
|
||||
jsmn_init(&parser);
|
||||
int num_of_tokens =
|
||||
jsmn_parse(&parser, json_string.c_str(), json_string.size(), tokens,
|
||||
kDefaultNumJsonTokens);
|
||||
|
||||
if (num_of_tokens <= 0) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Json parsing failed: "
|
||||
"%d",
|
||||
num_of_tokens);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string provider;
|
||||
if (!ExtractJsonValue(json_string, kProvider, &provider)) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
||||
"provider value");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string content_id;
|
||||
if (!ExtractJsonValue(json_string, kContentId, &content_id)) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
||||
"content_id value");
|
||||
std::vector<std::string> key_ids;
|
||||
|
||||
enum JsmnParserState {
|
||||
kParseState,
|
||||
kProviderState,
|
||||
kContentIdState,
|
||||
kKeyIdsState,
|
||||
} state = kParseState;
|
||||
|
||||
int number_of_key_ids = 0;
|
||||
|
||||
// Extract the provider, content_id and key_ids
|
||||
for (int i = 0; i < num_of_tokens; ++i) {
|
||||
if (tokens[i].start < 0 || tokens[i].end < 0) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Invalid start or end "
|
||||
"of token");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case kParseState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
std::string token(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
if (token == kProvider) {
|
||||
state = kProviderState;
|
||||
} else if (token == kContentId) {
|
||||
state = kContentIdState;
|
||||
} else if (token == kKeyIds) {
|
||||
state = kKeyIdsState;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kProviderState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
provider.assign(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
}
|
||||
state = kParseState;
|
||||
break;
|
||||
case kContentIdState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
content_id.assign(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
}
|
||||
state = kParseState;
|
||||
break;
|
||||
case kKeyIdsState:
|
||||
if (tokens[i].type == JSMN_ARRAY) {
|
||||
number_of_key_ids = tokens[i].size;
|
||||
} else if (tokens[i].type == JSMN_STRING) {
|
||||
std::string key_id(a2bs_hex(json_string.substr(
|
||||
tokens[i].start, tokens[i].end - tokens[i].start)));
|
||||
if (key_id.size() == 16) key_ids.push_back(key_id);
|
||||
--number_of_key_ids;
|
||||
} else {
|
||||
state = kParseState;
|
||||
}
|
||||
if (number_of_key_ids <= 0) state = kParseState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid provider");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string key_id;
|
||||
if (!ExtractJsonValue(json_string, kKeyIds, &key_id)) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
||||
"key_id values");
|
||||
if (content_id.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid content_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
key_id = a2bs_hex(key_id);
|
||||
if (key_id.size() != 16) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Invalid key_id size: "
|
||||
"%ld",
|
||||
key_id.size());
|
||||
if (key_ids.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: No key_ids present");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now format as Widevine init data protobuf
|
||||
WidevineCencHeader cenc_header;
|
||||
cenc_header.set_algorithm(WidevineCencHeader_Algorithm_AESCTR);
|
||||
cenc_header.add_key_id(key_id);
|
||||
for (size_t i = 0; i < key_ids.size(); ++i) {
|
||||
cenc_header.add_key_id(key_ids[i]);
|
||||
}
|
||||
cenc_header.set_provider(provider);
|
||||
cenc_header.set_content_id(content_id);
|
||||
cenc_header.SerializeToString(init_data_proto);
|
||||
@@ -470,45 +540,6 @@ bool InitializationData::ExtractAttribute(const std::string& attribute_list,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractJsonValue(const std::string& json,
|
||||
const std::string& key,
|
||||
std::string* value) {
|
||||
std::string quoted_key = key;
|
||||
quoted_key.insert(0, 1, kDoubleQuote);
|
||||
quoted_key.append(1, kDoubleQuote);
|
||||
|
||||
bool found = false;
|
||||
size_t pos = 0;
|
||||
// Find the key followed by ':'
|
||||
while (!found) {
|
||||
pos = json.find(quoted_key, pos);
|
||||
if (pos == std::string::npos) return false;
|
||||
pos += quoted_key.size();
|
||||
while (pos < json.size() && isspace(json[pos])) ++pos;
|
||||
if (pos >= json.size()) return false;
|
||||
if (json[pos] != kColon) continue;
|
||||
found = true;
|
||||
}
|
||||
|
||||
++pos;
|
||||
while (pos < json.size() && isspace(json[pos])) ++pos;
|
||||
if (pos >= json.size()) return false;
|
||||
|
||||
if (json[pos] == kLeftBracket) {
|
||||
++pos;
|
||||
while (pos < json.size() && isspace(json[pos])) ++pos;
|
||||
if (pos >= json.size()) return false;
|
||||
}
|
||||
|
||||
if (json[pos] != kDoubleQuote) return false;
|
||||
++pos;
|
||||
size_t end_pos = json.find(kDoubleQuote, pos);
|
||||
if (end_pos == std::string::npos) return false;
|
||||
--end_pos;
|
||||
*value = json.substr(pos, end_pos - pos + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Key format versions are individual values or multiple versions
|
||||
// separated by '/'. "1" or "1/2/5"
|
||||
std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
||||
|
||||
@@ -180,7 +180,7 @@ const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7";
|
||||
char kHlsTestKeyFormatVersionsSeparator = '/';
|
||||
const std::string kHlsTestUriDataFormat = "data:text/plain;base64,";
|
||||
const std::string kHlsTestProvider = "youtube";
|
||||
const std::string kHlsTestContentId = "Tears_2015";
|
||||
const std::string kHlsTestContentId = "MjAxNV9UZWFycw==";
|
||||
const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23";
|
||||
const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261";
|
||||
const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390";
|
||||
@@ -594,7 +594,17 @@ TEST_P(HlsConstructionTest, InitData) {
|
||||
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)));
|
||||
for (size_t i = 0; i < param.key_ids_.size(); ++i) {
|
||||
bool key_id_found = false;
|
||||
if (param.key_ids_[i].size() != 32) continue;
|
||||
for (int j = 0; j < cenc_header.key_id_size(); ++j) {
|
||||
if (param.key_ids_[i] == b2a_hex(cenc_header.key_id(j))) {
|
||||
key_id_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(key_id_found);
|
||||
}
|
||||
EXPECT_EQ(param.provider_, cenc_header.provider());
|
||||
EXPECT_EQ(param.content_id_, cenc_header.content_id());
|
||||
} else {
|
||||
@@ -617,7 +627,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
.AddKeyId(kHlsTestKeyId2)
|
||||
.AddKeyId(kHlsTestKeyId3),
|
||||
HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId,
|
||||
kHlsTestInvalidKeyId, false)
|
||||
kHlsTestInvalidKeyId, true)
|
||||
.AddKeyId(kHlsTestKeyId1)));
|
||||
|
||||
TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) {
|
||||
|
||||
@@ -34,12 +34,13 @@ LOCAL_C_INCLUDES += external/protobuf/src
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
libcdm_protos \
|
||||
libcdm_utils \
|
||||
libcrypto_static \
|
||||
libwvgmock \
|
||||
libjsmn \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libwvgmock \
|
||||
libwvlevel3 \
|
||||
libcdm_utils \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
|
||||
@@ -19,6 +19,7 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm_protos \
|
||||
libcdm_utils \
|
||||
libcrypto_static \
|
||||
libjsmn \
|
||||
libwvgmock \
|
||||
libwvgmock_main \
|
||||
libgtest \
|
||||
|
||||
@@ -21,9 +21,10 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm_protos \
|
||||
libcdm_utils \
|
||||
libcrypto_static \
|
||||
libjsmn \
|
||||
libgtest \
|
||||
libwvgmock \
|
||||
libwvgmock_main \
|
||||
libgtest \
|
||||
libwvlevel3 \
|
||||
libwvdrmdrmplugin \
|
||||
|
||||
|
||||
Reference in New Issue
Block a user