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 \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
libcrypto_static \
|
libcrypto_static \
|
||||||
|
libjsmn \
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libwvdrmcryptoplugin \
|
libwvdrmcryptoplugin \
|
||||||
libwvdrmdrmplugin \
|
libwvdrmdrmplugin \
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ LOCAL_C_INCLUDES := \
|
|||||||
vendor/widevine/libwvdrmengine/third_party/stringencoders/src
|
vendor/widevine/libwvdrmengine/third_party/stringencoders/src
|
||||||
|
|
||||||
LOCAL_C_INCLUDES += \
|
LOCAL_C_INCLUDES += \
|
||||||
external/protobuf/src
|
external/jsmn \
|
||||||
|
external/protobuf/src \
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
|
LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ class InitializationData {
|
|||||||
std::vector<uint8_t>* value);
|
std::vector<uint8_t>* value);
|
||||||
static bool ExtractAttribute(const std::string& attribute_list,
|
static bool ExtractAttribute(const std::string& attribute_list,
|
||||||
const std::string& key, std::string* value);
|
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(
|
static std::vector<std::string> ExtractKeyFormatVersions(
|
||||||
const std::string& key_format_versions);
|
const std::string& key_format_versions);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "buffer_reader.h"
|
#include "buffer_reader.h"
|
||||||
|
#include "jsmn.h"
|
||||||
#include "license_protocol.pb.h"
|
#include "license_protocol.pb.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
@@ -23,6 +24,9 @@ const std::string kBase64String = "base64,";
|
|||||||
const std::string kProvider = "provider";
|
const std::string kProvider = "provider";
|
||||||
const std::string kContentId = "content_id";
|
const std::string kContentId = "content_id";
|
||||||
const std::string kKeyIds = "key_ids";
|
const std::string kKeyIds = "key_ids";
|
||||||
|
|
||||||
|
// Being conservative, usually we expect 6 + number of Key Ids
|
||||||
|
const int kDefaultNumJsonTokens = 128;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -319,7 +323,7 @@ bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list,
|
|||||||
//
|
//
|
||||||
// {
|
// {
|
||||||
// "provider":"mlbamhbo",
|
// "provider":"mlbamhbo",
|
||||||
// "content_id":"2015_Tears",
|
// "content_id":"MjAxNV9UZWFycw==",
|
||||||
// "key_ids":
|
// "key_ids":
|
||||||
// [
|
// [
|
||||||
// "371e135e1a985d75d198a7f41020dc23"
|
// "371e135e1a985d75d198a7f41020dc23"
|
||||||
@@ -352,44 +356,110 @@ bool InitializationData::ConstructWidevineInitData(
|
|||||||
std::string json_string((const char*)(&json_init_data[0]),
|
std::string json_string((const char*)(&json_init_data[0]),
|
||||||
json_init_data.size());
|
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;
|
std::string provider;
|
||||||
if (!ExtractJsonValue(json_string, kProvider, &provider)) {
|
|
||||||
LOGV(
|
|
||||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
|
||||||
"provider value");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string content_id;
|
std::string content_id;
|
||||||
if (!ExtractJsonValue(json_string, kContentId, &content_id)) {
|
std::vector<std::string> key_ids;
|
||||||
LOGV(
|
|
||||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
enum JsmnParserState {
|
||||||
"content_id value");
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key_id;
|
if (content_id.size() == 0) {
|
||||||
if (!ExtractJsonValue(json_string, kKeyIds, &key_id)) {
|
LOGV("InitializationData::ConstructWidevineInitData: Invalid content_id");
|
||||||
LOGV(
|
|
||||||
"InitializationData::ConstructWidevineInitData: Unable to extract "
|
|
||||||
"key_id values");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
key_id = a2bs_hex(key_id);
|
if (key_ids.size() == 0) {
|
||||||
if (key_id.size() != 16) {
|
LOGV("InitializationData::ConstructWidevineInitData: No key_ids present");
|
||||||
LOGV(
|
|
||||||
"InitializationData::ConstructWidevineInitData: Invalid key_id size: "
|
|
||||||
"%ld",
|
|
||||||
key_id.size());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now format as Widevine init data protobuf
|
// Now format as Widevine init data protobuf
|
||||||
WidevineCencHeader cenc_header;
|
WidevineCencHeader cenc_header;
|
||||||
cenc_header.set_algorithm(WidevineCencHeader_Algorithm_AESCTR);
|
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_provider(provider);
|
||||||
cenc_header.set_content_id(content_id);
|
cenc_header.set_content_id(content_id);
|
||||||
cenc_header.SerializeToString(init_data_proto);
|
cenc_header.SerializeToString(init_data_proto);
|
||||||
@@ -470,45 +540,6 @@ bool InitializationData::ExtractAttribute(const std::string& attribute_list,
|
|||||||
return true;
|
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
|
// Key format versions are individual values or multiple versions
|
||||||
// separated by '/'. "1" or "1/2/5"
|
// separated by '/'. "1" or "1/2/5"
|
||||||
std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7";
|
|||||||
char kHlsTestKeyFormatVersionsSeparator = '/';
|
char kHlsTestKeyFormatVersionsSeparator = '/';
|
||||||
const std::string kHlsTestUriDataFormat = "data:text/plain;base64,";
|
const std::string kHlsTestUriDataFormat = "data:text/plain;base64,";
|
||||||
const std::string kHlsTestProvider = "youtube";
|
const std::string kHlsTestProvider = "youtube";
|
||||||
const std::string kHlsTestContentId = "Tears_2015";
|
const std::string kHlsTestContentId = "MjAxNV9UZWFycw==";
|
||||||
const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23";
|
const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23";
|
||||||
const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261";
|
const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261";
|
||||||
const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390";
|
const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390";
|
||||||
@@ -594,7 +594,17 @@ TEST_P(HlsConstructionTest, InitData) {
|
|||||||
EXPECT_TRUE(cenc_header.ParseFromString(value));
|
EXPECT_TRUE(cenc_header.ParseFromString(value));
|
||||||
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
||||||
cenc_header.algorithm());
|
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.provider_, cenc_header.provider());
|
||||||
EXPECT_EQ(param.content_id_, cenc_header.content_id());
|
EXPECT_EQ(param.content_id_, cenc_header.content_id());
|
||||||
} else {
|
} else {
|
||||||
@@ -617,7 +627,7 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
.AddKeyId(kHlsTestKeyId2)
|
.AddKeyId(kHlsTestKeyId2)
|
||||||
.AddKeyId(kHlsTestKeyId3),
|
.AddKeyId(kHlsTestKeyId3),
|
||||||
HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId,
|
HlsInitDataVariant(kHlsTestProvider, kHlsTestContentId,
|
||||||
kHlsTestInvalidKeyId, false)
|
kHlsTestInvalidKeyId, true)
|
||||||
.AddKeyId(kHlsTestKeyId1)));
|
.AddKeyId(kHlsTestKeyId1)));
|
||||||
|
|
||||||
TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) {
|
TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) {
|
||||||
|
|||||||
@@ -34,12 +34,13 @@ LOCAL_C_INCLUDES += external/protobuf/src
|
|||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
libcdm \
|
libcdm \
|
||||||
libcdm_protos \
|
libcdm_protos \
|
||||||
|
libcdm_utils \
|
||||||
libcrypto_static \
|
libcrypto_static \
|
||||||
libwvgmock \
|
libjsmn \
|
||||||
libgtest \
|
libgtest \
|
||||||
libgtest_main \
|
libgtest_main \
|
||||||
|
libwvgmock \
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libcdm_utils \
|
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
libcutils \
|
libcutils \
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libcdm_protos \
|
libcdm_protos \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
libcrypto_static \
|
libcrypto_static \
|
||||||
|
libjsmn \
|
||||||
libwvgmock \
|
libwvgmock \
|
||||||
libwvgmock_main \
|
libwvgmock_main \
|
||||||
libgtest \
|
libgtest \
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libcdm_protos \
|
libcdm_protos \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
libcrypto_static \
|
libcrypto_static \
|
||||||
|
libjsmn \
|
||||||
|
libgtest \
|
||||||
libwvgmock \
|
libwvgmock \
|
||||||
libwvgmock_main \
|
libwvgmock_main \
|
||||||
libgtest \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libwvdrmdrmplugin \
|
libwvdrmdrmplugin \
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user