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:
Rahul Frias
2016-01-29 13:08:31 -08:00
parent 7be45a9011
commit e0da404b14
8 changed files with 117 additions and 73 deletions

View File

@@ -85,6 +85,7 @@ LOCAL_STATIC_LIBRARIES := \
libcdm \
libcdm_utils \
libcrypto_static \
libjsmn \
libwvlevel3 \
libwvdrmcryptoplugin \
libwvdrmdrmplugin \

View File

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

View File

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

View File

@@ -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)) {
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: Unable to extract "
"content_id value");
"InitializationData::ConstructWidevineInitData: Invalid start or end "
"of token");
return false;
}
std::string key_id;
if (!ExtractJsonValue(json_string, kKeyIds, &key_id)) {
LOGV(
"InitializationData::ConstructWidevineInitData: Unable to extract "
"key_id values");
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;
}
key_id = a2bs_hex(key_id);
if (key_id.size() != 16) {
LOGV(
"InitializationData::ConstructWidevineInitData: Invalid key_id size: "
"%ld",
key_id.size());
if (content_id.size() == 0) {
LOGV("InitializationData::ConstructWidevineInitData: Invalid content_id");
return false;
}
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(

View File

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

View File

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

View File

@@ -19,6 +19,7 @@ LOCAL_STATIC_LIBRARIES := \
libcdm_protos \
libcdm_utils \
libcrypto_static \
libjsmn \
libwvgmock \
libwvgmock_main \
libgtest \

View File

@@ -21,9 +21,10 @@ LOCAL_STATIC_LIBRARIES := \
libcdm_protos \
libcdm_utils \
libcrypto_static \
libjsmn \
libgtest \
libwvgmock \
libwvgmock_main \
libgtest \
libwvlevel3 \
libwvdrmdrmplugin \