diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index 36ed6604c9..730f6f2c1a 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -10,13 +10,13 @@ #include "packager/base/base64.h" #include "packager/base/files/file_path.h" -#include "packager/base/json/json_writer.h" #include "packager/base/logging.h" #include "packager/base/optional.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/stringprintf.h" #include "packager/hls/base/media_playlist.h" #include "packager/media/base/protection_system_specific_info.h" +#include "packager/media/base/proto_json_util.h" #include "packager/media/base/raw_key_pssh_generator.h" #include "packager/media/base/raw_key_source.h" #include "packager/media/base/widevine_key_source.h" @@ -150,31 +150,21 @@ bool WidevinePsshToJson(const std::vector& pssh_box, return false; } - base::DictionaryValue pssh_dict; - pssh_dict.SetString("provider", pssh_proto.provider()); - if (pssh_proto.has_content_id()) { - std::string content_id_base64; - base::Base64Encode(base::StringPiece(pssh_proto.content_id().data(), - pssh_proto.content_id().size()), - &content_id_base64); - pssh_dict.SetString("content_id", content_id_base64); + media::WidevineHeader widevine_header; + widevine_header.set_provider(pssh_proto.provider()); + if (pssh_proto.has_content_id()) + widevine_header.set_content_id(pssh_proto.content_id()); + // Place the current |key_id| to the front and converts all key_id to hex + // format. + widevine_header.add_key_ids(base::HexEncode(key_id.data(), key_id.size())); + for (const std::string& key_id_in_pssh : pssh_proto.key_id()) { + const std::string key_id_hex = + base::HexEncode(key_id_in_pssh.data(), key_id_in_pssh.size()); + if (widevine_header.key_ids(0) != key_id_hex) + widevine_header.add_key_ids(key_id_hex); } - base::ListValue* key_ids = new base::ListValue(); - key_ids->AppendString(base::HexEncode(key_id.data(), key_id.size())); - for (const std::string& id : pssh_proto.key_id()) { - if (key_id.size() == id.size() && - memcmp(key_id.data(), id.data(), id.size()) == 0) { - continue; - } - key_ids->AppendString(base::HexEncode(id.data(), id.size())); - } - pssh_dict.Set("key_ids", key_ids); - - if (!base::JSONWriter::Write(pssh_dict, pssh_json)) { - LOG(ERROR) << "Failed to write to JSON."; - return false; - } + *pssh_json = media::MessageToJsonString(widevine_header); return true; } diff --git a/packager/hls/base/simple_hls_notifier_unittest.cc b/packager/hls/base/simple_hls_notifier_unittest.cc index 17fb08f6de..0e4acbacd9 100644 --- a/packager/hls/base/simple_hls_notifier_unittest.cc +++ b/packager/hls/base/simple_hls_notifier_unittest.cc @@ -500,10 +500,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) { pssh_builder.add_key_id(any_key_id); const char kExpectedJson[] = - "{" - "\"content_id\":\"Y29udGVudGlk\"," - "\"key_ids\":[\"11223344112233441122334411223344\"]," - "\"provider\":\"someprovider\"}"; + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); @@ -547,10 +545,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) { widevine_pssh_data_str.end()); const char kExpectedJson[] = - "{" - "\"content_id\":\"Y29udGVudGlk\"," - "\"key_ids\":[\"11223344112233441122334411223344\"]," - "\"provider\":\"someprovider\"}"; + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); @@ -655,10 +651,10 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) { pssh_builder.add_key_id(second_keyid); const char kExpectedJson[] = - "{" - "\"key_ids\":[\"22222222222222222222222222222222\"," - "\"11111111111111111111111111111111\"]," - "\"provider\":\"someprovider\"}"; + R"({)" + R"("key_ids":["22222222222222222222222222222222",)" + R"("11111111111111111111111111111111"],)" + R"("provider":"someprovider"})"; std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); @@ -801,10 +797,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) { widevine_pssh_data_str.end()); const char kExpectedJson[] = - "{" - "\"content_id\":\"Y29udGVudGlk\"," - "\"key_ids\":[\"11223344112233441122334411223344\"]," - "\"provider\":\"someprovider\"}"; + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 1b8ed4b40d..3cb145bf40 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -77,6 +77,8 @@ 'producer_consumer_queue.h', 'protection_system_specific_info.cc', 'protection_system_specific_info.h', + 'proto_json_util.cc', + 'proto_json_util.h', 'pssh_generator.cc', 'pssh_generator.h', 'pssh_generator_util.cc', diff --git a/packager/media/base/proto_json_util.cc b/packager/media/base/proto_json_util.cc new file mode 100644 index 0000000000..46fbdb4f60 --- /dev/null +++ b/packager/media/base/proto_json_util.cc @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/base/proto_json_util.h" + +#include + +#include "packager/base/logging.h" + +namespace shaka { +namespace media { + +std::string MessageToJsonString(const google::protobuf::Message& message) { + google::protobuf::util::JsonPrintOptions json_print_options; + json_print_options.preserve_proto_field_names = true; + + std::string result; + GOOGLE_CHECK_OK(google::protobuf::util::MessageToJsonString( + message, &result, json_print_options)); + return result; +} + +bool JsonStringToMessage(const std::string& input, + google::protobuf::Message* message) { + google::protobuf::util::JsonParseOptions json_parse_options; + json_parse_options.ignore_unknown_fields = true; + + auto status = google::protobuf::util::JsonStringToMessage(input, message, + json_parse_options); + if (!status.ok()) { + LOG(ERROR) << "Failed to parse from JSON: " << input + << " error: " << status.error_message(); + return false; + } + return true; +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/base/proto_json_util.h b/packager/media/base/proto_json_util.h new file mode 100644 index 0000000000..bd3b13a103 --- /dev/null +++ b/packager/media/base/proto_json_util.h @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_MEDIA_BASE_PROTO_JSON_UTIL_H_ +#define PACKAGER_MEDIA_BASE_PROTO_JSON_UTIL_H_ + +#include + +namespace google { +namespace protobuf { +class Message; +} // namespace protobuf +} // namespace google + +namespace shaka { +namespace media { + +/// Convert protobuf message to JSON. +/// @param message is a protobuf message. +/// @return The protobuf message in JSON format. +std::string MessageToJsonString(const google::protobuf::Message& message); + +/// Convert JSON to protobuf message. +/// @param input is the JSON form of a protobuf message. +/// @param message will contain the protobuf message on success. +/// @return true on success, false otherwise. +bool JsonStringToMessage(const std::string& input, + google::protobuf::Message* message); + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_BASE_PROTO_JSON_UTIL_H_ diff --git a/packager/media/base/widevine_pssh_data.proto b/packager/media/base/widevine_pssh_data.proto index 8b436b089b..8c67f003e4 100644 --- a/packager/media/base/widevine_pssh_data.proto +++ b/packager/media/base/widevine_pssh_data.proto @@ -7,7 +7,6 @@ // This file defines Widevine Pssh Data proto format. syntax = "proto2"; -option optimize_for = LITE_RUNTIME; package shaka.media; @@ -40,3 +39,18 @@ message WidevinePsshData { // 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample). optional uint32 protection_scheme = 9; } + +// Derived from WidevinePsshData. The JSON format of this proto is used in +// Widevine HLS DRM signaling v1. +// We cannot build JSON from WidevinePsshData as |key_id| is required to be in +// hex format, while |bytes| type is translated to base64 by JSON formatter, so +// we have to use |string| type and do hex conversion in the code. +message WidevineHeader { + repeated string key_ids = 2; + + // Content provider name. + optional string provider = 3; + + // A content identifier, specified by content provider. + optional bytes content_id = 4; +}