1) Change return type to WvCasStatus for functions in wv_cas_types.cc.
2) Add a binary wv_cas_types_example. 3) Surface wv_cas_key_fetcher *source code* to partner to serve as an example of how they would make a HTTP request to acquire an entitlement key from license server. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=229953562
This commit is contained in:
@@ -18,7 +18,9 @@ filegroup(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"test_ecmg_messages.h",
|
"test_ecmg_messages.h",
|
||||||
"wv_cas_ecm_example.cc",
|
"wv_cas_ecm_example.cc",
|
||||||
|
"wv_cas_types_example.cc",
|
||||||
":wv_cas_ecm_example",
|
":wv_cas_ecm_example",
|
||||||
|
":wv_cas_types_example",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,10 +34,16 @@ cc_library(
|
|||||||
hdrs = ["test_ecmg_messages.h"],
|
hdrs = ["test_ecmg_messages.h"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "test_emmg_messages",
|
||||||
|
hdrs = ["test_emmg_messages.h"],
|
||||||
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
name = "wv_cas_ecm_example",
|
name = "wv_cas_ecm_example",
|
||||||
srcs = ["wv_cas_ecm_example.cc"],
|
srcs = ["wv_cas_ecm_example.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
@@ -52,3 +60,13 @@ cc_binary(
|
|||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "wv_cas_types_example",
|
||||||
|
srcs = ["wv_cas_types_example.cc"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
const char kTestChannelSetup[] = {
|
const char kTestEcmgChannelSetup[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x01', // message_type - Channel_setup
|
'\x00', '\x01', // message_type - Channel_setup
|
||||||
'\x00', '\x0e', // message_length
|
'\x00', '\x0e', // message_length
|
||||||
@@ -26,7 +26,7 @@ const char kTestChannelSetup[] = {
|
|||||||
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestChannelStatus[] = {
|
const char kTestEcmgChannelStatus[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x03', // message_type - Channel_status
|
'\x00', '\x03', // message_type - Channel_status
|
||||||
'\x00', '\x39', // message_length
|
'\x00', '\x39', // message_length
|
||||||
@@ -62,7 +62,7 @@ const char kTestChannelStatus[] = {
|
|||||||
'\x00', '\x64' // parameter_value
|
'\x00', '\x64' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestStreamSetup[] = {
|
const char kTestEcmgStreamSetup[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x01', // message_type - Stream_setup
|
'\x01', '\x01', // message_type - Stream_setup
|
||||||
'\x00', '\x18', // message_length
|
'\x00', '\x18', // message_length
|
||||||
@@ -80,7 +80,7 @@ const char kTestStreamSetup[] = {
|
|||||||
'\x00', '\x64' // parameter_value
|
'\x00', '\x64' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestStreamStatus[] = {
|
const char kTestEcmgStreamStatus[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x03', // message_type - Stream_status
|
'\x01', '\x03', // message_type - Stream_status
|
||||||
'\x00', '\x17', // message_length
|
'\x00', '\x17', // message_length
|
||||||
@@ -98,7 +98,7 @@ const char kTestStreamStatus[] = {
|
|||||||
'\x01' // parameter_value
|
'\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestCwProvision[] = {
|
const char kTestEcmgCwProvision[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x02', '\x01', // message_type - CW_provision
|
'\x02', '\x01', // message_type - CW_provision
|
||||||
'\x00', '\x44', // message_length
|
'\x00', '\x44', // message_length
|
||||||
@@ -126,7 +126,7 @@ const char kTestCwProvision[] = {
|
|||||||
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
||||||
|
|
||||||
// CW is encrypted using hardcoded fixed entitlement key.
|
// CW is encrypted using hardcoded fixed entitlement key.
|
||||||
const char kTestEcmResponse[] = {
|
const char kTestEcmgEcmResponse[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x02', '\x02', // message_type - ECM_response
|
'\x02', '\x02', // message_type - ECM_response
|
||||||
'\x00', '\xd2', // message_length
|
'\x00', '\xd2', // message_length
|
||||||
@@ -164,7 +164,7 @@ const char kTestEcmResponse[] = {
|
|||||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
|
||||||
|
|
||||||
const char kTestStreamCloseRequest[] = {
|
const char kTestEcmgStreamCloseRequest[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x04', // message_type - Stream_close_request
|
'\x01', '\x04', // message_type - Stream_close_request
|
||||||
'\x00', '\x0c', // message_length
|
'\x00', '\x0c', // message_length
|
||||||
@@ -176,7 +176,7 @@ const char kTestStreamCloseRequest[] = {
|
|||||||
'\x00', '\x01' // parameter_value
|
'\x00', '\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestStreamCloseResponse[] = {
|
const char kTestEcmgStreamCloseResponse[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x05', // message_type - Stream_close_response
|
'\x01', '\x05', // message_type - Stream_close_response
|
||||||
'\x00', '\x0c', // message_length
|
'\x00', '\x0c', // message_length
|
||||||
@@ -188,7 +188,7 @@ const char kTestStreamCloseResponse[] = {
|
|||||||
'\x00', '\x01' // parameter_value
|
'\x00', '\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestChannelClose[] = {
|
const char kTestEcmgChannelClose[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x04', // message_type - Channel_close
|
'\x00', '\x04', // message_type - Channel_close
|
||||||
'\x00', '\x06', // message_length
|
'\x00', '\x06', // message_length
|
||||||
|
|||||||
146
example/test_emmg_messages.h
Normal file
146
example/test_emmg_messages.h
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example EMMG messages used in unit tests.
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
const char kTestEmmgTsPacket[] = {
|
||||||
|
'\x47', '\x5F', '\xFF', '\x10', '\x00', '\x82', '\x70', '\x61', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x83', '\x70', '\x61',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01'};
|
||||||
|
|
||||||
|
const char kTestEmmgChannelSetup[] = {
|
||||||
|
'\x02', // protocol_version
|
||||||
|
'\x00', '\x11', // message_type - Channel_setup
|
||||||
|
'\x00', '\x13', // message_length
|
||||||
|
'\x00', '\x01', // parameter_type - client_id
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - data_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x02', // parameter_type - section_TSpkt_flag
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestEmmgStreamSetup[] = {
|
||||||
|
'\x02', // protocol_version
|
||||||
|
'\x01', '\x11', // message_type - Stream_setup
|
||||||
|
'\x00', '\x1f', // message_length
|
||||||
|
'\x00', '\x01', // parameter_type - client_id
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - data_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x04', // parameter_type - data_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x08', // parameter_type - data_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x07', // parameter_type - data_type
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x01' // parameter_value - private data
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestEmmgDataProvision[] = {
|
||||||
|
'\x02', // protocol_version
|
||||||
|
'\x02', '\x11', // message_type - Data_provision
|
||||||
|
'\x00', '\xda', // message_length
|
||||||
|
'\x00', '\x01', // parameter_type - client_id
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - data_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x04', // parameter_type - data_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x08', // parameter_type - data_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x05', // parameter_type - datagram
|
||||||
|
'\x00', '\xbc', // parameter_length
|
||||||
|
'\x47', '\x5f', '\xff', '\x10', '\x00', '\x82', '\x70', '\x61', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x83', '\x70', '\x61',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01'};
|
||||||
|
|
||||||
|
const char kTestEmmgStreamCloseRequest[] = {
|
||||||
|
'\x02', // protocol_version
|
||||||
|
'\x01', '\x14', // message_type - Stream_close_request
|
||||||
|
'\x00', '\x14', // message_length
|
||||||
|
'\x00', '\x01', // parameter_type - client_id
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - data_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x04', // parameter_type - data_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestEmmgChannelClose[] = {
|
||||||
|
'\x02', // protocol_version
|
||||||
|
'\x00', '\x14', // message_type - Channel_close
|
||||||
|
'\x00', '\x0e', // message_length
|
||||||
|
'\x00', '\x01', // parameter_type - client_id
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - data_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_EMMG_MESSAGES_H_
|
||||||
@@ -8,12 +8,23 @@
|
|||||||
|
|
||||||
// Example of how to use the wv_cas_ecm library.
|
// Example of how to use the wv_cas_ecm library.
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||||
|
|
||||||
|
DEFINE_int32(content_iv_size, 8, "Content IV size");
|
||||||
|
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
|
||||||
|
DEFINE_string(crypto_mode, "CSA2", "Only CBC, CTR, or CSA2 is allowed");
|
||||||
|
DEFINE_int32(ecm_pid, 149, "PID for the ECM packet");
|
||||||
|
DEFINE_string(output_file, "",
|
||||||
|
"If specified, generated ECM TS packet will be written to the "
|
||||||
|
"specified output file path");
|
||||||
|
|
||||||
const char kCsaEvenKey[] = "even_key"; // 8 bytes
|
const char kCsaEvenKey[] = "even_key"; // 8 bytes
|
||||||
const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes
|
const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes
|
||||||
const char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes
|
const char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes
|
||||||
@@ -24,26 +35,45 @@ const char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes
|
|||||||
const char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes
|
const char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes
|
||||||
const char kOddEntitlementKey[] =
|
const char kOddEntitlementKey[] =
|
||||||
"fakefakefakefakefakefakefake2..."; // 32 bytes
|
"fakefakefakefakefakefakefake2..."; // 32 bytes
|
||||||
|
const size_t kTsPacketSize = 188;
|
||||||
|
|
||||||
|
widevine::cas::CryptoMode GetCryptoMode() {
|
||||||
|
if (FLAGS_crypto_mode.compare("CBC") == 0) {
|
||||||
|
return widevine::cas::CryptoMode::kAesCbc;
|
||||||
|
}
|
||||||
|
if (FLAGS_crypto_mode.compare("CTR") == 0) {
|
||||||
|
return widevine::cas::CryptoMode::kAesCtr;
|
||||||
|
}
|
||||||
|
return widevine::cas::CryptoMode::kDvbCsa2;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
// Generate ECM.
|
||||||
widevine::cas::WvCasEcm wv_cas_ecm;
|
widevine::cas::WvCasEcm wv_cas_ecm;
|
||||||
widevine::cas::WvCasStatus status = wv_cas_ecm.Initialize(
|
widevine::cas::WvCasStatus status = wv_cas_ecm.Initialize(
|
||||||
/* content_iv_size= */ 8, /* key_rotation_enabled= */ true,
|
FLAGS_content_iv_size, FLAGS_key_rotation, GetCryptoMode());
|
||||||
widevine::cas::CryptoMode::kDvbCsa2);
|
|
||||||
if (status != widevine::cas::OK) {
|
if (status != widevine::cas::OK) {
|
||||||
std::cerr << "Failed to initialize WV CAS ECM, error: "
|
std::cerr << "Failed to initialize WV CAS ECM, error: "
|
||||||
<< widevine::cas::GetWvCasStatusMessage(status)
|
<< widevine::cas::GetWvCasStatusMessage(status)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
std::string ecm;
|
std::string ecm;
|
||||||
|
if (FLAGS_key_rotation) {
|
||||||
status = wv_cas_ecm.GenerateEcm(
|
status = wv_cas_ecm.GenerateEcm(
|
||||||
kCsaEvenKey, kEvenContentIv8Bytes, kEvenEntitlementKeyId,
|
kCsaEvenKey, kEvenContentIv8Bytes, kEvenEntitlementKeyId,
|
||||||
kEvenEntitlementKey, kCsaOddKey, kOddContentIv8Bytes,
|
kEvenEntitlementKey, kCsaOddKey, kOddContentIv8Bytes,
|
||||||
kOddEntitlementKeyId, kOddEntitlementKey, &ecm);
|
kOddEntitlementKeyId, kOddEntitlementKey, &ecm);
|
||||||
|
} else {
|
||||||
|
status = wv_cas_ecm.GenerateSingleKeyEcm(kCsaEvenKey, kEvenContentIv8Bytes,
|
||||||
|
kEvenEntitlementKeyId,
|
||||||
|
kEvenEntitlementKey, &ecm);
|
||||||
|
}
|
||||||
if (status != widevine::cas::OK) {
|
if (status != widevine::cas::OK) {
|
||||||
std::cerr << "Failed to generate WV CAS ECM, error: "
|
std::cerr << "Failed to generate WV CAS ECM, error: "
|
||||||
<< widevine::cas::GetWvCasStatusMessage(status)
|
<< widevine::cas::GetWvCasStatusMessage(status)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "ECM size: " << ecm.size() << std::endl;
|
std::cout << "ECM size: " << ecm.size() << std::endl;
|
||||||
std::cout << "ECM bytes: ";
|
std::cout << "ECM bytes: ";
|
||||||
@@ -52,6 +82,30 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
// Generate ECM TS Packet.
|
||||||
|
uint8_t packet[kTsPacketSize];
|
||||||
|
uint8_t continuity_counter; // not used.
|
||||||
|
status = wv_cas_ecm.GenerateTsPacket(ecm, FLAGS_ecm_pid,
|
||||||
|
/* table_id= */ 0x80,
|
||||||
|
&continuity_counter, packet);
|
||||||
|
if (status != widevine::cas::OK) {
|
||||||
|
std::cerr << "Failed to create ECM TS packet" << std::endl;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
std::cout << "TS packet bytes: ";
|
||||||
|
for (size_t i = 0; i < kTsPacketSize; i++) {
|
||||||
|
printf("'\\x%02x', ", static_cast<uint16_t>(packet[i]));
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
// Write ECM TS Packet to a file.
|
||||||
|
if (!FLAGS_output_file.empty()) {
|
||||||
|
std::ofstream file;
|
||||||
|
file.open(FLAGS_output_file.c_str(), std::ios_base::binary);
|
||||||
|
assert(file.is_open());
|
||||||
|
file.write(reinterpret_cast<char *>(packet), kTsPacketSize);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
37
example/wv_cas_types_example.cc
Normal file
37
example/wv_cas_types_example.cc
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example of how to use types/functions in wv_cas_types.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||||
|
|
||||||
|
DEFINE_string(function_to_call, "CreateWvCasEncryptionRequestJson",
|
||||||
|
"Function in wv_cas_types to exercise");
|
||||||
|
|
||||||
|
void CallCreateWvCasEncryptionRequestJson() {
|
||||||
|
widevine::cas::WvCasEncryptionRequest request;
|
||||||
|
request.content_id = "cont_id cont_id ";
|
||||||
|
request.provider = "widevine_test";
|
||||||
|
request.track_types = {"SD"};
|
||||||
|
request.key_rotation = true;
|
||||||
|
std::string request_json;
|
||||||
|
widevine::cas::CreateWvCasEncryptionRequestJson(request, &request_json);
|
||||||
|
LOG(INFO) << FLAGS_function_to_call << " returns " << request_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
if (FLAGS_function_to_call.compare("CreateWvCasEncryptionRequestJson") == 0) {
|
||||||
|
CallCreateWvCasEncryptionRequestJson();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -56,9 +56,10 @@ cc_library(
|
|||||||
srcs = ["ecm_generator.cc"],
|
srcs = ["ecm_generator.cc"],
|
||||||
hdrs = ["ecm_generator.h"],
|
hdrs = ["ecm_generator.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":ecm",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//media_cas_packager_sdk/internal:ecm",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,12 +82,14 @@ cc_library(
|
|||||||
hdrs = [
|
hdrs = [
|
||||||
"ecmg_client_handler.h",
|
"ecmg_client_handler.h",
|
||||||
"ecmg_constants.h",
|
"ecmg_constants.h",
|
||||||
|
"simulcrypt_constants.h",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":ecm",
|
":ecm",
|
||||||
":ecm_generator",
|
":ecm_generator",
|
||||||
":fixed_key_fetcher",
|
":fixed_key_fetcher",
|
||||||
":mpeg2ts",
|
":mpeg2ts",
|
||||||
|
":simulcrypt_util",
|
||||||
":util",
|
":util",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
@@ -112,6 +115,36 @@ cc_test(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "emmg",
|
||||||
|
srcs = ["emmg.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"emmg.h",
|
||||||
|
"emmg_constants.h",
|
||||||
|
"simulcrypt_constants.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":simulcrypt_util",
|
||||||
|
":util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"//common:status",
|
||||||
|
"//example:test_emmg_messages",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "emmg_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["emmg_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":emmg",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//example:test_emmg_messages",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "fixed_key_fetcher",
|
name = "fixed_key_fetcher",
|
||||||
srcs = [
|
srcs = [
|
||||||
@@ -146,6 +179,21 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "simulcrypt_util",
|
||||||
|
srcs = ["simulcrypt_util.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"simulcrypt_constants.h",
|
||||||
|
"simulcrypt_util.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"//common:status",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "ts_packet",
|
name = "ts_packet",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -19,10 +20,13 @@
|
|||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "example/constants.h"
|
#include "example/constants.h"
|
||||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||||
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
||||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||||
#include "media_cas_packager_sdk/internal/util.h"
|
#include "media_cas_packager_sdk/internal/util.h"
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||||
@@ -183,81 +187,16 @@ Status HandleParameters(const char* const request, size_t request_length,
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add 'protocol_version', 'message_type', 'message_length' to the message.
|
|
||||||
// TODO(user): Per jfore@, consider pass in a pointer to a structure
|
|
||||||
// #pragma pack(push, 1) // exact fit - no padding
|
|
||||||
// struct MessageHeader{
|
|
||||||
// uint8_t protocol_version;
|
|
||||||
// uint16_t message_type;
|
|
||||||
// uint16_t message_length;
|
|
||||||
// };
|
|
||||||
// #pragma pack(pop) // restore previous pack
|
|
||||||
void BuildMessageHeader(uint16_t message_type, char* message,
|
|
||||||
size_t* message_length) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(message_length);
|
|
||||||
*message_length = 0;
|
|
||||||
message[*message_length] = ECMG_SCS_PROTOCOL_VERSION;
|
|
||||||
*message_length += PROTOCOL_VERSION_SIZE;
|
|
||||||
Host16ToBigEndian(message + *message_length, &message_type);
|
|
||||||
*message_length += MESSAGE_TYPE_SIZE;
|
|
||||||
// NOTE: 'message_length' needs to be updated later after we have added all
|
|
||||||
// the params so we know the exact length of the all the params.
|
|
||||||
// Use 0 for 'total_param_length' until we know the length of the message.
|
|
||||||
uint16_t total_param_length = 0;
|
|
||||||
Host16ToBigEndian(message + *message_length, &total_param_length);
|
|
||||||
*message_length += MESSAGE_LENGTH_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a uint16_t parameter to the message.
|
|
||||||
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
|
|
||||||
size_t* message_length) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(message_length);
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_type);
|
|
||||||
*message_length += 2;
|
|
||||||
uint16_t param_length = 2;
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_length);
|
|
||||||
*message_length += 2;
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_value);
|
|
||||||
*message_length += param_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a uint8_t parameter to the message.
|
|
||||||
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
|
|
||||||
size_t* message_length) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(message_length);
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_type);
|
|
||||||
*message_length += 2;
|
|
||||||
uint16_t param_length = 1;
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_length);
|
|
||||||
*message_length += 2;
|
|
||||||
memcpy(message + *message_length, ¶m_value, param_length);
|
|
||||||
*message_length += param_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a param that is |param_length| bytes long.
|
|
||||||
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
|
|
||||||
char* message, size_t* message_length) {
|
|
||||||
DCHECK(param_value);
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(message_length);
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_type);
|
|
||||||
*message_length += 2;
|
|
||||||
Host16ToBigEndian(message + *message_length, ¶m_length);
|
|
||||||
*message_length += 2;
|
|
||||||
memcpy(message + *message_length, param_value, param_length);
|
|
||||||
*message_length += param_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message,
|
void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message,
|
||||||
size_t* message_length) {
|
size_t* message_length) {
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_CHANNEL_ERROR, message, message_length);
|
simulcrypt_util::BuildMessageHeader(
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_ERROR, message, message_length);
|
||||||
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message,
|
||||||
|
message_length);
|
||||||
// No setting Error_information parameter yet.
|
// No setting Error_information parameter yet.
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
@@ -268,25 +207,33 @@ void BuildChannelStatus(uint16_t channel_id, EcmgConfig* config, char* message,
|
|||||||
DCHECK(config);
|
DCHECK(config);
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_CHANNEL_STATUS, message, message_length);
|
simulcrypt_util::BuildMessageHeader(
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_STATUS, message, message_length);
|
||||||
AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message,
|
||||||
|
message_length);
|
||||||
// No setting AC_delay_start parameter yet.
|
// No setting AC_delay_start parameter yet.
|
||||||
// No setting AC_delay_stop parameter yet.
|
// No setting AC_delay_stop parameter yet.
|
||||||
AddUint16Param(DELAY_START, config->delay_start, message, message_length);
|
simulcrypt_util::AddUint16Param(DELAY_START, config->delay_start, message,
|
||||||
AddUint16Param(DELAY_STOP, config->delay_stop, message, message_length);
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(DELAY_STOP, config->delay_stop, message,
|
||||||
|
message_length);
|
||||||
// No setting transition_delay_start parameter yet.
|
// No setting transition_delay_start parameter yet.
|
||||||
// No setting transition_delay_stop parameter yet.
|
// No setting transition_delay_stop parameter yet.
|
||||||
AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, message,
|
simulcrypt_util::AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period,
|
||||||
|
message, message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(MAX_STREAMS, kMaxStream, message,
|
||||||
message_length);
|
message_length);
|
||||||
AddUint16Param(MAX_STREAMS, kMaxStream, message, message_length);
|
|
||||||
// min_CP_duration needs to be at least max_comp_time. So we just use
|
// min_CP_duration needs to be at least max_comp_time. So we just use
|
||||||
// max_comp_time here for now.
|
// max_comp_time here for now.
|
||||||
AddUint16Param(MIN_CP_DURATION, config->max_comp_time, message,
|
simulcrypt_util::AddUint16Param(MIN_CP_DURATION, config->max_comp_time,
|
||||||
|
message, message_length);
|
||||||
|
simulcrypt_util::AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
|
||||||
|
simulcrypt_util::AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message,
|
||||||
message_length);
|
message_length);
|
||||||
AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
|
|
||||||
AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, message_length);
|
|
||||||
AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, message_length);
|
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
}
|
}
|
||||||
@@ -295,10 +242,14 @@ void BuildStreamError(uint16_t channel_id, uint16_t stream_id, uint16_t error_st
|
|||||||
char* message, size_t* message_length) {
|
char* message, size_t* message_length) {
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_STREAM_ERROR, message, message_length);
|
simulcrypt_util::BuildMessageHeader(
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_ERROR, message, message_length);
|
||||||
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message,
|
||||||
|
message_length);
|
||||||
// No setting Error_information parameter yet.
|
// No setting Error_information parameter yet.
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
@@ -309,12 +260,16 @@ void BuildStreamStatus(uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id,
|
|||||||
size_t* message_length) {
|
size_t* message_length) {
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_STREAM_STATUS, message, message_length);
|
simulcrypt_util::BuildMessageHeader(
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_STATUS, message, message_length);
|
||||||
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
AddUint16Param(ECM_ID, ecm_id, message, message_length);
|
message_length);
|
||||||
AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, access_criteria_transfer_mode,
|
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
|
||||||
message, message_length);
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ECM_ID, ecm_id, message, message_length);
|
||||||
|
simulcrypt_util::AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE,
|
||||||
|
access_criteria_transfer_mode, message,
|
||||||
|
message_length);
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
}
|
}
|
||||||
@@ -323,9 +278,13 @@ void BuildStreamCloseResponse(uint16_t channel_id, uint16_t stream_id,
|
|||||||
char* message, size_t* message_length) {
|
char* message, size_t* message_length) {
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_STREAM_CLOSE_RESPONSE, message, message_length);
|
simulcrypt_util::BuildMessageHeader(ECMG_SCS_PROTOCOL_VERSION,
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_STREAM_CLOSE_RESPONSE, message,
|
||||||
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
|
||||||
|
message_length);
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
}
|
}
|
||||||
@@ -336,11 +295,16 @@ void BuildEcmResponse(uint16_t channel_id, uint16_t stream_id, uint16_t cp_numbe
|
|||||||
DCHECK(ecm_datagram);
|
DCHECK(ecm_datagram);
|
||||||
DCHECK(message);
|
DCHECK(message);
|
||||||
DCHECK(message_length);
|
DCHECK(message_length);
|
||||||
BuildMessageHeader(ECMG_ECM_RESPONSE, message, message_length);
|
simulcrypt_util::BuildMessageHeader(
|
||||||
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
ECMG_SCS_PROTOCOL_VERSION, ECMG_ECM_RESPONSE, message, message_length);
|
||||||
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
|
||||||
AddUint16Param(CP_NUMBER, cp_number, message, message_length);
|
message_length);
|
||||||
AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, message_length);
|
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddUint16Param(CP_NUMBER, cp_number, message,
|
||||||
|
message_length);
|
||||||
|
simulcrypt_util::AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message,
|
||||||
|
message_length);
|
||||||
uint16_t total_param_length = *message_length - 5;
|
uint16_t total_param_length = *message_length - 5;
|
||||||
Host16ToBigEndian(message + 3, &total_param_length);
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,27 +56,31 @@ TEST_F(EcmgClientHandlerTest, SuccessSequence) {
|
|||||||
char response[BUFFER_SIZE];
|
char response[BUFFER_SIZE];
|
||||||
size_t response_length;
|
size_t response_length;
|
||||||
|
|
||||||
client_handler_->HandleRequest(kTestChannelSetup, response, &response_length);
|
client_handler_->HandleRequest(kTestEcmgChannelSetup, response,
|
||||||
|
&response_length);
|
||||||
EXPECT_EQ(62, response_length);
|
EXPECT_EQ(62, response_length);
|
||||||
EXPECT_EQ(0, memcmp(kTestChannelStatus, response, response_length));
|
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response, response_length));
|
||||||
|
|
||||||
client_handler_->HandleRequest(kTestStreamSetup, response, &response_length);
|
client_handler_->HandleRequest(kTestEcmgStreamSetup, response,
|
||||||
|
&response_length);
|
||||||
EXPECT_EQ(28, response_length);
|
EXPECT_EQ(28, response_length);
|
||||||
EXPECT_EQ(0, memcmp(kTestStreamStatus, response, response_length));
|
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response, response_length));
|
||||||
|
|
||||||
client_handler_->HandleRequest(kTestCwProvision, response, &response_length);
|
client_handler_->HandleRequest(kTestEcmgCwProvision, response,
|
||||||
|
&response_length);
|
||||||
EXPECT_EQ(215, response_length);
|
EXPECT_EQ(215, response_length);
|
||||||
// Only comparing the bytes in front of the ECM_datagram, because
|
// Only comparing the bytes in front of the ECM_datagram, because
|
||||||
// random wrapping IV is generated each time causing the ECM_datagram
|
// random wrapping IV is generated each time causing the ECM_datagram
|
||||||
// to be non-deterministic.
|
// to be non-deterministic.
|
||||||
EXPECT_EQ(0, memcmp(kTestEcmResponse, response, 27));
|
EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response, 27));
|
||||||
|
|
||||||
client_handler_->HandleRequest(kTestStreamCloseRequest, response,
|
client_handler_->HandleRequest(kTestEcmgStreamCloseRequest, response,
|
||||||
&response_length);
|
&response_length);
|
||||||
EXPECT_EQ(17, response_length);
|
EXPECT_EQ(17, response_length);
|
||||||
EXPECT_EQ(0, memcmp(kTestStreamCloseResponse, response, response_length));
|
EXPECT_EQ(0, memcmp(kTestEcmgStreamCloseResponse, response, response_length));
|
||||||
|
|
||||||
client_handler_->HandleRequest(kTestChannelClose, response, &response_length);
|
client_handler_->HandleRequest(kTestEcmgChannelClose, response,
|
||||||
|
&response_length);
|
||||||
EXPECT_EQ(0, response_length);
|
EXPECT_EQ(0, response_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,35 +62,30 @@
|
|||||||
|
|
||||||
// ECMG protocol error values.
|
// ECMG protocol error values.
|
||||||
#define INVAID_MESSAGE (0x0001)
|
#define INVAID_MESSAGE (0x0001)
|
||||||
#define UNSUPPORTED_PROTOCOL_VERSION (0X0002)
|
#define UNSUPPORTED_PROTOCOL_VERSION (0x0002)
|
||||||
#define UNKNOWN_MESSAGE_TYPE_VALUE (0X0003)
|
#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003)
|
||||||
#define MESSAGE_TOO_LONG (0X0004)
|
#define MESSAGE_TOO_LONG (0x0004)
|
||||||
#define UNKNOWN_SUPER_CAS_ID_VALUE (0X0005)
|
#define UNKNOWN_SUPER_CAS_ID_VALUE (0x0005)
|
||||||
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0X0006)
|
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0x0006)
|
||||||
#define UNKNOWN_ECM_STREAM_ID_VALUE (0X0007)
|
#define UNKNOWN_ECM_STREAM_ID_VALUE (0x0007)
|
||||||
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0X0008)
|
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0x0008)
|
||||||
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0X0009)
|
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0x0009)
|
||||||
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0X000A)
|
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0x000A)
|
||||||
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0X000B)
|
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0x000B)
|
||||||
#define ECMG_OUT_OF_STORAGE_CAPACITY (0X000C)
|
#define ECMG_OUT_OF_STORAGE_CAPACITY (0x000C)
|
||||||
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0X000D)
|
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0x000D)
|
||||||
#define UNKNOWN_PARAMETER_TYPE_VALUE (0X000E)
|
#define UNKNOWN_PARAMETER_TYPE_VALUE (0x000E)
|
||||||
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0X000F)
|
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000F)
|
||||||
#define MISSING_MANDATORY_DVB_PARAMETER (0X0010)
|
#define MISSING_MANDATORY_DVB_PARAMETER (0x0010)
|
||||||
#define INVALID_VALUE_FOR_DVB_PARAMETER (0X0011)
|
#define INVALID_VALUE_FOR_DVB_PARAMETER (0x0011)
|
||||||
#define UNKNOWN_ECM_ID_VALUE (0X0012)
|
#define UNKNOWN_ECM_ID_VALUE (0x0012)
|
||||||
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0X0013)
|
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0013)
|
||||||
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0X0014)
|
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0x0014)
|
||||||
#define ECM_ID_VALUE_ALREADY_IN_USE (0X0015)
|
#define ECM_ID_VALUE_ALREADY_IN_USE (0x0015)
|
||||||
#define UNKNOWN_ERROR (0X7000)
|
#define UNKNOWN_ERROR (0x7000)
|
||||||
#define UNRECOVERABLE_ERROR (0X7001)
|
#define UNRECOVERABLE_ERROR (0x7001)
|
||||||
|
|
||||||
// Size (in # of bytes) of various fields.
|
// Size (in # of bytes) of various fields.
|
||||||
#define PROTOCOL_VERSION_SIZE (1)
|
|
||||||
#define MESSAGE_TYPE_SIZE (2)
|
|
||||||
#define MESSAGE_LENGTH_SIZE (2)
|
|
||||||
#define PARAMETER_TYPE_SIZE (2)
|
|
||||||
#define PARAMETER_LENGTH_SIZE (2)
|
|
||||||
#define SUPER_CAS_ID_SIZE (4)
|
#define SUPER_CAS_ID_SIZE (4)
|
||||||
#define ECM_CHANNEL_ID_SIZE (2)
|
#define ECM_CHANNEL_ID_SIZE (2)
|
||||||
#define ECM_ID_SIZE (2)
|
#define ECM_ID_SIZE (2)
|
||||||
|
|||||||
247
media_cas_packager_sdk/internal/emmg.cc
Normal file
247
media_cas_packager_sdk/internal/emmg.cc
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "example/test_emmg_messages.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/emmg_constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void Print(const char* const msg, size_t msg_size) {
|
||||||
|
for (size_t i = 0; i < msg_size; i++) {
|
||||||
|
printf("'\\x%02x', ", static_cast<uint16_t>(*(msg + i)));
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MessageTypeToString(uint16_t message_type) {
|
||||||
|
switch (message_type) {
|
||||||
|
case EMMG_CHANNEL_SETUP:
|
||||||
|
return "EMMG_CHANNEL_SETUP";
|
||||||
|
case EMMG_CHANNEL_STATUS:
|
||||||
|
return "EMMG_CHANNEL_STATUS";
|
||||||
|
case EMMG_CHANNEL_CLOSE:
|
||||||
|
return "EMMG_CHANNEL_CLOSE";
|
||||||
|
case EMMG_CHANNEL_ERROR:
|
||||||
|
return "EMMG_CHANNEL_ERROR";
|
||||||
|
case EMMG_STREAM_SETUP:
|
||||||
|
return "EMMG_STREAM_SETUP";
|
||||||
|
case EMMG_STREAM_TEST:
|
||||||
|
return "EMMG_STREAM_TEST";
|
||||||
|
case EMMG_STREAM_STATUS:
|
||||||
|
return "EMMG_STREAM_STATUS";
|
||||||
|
case EMMG_STREAM_CLOSE_REQUEST:
|
||||||
|
return "EMMG_STREAM_CLOSE_REQUEST";
|
||||||
|
case EMMG_STREAM_CLOSE_RESPONSE:
|
||||||
|
return "EMMG_STREAM_CLOSE_RESPONSE";
|
||||||
|
case EMMG_STREAM_ERROR:
|
||||||
|
return "EMMG_STREAM_ERROR";
|
||||||
|
case EMMG_STREAM_BW_REQUEST:
|
||||||
|
return "EMMG_STREAM_BW_REQUEST";
|
||||||
|
case EMMG_STREAM_BW_ALLOCATION:
|
||||||
|
return "";
|
||||||
|
case EMMG_DATA_PROVISION:
|
||||||
|
return "EMMG_DATA_PROVISION";
|
||||||
|
default:
|
||||||
|
return "UNRECOGNIZED_MESSAGE_TYPE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Emmg::Emmg(EmmgConfig* emmg_config, int server_socket_fd) {
|
||||||
|
CHECK(emmg_config);
|
||||||
|
emmg_config_ = emmg_config;
|
||||||
|
server_socket_fd_ = server_socket_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::Start() {
|
||||||
|
SendChannelSetup();
|
||||||
|
SendStreamSetup();
|
||||||
|
// TODO(user): Send stream_BW_request message.
|
||||||
|
// TODO(user): Send stream_BW_allocation message.
|
||||||
|
SendDataProvision();
|
||||||
|
SendStreamCloseRequest();
|
||||||
|
SendChannelClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::BuildChannelSetup() {
|
||||||
|
bzero(request_, BUFFER_SIZE);
|
||||||
|
request_length_ = 0;
|
||||||
|
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
|
||||||
|
EMMG_CHANNEL_SETUP, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
|
||||||
|
request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
|
||||||
|
emmg_config_->data_channel_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint8Param(EMMG_SECTION_TSPKT_FLAG,
|
||||||
|
emmg_config_->section_tspkt_flag, request_,
|
||||||
|
&request_length_);
|
||||||
|
uint16_t total_param_length = request_length_ - 5;
|
||||||
|
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::BuildStreamSetup() {
|
||||||
|
bzero(request_, BUFFER_SIZE);
|
||||||
|
request_length_ = 0;
|
||||||
|
simulcrypt_util::BuildMessageHeader(
|
||||||
|
EMMG_MUX_PROTOCOL_VERSION, EMMG_STREAM_SETUP, request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
|
||||||
|
request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
|
||||||
|
emmg_config_->data_channel_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
|
||||||
|
emmg_config_->data_stream_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint8Param(EMMG_DATA_TYPE, emmg_config_->data_type,
|
||||||
|
request_, &request_length_);
|
||||||
|
uint16_t total_param_length = request_length_ - 5;
|
||||||
|
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::BuildDataProvision() {
|
||||||
|
bzero(request_, BUFFER_SIZE);
|
||||||
|
request_length_ = 0;
|
||||||
|
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
|
||||||
|
EMMG_DATA_PROVISION, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
|
||||||
|
request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
|
||||||
|
emmg_config_->data_channel_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
|
||||||
|
emmg_config_->data_stream_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
|
||||||
|
// Add a fake TS packet.
|
||||||
|
uint16_t datagram_type = EMMG_DATAGRAM;
|
||||||
|
Host16ToBigEndian(request_ + request_length_, &datagram_type);
|
||||||
|
request_length_ += 2;
|
||||||
|
uint16_t param_length = sizeof(kTestEmmgTsPacket); // Should be 188.
|
||||||
|
Host16ToBigEndian(request_ + request_length_, ¶m_length);
|
||||||
|
request_length_ += 2;
|
||||||
|
memcpy(request_ + request_length_, kTestEmmgTsPacket, param_length);
|
||||||
|
request_length_ += param_length;
|
||||||
|
|
||||||
|
uint16_t total_param_length = request_length_ - 5;
|
||||||
|
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::BuildStreamCloseRequest() {
|
||||||
|
bzero(request_, BUFFER_SIZE);
|
||||||
|
request_length_ = 0;
|
||||||
|
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
|
||||||
|
EMMG_STREAM_CLOSE_REQUEST, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
|
||||||
|
request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
|
||||||
|
emmg_config_->data_channel_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
|
||||||
|
emmg_config_->data_stream_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
uint16_t total_param_length = request_length_ - 5;
|
||||||
|
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::BuildChannelClose() {
|
||||||
|
bzero(request_, BUFFER_SIZE);
|
||||||
|
request_length_ = 0;
|
||||||
|
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
|
||||||
|
EMMG_CHANNEL_CLOSE, request_,
|
||||||
|
&request_length_);
|
||||||
|
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
|
||||||
|
request_, &request_length_);
|
||||||
|
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
|
||||||
|
emmg_config_->data_channel_id, request_,
|
||||||
|
&request_length_);
|
||||||
|
uint16_t total_param_length = request_length_ - 5;
|
||||||
|
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::SendChannelSetup() {
|
||||||
|
BuildChannelSetup();
|
||||||
|
Send(EMMG_CHANNEL_SETUP);
|
||||||
|
ReceiveResponseAndVerify(EMMG_CHANNEL_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::SendStreamSetup() {
|
||||||
|
BuildStreamSetup();
|
||||||
|
Send(EMMG_STREAM_SETUP);
|
||||||
|
ReceiveResponseAndVerify(EMMG_STREAM_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::SendDataProvision() {
|
||||||
|
BuildDataProvision();
|
||||||
|
Send(EMMG_DATA_PROVISION);
|
||||||
|
// No response for Data_provision message.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::SendStreamCloseRequest() {
|
||||||
|
BuildStreamCloseRequest();
|
||||||
|
Send(EMMG_STREAM_CLOSE_REQUEST);
|
||||||
|
ReceiveResponseAndVerify(EMMG_STREAM_CLOSE_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::SendChannelClose() {
|
||||||
|
BuildChannelClose();
|
||||||
|
Send(EMMG_CHANNEL_CLOSE);
|
||||||
|
// No response for Channel_close message.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::ReceiveResponseAndVerify(uint16_t expected_type) {
|
||||||
|
// Read response from socket.
|
||||||
|
bzero(response_, BUFFER_SIZE);
|
||||||
|
response_length_ = recv(server_socket_fd_, response_, BUFFER_SIZE, 0);
|
||||||
|
if (response_length_ == 0) {
|
||||||
|
LOG(FATAL) << "Expect a response from server but nothing was received";
|
||||||
|
}
|
||||||
|
// Verify request type (2nd and 3rd bytes) match the expected type.
|
||||||
|
uint16_t request_type;
|
||||||
|
BigEndianToHost16(&request_type, response_ + 1);
|
||||||
|
if (request_type != expected_type) {
|
||||||
|
LOG(ERROR) << "Unexpected non " << MessageTypeToString(expected_type)
|
||||||
|
<< " type message received";
|
||||||
|
LOG(ERROR) << "Response: ";
|
||||||
|
Print(response_, response_length_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emmg::Send(uint16_t message_type) {
|
||||||
|
if (send(server_socket_fd_, request_, request_length_, 0) < 0) {
|
||||||
|
LOG(FATAL) << "Failed to send " << MessageTypeToString(message_type)
|
||||||
|
<< " message to server";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
80
media_cas_packager_sdk/internal/emmg.h
Normal file
80
media_cas_packager_sdk/internal/emmg.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (1024)
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
// A struct that captures the Emmg configs.
|
||||||
|
struct EmmgConfig {
|
||||||
|
uint32_t client_id;
|
||||||
|
uint8_t section_tspkt_flag;
|
||||||
|
uint16_t data_channel_id;
|
||||||
|
uint16_t data_stream_id;
|
||||||
|
uint16_t data_id;
|
||||||
|
uint8_t data_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class that sends EMMG/PDG message to the MUX server.
|
||||||
|
class Emmg {
|
||||||
|
public:
|
||||||
|
Emmg(EmmgConfig* emmg_config, int server_socket_fd);
|
||||||
|
Emmg(const Emmg&) = delete;
|
||||||
|
Emmg& operator=(const Emmg&) = delete;
|
||||||
|
virtual ~Emmg() = default;
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Protected visibility for unit testing.
|
||||||
|
void BuildChannelSetup();
|
||||||
|
void BuildStreamSetup();
|
||||||
|
void BuildDataProvision();
|
||||||
|
void BuildStreamCloseRequest();
|
||||||
|
void BuildChannelClose();
|
||||||
|
|
||||||
|
size_t request_length_ = 0;
|
||||||
|
char request_[BUFFER_SIZE];
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendChannelSetup();
|
||||||
|
void SendStreamSetup();
|
||||||
|
void SendDataProvision();
|
||||||
|
void SendStreamCloseRequest();
|
||||||
|
void SendChannelClose();
|
||||||
|
|
||||||
|
void ReceiveResponseAndVerify(uint16_t expected_type);
|
||||||
|
void Send(uint16_t message_type);
|
||||||
|
|
||||||
|
char response_[BUFFER_SIZE];
|
||||||
|
size_t response_length_ = 0;
|
||||||
|
|
||||||
|
EmmgConfig* emmg_config_;
|
||||||
|
// |server_socket_fd| is a file descriptor we can use to communicate
|
||||||
|
// with the MUX server.
|
||||||
|
int server_socket_fd_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
|
||||||
68
media_cas_packager_sdk/internal/emmg_constants.h
Normal file
68
media_cas_packager_sdk/internal/emmg_constants.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_
|
||||||
|
|
||||||
|
// EMMG/PDG <=> MUX protocol_version.
|
||||||
|
#define EMMG_MUX_PROTOCOL_VERSION (0x02)
|
||||||
|
|
||||||
|
// EMMG message type values.
|
||||||
|
#define EMMG_CHANNEL_SETUP (0x0011)
|
||||||
|
#define EMMG_CHANNEL_TEST (0x0012)
|
||||||
|
#define EMMG_CHANNEL_STATUS (0x0013)
|
||||||
|
#define EMMG_CHANNEL_CLOSE (0x0014)
|
||||||
|
#define EMMG_CHANNEL_ERROR (0x0015)
|
||||||
|
#define EMMG_STREAM_SETUP (0x0111)
|
||||||
|
#define EMMG_STREAM_TEST (0x0112)
|
||||||
|
#define EMMG_STREAM_STATUS (0x0113)
|
||||||
|
#define EMMG_STREAM_CLOSE_REQUEST (0x0114)
|
||||||
|
#define EMMG_STREAM_CLOSE_RESPONSE (0x0115)
|
||||||
|
#define EMMG_STREAM_ERROR (0x0116)
|
||||||
|
#define EMMG_STREAM_BW_REQUEST (0x0117)
|
||||||
|
#define EMMG_STREAM_BW_ALLOCATION (0x0118)
|
||||||
|
#define EMMG_DATA_PROVISION (0x0211)
|
||||||
|
|
||||||
|
// EMMG parameter type values.
|
||||||
|
#define EMMG_DVB_RESERVED (0x0000)
|
||||||
|
#define EMMG_CLIENT_ID (0x0001)
|
||||||
|
#define EMMG_SECTION_TSPKT_FLAG (0x0002)
|
||||||
|
#define EMMG_DATA_CHANNEL_ID (0x0003)
|
||||||
|
#define EMMG_DATA_STREAM_ID (0x0004)
|
||||||
|
#define EMMG_DATAGRAM (0x0005)
|
||||||
|
#define EMMG_BANDWIDTH (0x0006)
|
||||||
|
#define EMMG_DATA_TYPE (0x0007)
|
||||||
|
#define EMMG_DATA_ID (0x0008)
|
||||||
|
#define EMMG_ERROR_STATUS (0x7000)
|
||||||
|
#define EMMG_ERROR_INFORMATION (0x7001)
|
||||||
|
|
||||||
|
// EMMG protocol error values.
|
||||||
|
#define INVALID_MESSAGE (0x0001)
|
||||||
|
#define UNSUPPORTED_PROTOCOL_VERSION (0x0002)
|
||||||
|
#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003)
|
||||||
|
#define MESSAGE_TOO_LONG (0x0004)
|
||||||
|
#define UNKNOWN_DATA_STREAM_ID_VALUE (0x0005)
|
||||||
|
#define UNKNOWN_DATA_CHANNEL_ID_VALUE (0x0006)
|
||||||
|
#define TOO_MANY_CHANNELS_ON_THIS_MUX (0x0007)
|
||||||
|
#define TOO_MANY_DATA_STREAMS_ON_THIS_CHANNEL (0x0008)
|
||||||
|
#define TOO_MANY_DATA_STREAMS_ON_THIS_MUX (0x0009)
|
||||||
|
#define UNKNOWN_PARAMETER_TYPE (0x000A)
|
||||||
|
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000B)
|
||||||
|
#define MISSING_MANDATORY_DVB_PARAMETER (0x000C)
|
||||||
|
#define INVALID_VALUE_FOR_DVB_PARAMETER (0x000D)
|
||||||
|
#define UNKNOWN_CLIENT_ID_VALUE (0x000E)
|
||||||
|
#define EXCEEDED_BANDWIDTH (0x000F)
|
||||||
|
#define UNKNOWN_DATA_ID_VALUE (0x0010)
|
||||||
|
#define DATA_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0011)
|
||||||
|
#define DATA_STREAM_ID_VALUE_ALREADY_IN_USE (0x0012)
|
||||||
|
#define DATA_ID_VALUE_ALREADY_IN_USE (0x0013)
|
||||||
|
#define CLIENT_ID_VALUE_ALREADY_IN_USE (0x0014)
|
||||||
|
#define UNKNOWN_ERROR (0x7000)
|
||||||
|
#define UNRECOVERABLE_ERROR (0x7001)
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_
|
||||||
93
media_cas_packager_sdk/internal/emmg_test.cc
Normal file
93
media_cas_packager_sdk/internal/emmg_test.cc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||||
|
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "example/test_emmg_messages.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestableEmmg : public Emmg {
|
||||||
|
public:
|
||||||
|
explicit TestableEmmg(EmmgConfig* emmg_config)
|
||||||
|
: Emmg(emmg_config, /* server_socket_fd= */ 1) {}
|
||||||
|
void PublicBuildChannelSetup() { BuildChannelSetup(); }
|
||||||
|
void PublicBuildStreamSetup() { BuildStreamSetup(); }
|
||||||
|
void PublicBuildDataProvision() { BuildDataProvision(); }
|
||||||
|
void PublicBuildStreamCloseRequest() { BuildStreamCloseRequest(); }
|
||||||
|
void PublicBuildChannelClose() { BuildChannelClose(); }
|
||||||
|
char* GetRequest() { return request_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class EmmgTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
EmmgTest() {
|
||||||
|
config_.client_id = 0x4AD40000;
|
||||||
|
config_.section_tspkt_flag = 0x01;
|
||||||
|
config_.data_channel_id = 0x0001;
|
||||||
|
config_.data_stream_id = 0x0001;
|
||||||
|
config_.data_id = 0x0001;
|
||||||
|
config_.data_type = 0x01;
|
||||||
|
emmg_ = absl::make_unique<TestableEmmg>(&config_);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Helper function for debugging the tests.
|
||||||
|
void PrintMessage(const std::string& description, const char* const message,
|
||||||
|
size_t length) {
|
||||||
|
printf("%s ", description.c_str());
|
||||||
|
fflush(stdout);
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmmgConfig config_;
|
||||||
|
std::unique_ptr<TestableEmmg> emmg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(EmmgTest, BuildChannelSetup) {
|
||||||
|
emmg_->PublicBuildChannelSetup();
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEmmgChannelSetup, emmg_->GetRequest(),
|
||||||
|
sizeof(kTestEmmgChannelSetup)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmmgTest, BuildStreamSetup) {
|
||||||
|
emmg_->PublicBuildStreamSetup();
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEmmgStreamSetup, emmg_->GetRequest(),
|
||||||
|
sizeof(kTestEmmgStreamSetup)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmmgTest, BuildDataProvision) {
|
||||||
|
emmg_->PublicBuildDataProvision();
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEmmgDataProvision, emmg_->GetRequest(),
|
||||||
|
sizeof(kTestEmmgDataProvision)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmmgTest, BuildStreamCloseRequest) {
|
||||||
|
emmg_->PublicBuildStreamCloseRequest();
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(),
|
||||||
|
sizeof(kTestEmmgStreamCloseRequest)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EmmgTest, BuildChannelClose) {
|
||||||
|
emmg_->PublicBuildChannelClose();
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEmmgChannelClose, emmg_->GetRequest(),
|
||||||
|
sizeof(kTestEmmgChannelClose)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
19
media_cas_packager_sdk/internal/simulcrypt_constants.h
Normal file
19
media_cas_packager_sdk/internal/simulcrypt_constants.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
||||||
|
|
||||||
|
// Size (in # of bytes) of various fields.
|
||||||
|
#define PROTOCOL_VERSION_SIZE (1)
|
||||||
|
#define MESSAGE_TYPE_SIZE (2)
|
||||||
|
#define MESSAGE_LENGTH_SIZE (2)
|
||||||
|
#define PARAMETER_TYPE_SIZE (2)
|
||||||
|
#define PARAMETER_LENGTH_SIZE (2)
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
||||||
93
media_cas_packager_sdk/internal/simulcrypt_util.cc
Normal file
93
media_cas_packager_sdk/internal/simulcrypt_util.cc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
namespace simulcrypt_util {
|
||||||
|
|
||||||
|
void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type,
|
||||||
|
char* message, size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
*message_length = 0;
|
||||||
|
message[*message_length] = protocol_version;
|
||||||
|
*message_length += PROTOCOL_VERSION_SIZE;
|
||||||
|
Host16ToBigEndian(message + *message_length, &message_type);
|
||||||
|
*message_length += MESSAGE_TYPE_SIZE;
|
||||||
|
// NOTE: 'message_length' needs to be updated later after we have added all
|
||||||
|
// the params so we know the exact length of the all the params.
|
||||||
|
// Use 0 for 'total_param_length' until we know the length of the message.
|
||||||
|
uint16_t total_param_length = 0;
|
||||||
|
Host16ToBigEndian(message + *message_length, &total_param_length);
|
||||||
|
*message_length += MESSAGE_LENGTH_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
uint16_t param_length = 4;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
Host32ToBigEndian(message + *message_length, ¶m_value);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
uint16_t param_length = 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_value);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
uint16_t param_length = 1;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
memcpy(message + *message_length, ¶m_value, param_length);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
|
||||||
|
char* message, size_t* message_length) {
|
||||||
|
DCHECK(param_value);
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
memcpy(message + *message_length, param_value, param_length);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace simulcrypt_util
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
59
media_cas_packager_sdk/internal/simulcrypt_util.h
Normal file
59
media_cas_packager_sdk/internal/simulcrypt_util.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Utility functions for building Simulcrypt messages.
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
namespace simulcrypt_util {
|
||||||
|
|
||||||
|
// Add 'protocol_version', 'message_type', 'message_length' to the message.
|
||||||
|
// TODO(user): Per jfore@, consider pass in a pointer to a structure
|
||||||
|
// #pragma pack(push, 1) // exact fit - no padding
|
||||||
|
// struct MessageHeader{
|
||||||
|
// uint8_t protocol_version;
|
||||||
|
// uint16_t message_type;
|
||||||
|
// uint16_t message_length;
|
||||||
|
// };
|
||||||
|
// #pragma pack(pop) // restore previous pack
|
||||||
|
void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type,
|
||||||
|
char* message, size_t* message_length);
|
||||||
|
|
||||||
|
// Add a uint32_t parameter to the message.
|
||||||
|
void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message,
|
||||||
|
size_t* message_length);
|
||||||
|
|
||||||
|
// Add a uint16_t parameter to the message.
|
||||||
|
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
|
||||||
|
size_t* message_length);
|
||||||
|
|
||||||
|
// Add a uint8_t parameter to the message.
|
||||||
|
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
|
||||||
|
size_t* message_length);
|
||||||
|
|
||||||
|
// Add a param that is |param_length| bytes long.
|
||||||
|
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
|
||||||
|
char* message, size_t* message_length);
|
||||||
|
|
||||||
|
} // namespace simulcrypt_util
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
|
||||||
@@ -57,6 +57,13 @@ void Host16ToBigEndian(void* destination, const int16_t* source) {
|
|||||||
memcpy(destination, &big_endian_number, 2);
|
memcpy(destination, &big_endian_number, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host32ToBigEndian(void* destination, const uint32_t* source) {
|
||||||
|
DCHECK(destination);
|
||||||
|
DCHECK(source);
|
||||||
|
uint32_t big_endian_number = htonl(*source);
|
||||||
|
memcpy(destination, &big_endian_number, 4);
|
||||||
|
}
|
||||||
|
|
||||||
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
|
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
|
||||||
ContinuityCounter* cc, uint8_t* buffer,
|
ContinuityCounter* cc, uint8_t* buffer,
|
||||||
ssize_t* bytes_modified) {
|
ssize_t* bytes_modified) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ void BigEndianToHost32(uint32_t* destination, const void* source);
|
|||||||
// |destination|.
|
// |destination|.
|
||||||
void Host16ToBigEndian(void* destination, const uint16_t* source);
|
void Host16ToBigEndian(void* destination, const uint16_t* source);
|
||||||
void Host16ToBigEndian(void* destination, const int16_t* source);
|
void Host16ToBigEndian(void* destination, const int16_t* source);
|
||||||
|
void Host32ToBigEndian(void* destination, const uint32_t* source);
|
||||||
|
|
||||||
// Packages an ECM as a TS packet and inserts it into a buffer.
|
// Packages an ECM as a TS packet and inserts it into a buffer.
|
||||||
// Args:
|
// Args:
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ filegroup(
|
|||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = glob(["*.h"]) + [
|
srcs = glob(["*.h"]) + [
|
||||||
":wv_ecmg",
|
":wv_ecmg",
|
||||||
|
"wv_cas_key_fetcher.cc",
|
||||||
|
"wv_cas_types.cc",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -127,7 +129,9 @@ cc_library(
|
|||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@curl_repo//:curl",
|
"@curl_repo//:curl",
|
||||||
|
"//util:error_space",
|
||||||
"//common:signature_util",
|
"//common:signature_util",
|
||||||
|
"//common:status",
|
||||||
"//media_cas_packager_sdk/internal:key_fetcher",
|
"//media_cas_packager_sdk/internal:key_fetcher",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
],
|
],
|
||||||
@@ -181,3 +185,12 @@ cc_binary(
|
|||||||
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "wv_emmg",
|
||||||
|
srcs = ["wv_emmg.cc"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//media_cas_packager_sdk/internal:emmg",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -103,8 +103,6 @@ Status WvCasKeyFetcher::RequestEntitlementKey(const std::string& request_string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Processes signed response.
|
// Processes signed response.
|
||||||
// TODO(b/114741232): Seems we are getting CasEncryptionResponse back instead
|
|
||||||
// of SignedCasEncryptionResponse.
|
|
||||||
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
|
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
|
||||||
CasEncryptionResponse response;
|
CasEncryptionResponse response;
|
||||||
if (!JsonStringToMessage(http_response.response(), &response).ok()) {
|
if (!JsonStringToMessage(http_response.response(), &response).ok()) {
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ bool StringToCryptoMode(const std::string& str, CryptoMode* mode) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
|
WvCasStatus CreateWvCasEncryptionRequestJson(
|
||||||
std::string* request_json) {
|
const WvCasEncryptionRequest& request, std::string* request_json) {
|
||||||
CHECK(request_json);
|
CHECK(request_json);
|
||||||
|
|
||||||
CasEncryptionRequest request_proto;
|
CasEncryptionRequest request_proto;
|
||||||
@@ -100,23 +100,23 @@ Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
|
|||||||
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields
|
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields
|
||||||
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
|
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
|
||||||
if (!MessageToJsonString(request_proto, request_json, print_options).ok()) {
|
if (!MessageToJsonString(request_proto, request_json, print_options).ok()) {
|
||||||
return Status(error::INTERNAL,
|
LOG(ERROR) << "Failed to convert request message to json.";
|
||||||
"Failed to convert request message to json.");
|
return INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OkStatus();
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
|
WvCasStatus ParseWvCasEncryptionResponseJson(
|
||||||
WvCasEncryptionResponse* response) {
|
const std::string& response_json, WvCasEncryptionResponse* response) {
|
||||||
CHECK(response);
|
CHECK(response);
|
||||||
|
|
||||||
CasEncryptionResponse response_proto;
|
CasEncryptionResponse response_proto;
|
||||||
// NOTE: JsonStringToMessage will automatically perform base64 decode for
|
// NOTE: JsonStringToMessage will automatically perform base64 decode for
|
||||||
// 'bytes' type fields.
|
// 'bytes' type fields.
|
||||||
if (!JsonStringToMessage(response_json, &response_proto).ok()) {
|
if (!JsonStringToMessage(response_json, &response_proto).ok()) {
|
||||||
return Status(error::INTERNAL,
|
LOG(ERROR) << "Failed to convert response json to message.";
|
||||||
"Failed to convert response json to message.");
|
return INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
response->status =
|
response->status =
|
||||||
@@ -133,7 +133,7 @@ Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
|
|||||||
response->entitlement_keys.push_back(key_info);
|
response->entitlement_keys.push_back(key_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OkStatus();
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -114,14 +114,14 @@ struct WvCasEncryptionResponse {
|
|||||||
// request JSON message.
|
// request JSON message.
|
||||||
// And that signed JSON message can be sent to Widevine license server for
|
// And that signed JSON message can be sent to Widevine license server for
|
||||||
// aquiring entitlement keys.
|
// aquiring entitlement keys.
|
||||||
Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
|
WvCasStatus CreateWvCasEncryptionRequestJson(
|
||||||
std::string* request_json);
|
const WvCasEncryptionRequest& request, std::string* request_json);
|
||||||
|
|
||||||
// Parses a WvCasEncryptionResponse in JSON format, returns a
|
// Parses a WvCasEncryptionResponse in JSON format, returns a
|
||||||
// WvCasEncryptionResponse.
|
// WvCasEncryptionResponse.
|
||||||
// |response_json| is supposed to be the 'response' field in the signed
|
// |response_json| is supposed to be the 'response' field in the signed
|
||||||
// response from Widevine license server.
|
// response from Widevine license server.
|
||||||
Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
|
WvCasStatus ParseWvCasEncryptionResponseJson(const std::string& response_json,
|
||||||
WvCasEncryptionResponse* response);
|
WvCasEncryptionResponse* response);
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ TEST(WvCasTypesTest, CreateWvCasEncryptionRequestJson) {
|
|||||||
request.key_rotation = true;
|
request.key_rotation = true;
|
||||||
|
|
||||||
std::string actual_request_json;
|
std::string actual_request_json;
|
||||||
EXPECT_OK(CreateWvCasEncryptionRequestJson(request, &actual_request_json));
|
EXPECT_EQ(OK,
|
||||||
|
CreateWvCasEncryptionRequestJson(request, &actual_request_json));
|
||||||
|
|
||||||
// Content_id has been base64 encoded.
|
// Content_id has been base64 encoded.
|
||||||
std::string expected_request_json =
|
std::string expected_request_json =
|
||||||
@@ -76,7 +77,8 @@ TEST(WvCasTypesTest, ParseWvCasEncryptionResponseJson) {
|
|||||||
"\"key_slot\":\"ODD\"}]}";
|
"\"key_slot\":\"ODD\"}]}";
|
||||||
|
|
||||||
WvCasEncryptionResponse actual_response;
|
WvCasEncryptionResponse actual_response;
|
||||||
EXPECT_OK(ParseWvCasEncryptionResponseJson(response_json, &actual_response));
|
EXPECT_EQ(OK,
|
||||||
|
ParseWvCasEncryptionResponseJson(response_json, &actual_response));
|
||||||
|
|
||||||
EXPECT_EQ(WvCasEncryptionResponse::Status::OK, actual_response.status);
|
EXPECT_EQ(WvCasEncryptionResponse::Status::OK, actual_response.status);
|
||||||
// 21140844 is base64 decode of "MjExNDA4NDQ=".
|
// 21140844 is base64 decode of "MjExNDA4NDQ=".
|
||||||
|
|||||||
@@ -6,15 +6,17 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Example server that listens on a port for Simulcrypt API messages.
|
// Widevine MediaCAS ECMG server.
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "gflags/gflags.h"
|
#include "gflags/gflags.h"
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
|||||||
92
media_cas_packager_sdk/public/wv_emmg.cc
Normal file
92
media_cas_packager_sdk/public/wv_emmg.cc
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Widevine MediaCAS EMMG server.
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||||
|
|
||||||
|
DEFINE_string(mux_address, "", "Mux server address");
|
||||||
|
DEFINE_int32(mux_port, 0, "Mux server port number");
|
||||||
|
|
||||||
|
// EMMG specfic configs.
|
||||||
|
DEFINE_int32(client_id, 0, "EMMG client_id");
|
||||||
|
DEFINE_int32(section_tspkt_flag, 1, "EMMG section_tspkt_flag");
|
||||||
|
DEFINE_int32(data_channel_id, 0, "EMMG data_channel_id");
|
||||||
|
DEFINE_int32(data_stream_id, 0, "EMMG data_stream_id");
|
||||||
|
DEFINE_int32(data_id, 0, "EMMG data_id");
|
||||||
|
// data_type: type of data carried in the datagram in the stream:
|
||||||
|
// 0x00: EMM;
|
||||||
|
// 0x01: private data;
|
||||||
|
// 0x02: DVB reserved (ECM);
|
||||||
|
// other values: DVB reserved.
|
||||||
|
DEFINE_int32(data_type, 0, "EMMG data_type");
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (1024)
|
||||||
|
|
||||||
|
void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
|
||||||
|
CHECK(config);
|
||||||
|
config->client_id = FLAGS_client_id;
|
||||||
|
config->section_tspkt_flag = FLAGS_section_tspkt_flag;
|
||||||
|
config->data_channel_id = FLAGS_data_channel_id;
|
||||||
|
config->data_stream_id = FLAGS_data_stream_id;
|
||||||
|
config->data_id = FLAGS_data_id;
|
||||||
|
config->data_type = FLAGS_data_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
CHECK(!FLAGS_mux_address.empty()) << "flag --mux_address is required";
|
||||||
|
CHECK(FLAGS_mux_port != 0) << "flag --mux_port is required";
|
||||||
|
|
||||||
|
// Create server address.
|
||||||
|
struct hostent ret;
|
||||||
|
struct hostent *server;
|
||||||
|
char buffer[BUFFER_SIZE];
|
||||||
|
int h_errnop = 0;
|
||||||
|
int return_val = gethostbyname_r(FLAGS_mux_address.c_str(), &ret, buffer,
|
||||||
|
sizeof(buffer), &server, &h_errnop);
|
||||||
|
if (return_val != 0 || server == nullptr) {
|
||||||
|
LOG(FATAL) << "gethostbyname_r failed for " << FLAGS_mux_address;
|
||||||
|
}
|
||||||
|
struct sockaddr_in server_address;
|
||||||
|
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
|
||||||
|
server_address.sin_family = AF_INET;
|
||||||
|
bcopy(server->h_addr,
|
||||||
|
reinterpret_cast<char *>(&server_address.sin_addr.s_addr),
|
||||||
|
server->h_length);
|
||||||
|
server_address.sin_port = htons(FLAGS_mux_port);
|
||||||
|
|
||||||
|
// Connect to server.
|
||||||
|
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (server_socket_fd < 0) {
|
||||||
|
LOG(FATAL) << "Failed to create socket.";
|
||||||
|
}
|
||||||
|
if (connect(server_socket_fd,
|
||||||
|
reinterpret_cast<struct sockaddr *>(&server_address),
|
||||||
|
sizeof(server_address)) < 0) {
|
||||||
|
LOG(FATAL) << "Failed to connect to server.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send EMMG messages.
|
||||||
|
widevine::cas::EmmgConfig emmg_config;
|
||||||
|
BuildEmmgConfig(&emmg_config);
|
||||||
|
widevine::cas::Emmg emmg(&emmg_config, server_socket_fd);
|
||||||
|
emmg.Start();
|
||||||
|
|
||||||
|
close(server_socket_fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user