From e0da404b14c248b947227f2b5bef0687c5544fe3 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Fri, 29 Jan 2016 13:08:31 -0800 Subject: [PATCH] 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 --- libwvdrmengine/Android.mk | 1 + libwvdrmengine/cdm/Android.mk | 3 +- .../cdm/core/include/initialization_data.h | 2 - .../cdm/core/src/initialization_data.cpp | 159 +++++++++++------- .../test/initialization_data_unittest.cpp | 16 +- libwvdrmengine/cdm/test/unit-test.mk | 5 +- libwvdrmengine/mediacrypto/test/Android.mk | 1 + libwvdrmengine/mediadrm/test/Android.mk | 3 +- 8 files changed, 117 insertions(+), 73 deletions(-) diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index 335d7417..8654a5a9 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -85,6 +85,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_utils \ libcrypto_static \ + libjsmn \ libwvlevel3 \ libwvdrmcryptoplugin \ libwvdrmdrmplugin \ diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 923ed31e..ff65d58d 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -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 diff --git a/libwvdrmengine/cdm/core/include/initialization_data.h b/libwvdrmengine/cdm/core/include/initialization_data.h index 9a0fe8ee..f1ac8d74 100644 --- a/libwvdrmengine/cdm/core/include/initialization_data.h +++ b/libwvdrmengine/cdm/core/include/initialization_data.h @@ -47,8 +47,6 @@ class InitializationData { std::vector* 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 ExtractKeyFormatVersions( const std::string& key_format_versions); diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index e35d9f62..86bb34e8 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -5,6 +5,7 @@ #include #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 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 InitializationData::ExtractKeyFormatVersions( diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index 3dc9405a..afce8bfb 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -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) { diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index 6822b13e..e013c813 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -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 \ diff --git a/libwvdrmengine/mediacrypto/test/Android.mk b/libwvdrmengine/mediacrypto/test/Android.mk index 1f32b3c5..ad905249 100644 --- a/libwvdrmengine/mediacrypto/test/Android.mk +++ b/libwvdrmengine/mediacrypto/test/Android.mk @@ -19,6 +19,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm_protos \ libcdm_utils \ libcrypto_static \ + libjsmn \ libwvgmock \ libwvgmock_main \ libgtest \ diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk index 06b2654f..b6e8a96e 100644 --- a/libwvdrmengine/mediadrm/test/Android.mk +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -21,9 +21,10 @@ LOCAL_STATIC_LIBRARIES := \ libcdm_protos \ libcdm_utils \ libcrypto_static \ + libjsmn \ + libgtest \ libwvgmock \ libwvgmock_main \ - libgtest \ libwvlevel3 \ libwvdrmdrmplugin \