Change order of loading certificates from pk7 cert
------------- Add libcurl to media_cas_packager_sdk. libcurl will later be used by a key fetcher to retrieve entitlement key from License Server using a HTTP request. ------------- Add a function named parsehelper to parse DCSL from the key smith response. ------------- Move wv_cas_key_fetcher to media_cas_packager_sdk so partners can use it request entitlement keys from License Server. ------------- Add pkcs7 write method to x509_cert.cc ------------- Update boringssl_repo to latest in master-with-bazel ------------- Add a TsPacket class to media_cas_packager_sdk to allow the construction of a ECM TS packet in the SDK. ------------- Move InsertEcm() from our internal CAS directory to the media_cas_packager_sdk, to be used to build a ECM TS packet by the SDK. ------------- Add METADATA in common folder ------------- Refactoring of certificate verification into DrmRootCertificate. ------------- Extend the default duration of leaf certificates. ------------- Fix moe_test ------------- Add a new method to WvCasEcm to allow partner to create a TS packet carrying the generated ECM. ------------- Change from SHA1 to SHA256 for Cast certificates ------------- Update crypto mode enumeration to match WV ECM document ------------- Fix the way we set the validity dates ------------- Move exported_root/util/status to common/ to prepare for util::Status migration Also added constructor/operator to copy from/to util::Status. ------------- Add GenerateDCSLrequest function to certificate_util.h. ------------- Fix build break ------------- Allow 'table_id' (in the section header) be specified by caller of SDK method WvCasEcm::GenerateTsPacket(). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=224535399
This commit is contained in:
@@ -120,6 +120,17 @@ cc_library(
|
||||
deps = ["//util:status"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "mpeg2ts",
|
||||
hdrs = [
|
||||
"mpeg2ts.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "simulcrypt",
|
||||
srcs = ["simulcrypt.cc"],
|
||||
@@ -149,11 +160,60 @@ cc_test(
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "util",
|
||||
srcs = ["util.cc"],
|
||||
hdrs = ["util.h"],
|
||||
name = "ts_packet",
|
||||
srcs = [
|
||||
"ts_packet.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"mpeg2ts.h",
|
||||
"ts_packet.h",
|
||||
],
|
||||
deps = [
|
||||
":mpeg2ts",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:status",
|
||||
"//common:string_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ts_packet_test",
|
||||
size = "small",
|
||||
srcs = ["ts_packet_test.cc"],
|
||||
deps = [
|
||||
":mpeg2ts",
|
||||
":ts_packet",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "util",
|
||||
srcs = ["util.cc"],
|
||||
hdrs = [
|
||||
"mpeg2ts.h",
|
||||
"util.h",
|
||||
],
|
||||
deps = [
|
||||
":mpeg2ts",
|
||||
":ts_packet",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "util_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"util_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":util",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -140,12 +140,7 @@ util::Status CasEcm::Initialize(const std::string& content_id,
|
||||
"Parameter content_iv_size must be kIvSize8 or kIvSize16."};
|
||||
}
|
||||
|
||||
if (ecm_init_parameters.crypto_mode == CryptoMode::kCryptoModeUnspecified) {
|
||||
return {util::error::INVALID_ARGUMENT, "Invalid crypto mode."};
|
||||
} else {
|
||||
crypto_mode_ = ecm_init_parameters.crypto_mode;
|
||||
}
|
||||
|
||||
crypto_mode_ = ecm_init_parameters.crypto_mode;
|
||||
content_id_ = content_id;
|
||||
content_provider_ = content_provider;
|
||||
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
|
||||
@@ -414,9 +409,6 @@ std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
||||
generation());
|
||||
std::bitset<kNumBitsDecryptModeField> decrypt_mode(
|
||||
static_cast<int>(crypto_mode()));
|
||||
if (decrypt_mode.to_string() == "00") {
|
||||
LOG(FATAL) << "Invalid decrypt mode \"00\"";
|
||||
}
|
||||
std::bitset<kNumBitsRotationEnabledField> rotation_enabled(
|
||||
RotationFieldValue(paired_keys_required()));
|
||||
std::bitset<kNumBitsWrappedKeyIvSizeField> wrapped_key_iv_size(
|
||||
|
||||
40
media_cas_packager_sdk/internal/mpeg2ts.h
Normal file
40
media_cas_packager_sdk/internal/mpeg2ts.h
Normal file
@@ -0,0 +1,40 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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_MPEG2TS_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_MPEG2TS_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
// MPEG2 TS Program ID (13 bits).
|
||||
typedef uint16_t ProgramId;
|
||||
// MPEG2 TS Continuity Counter (4 bits).
|
||||
typedef uint8_t ContinuityCounter;
|
||||
|
||||
constexpr ProgramId kInvalidPid = 0x2000;
|
||||
|
||||
constexpr size_t kTsPacketSize = 188;
|
||||
constexpr size_t kTsPacketHeaderSize = 4;
|
||||
constexpr size_t kMaxTsPayloadSize = kTsPacketSize - kTsPacketHeaderSize;
|
||||
|
||||
constexpr uint8_t kTsPacketSyncByte = 0x47;
|
||||
|
||||
constexpr uint8_t kTsPacketTableId80 = 0x80;
|
||||
constexpr uint8_t kTsPacketTableId81 = 0x81;
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_MPEG2TS_H_
|
||||
77
media_cas_packager_sdk/internal/ts_packet.cc
Normal file
77
media_cas_packager_sdk/internal/ts_packet.cc
Normal file
@@ -0,0 +1,77 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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/ts_packet.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "util/status.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
util::Status TsPacket::Write(std::string* output) const {
|
||||
DCHECK(output);
|
||||
output->resize(kTsPacketSize);
|
||||
|
||||
// TsPacket header.
|
||||
std::bitset<8> sync_byte(kTsPacketSyncByte);
|
||||
std::bitset<1> transport_error_indication(transport_error_indication_);
|
||||
std::bitset<1> payload_unit_start_indicator(payload_unit_start_indicator_);
|
||||
std::bitset<1> transport_priority(transport_priority_);
|
||||
std::bitset<13> pid(pid_);
|
||||
std::bitset<2> transport_scrambling_control(transport_scrambling_control_);
|
||||
std::bitset<2> adaptation_field_control(adaptation_field_control_);
|
||||
std::bitset<4> continuity_counter(continuity_counter_);
|
||||
if (adaptation_field_control_ & kAdaptationFieldOnly) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"TsPacket does NOT handle adaptation field yet");
|
||||
}
|
||||
|
||||
// Converts header bitset to string.
|
||||
std::string header_bitset = absl::StrCat(
|
||||
sync_byte.to_string(), transport_error_indication.to_string(),
|
||||
payload_unit_start_indicator.to_string(), transport_priority.to_string(),
|
||||
pid.to_string(), transport_scrambling_control.to_string(),
|
||||
adaptation_field_control.to_string(), continuity_counter.to_string());
|
||||
if (header_bitset.size() != 4 * 8) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
absl::StrCat("TS packet header bitset incorret size: ",
|
||||
header_bitset.size()));
|
||||
}
|
||||
std::string serialized_header;
|
||||
util::Status status = string_util::BitsetStringToBinaryString(
|
||||
header_bitset, &serialized_header);
|
||||
if (!status.ok()) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to convert TS packet header bitset to std::string");
|
||||
}
|
||||
|
||||
// Write TsPacket payload.
|
||||
if (payload_.size() != CalculatePayloadSize()) {
|
||||
return util::Status(
|
||||
util::error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Incorrect payload size: ", payload_.size()));
|
||||
}
|
||||
|
||||
// Return header + payload as a TS packet.
|
||||
*output = serialized_header + payload_;
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
int32_t TsPacket::CalculatePayloadSize() const {
|
||||
int32_t adaptation_length = 0;
|
||||
return kMaxTsPayloadSize - adaptation_length;
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
117
media_cas_packager_sdk/internal/ts_packet.h
Normal file
117
media_cas_packager_sdk/internal/ts_packet.h
Normal file
@@ -0,0 +1,117 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TS Packet is the basic unit of data in a transport stream, and a transport
|
||||
// stream is merely a sequence of packets, without any global header. Each
|
||||
// packet starts with a sync byte and a header, that may be followed with
|
||||
// optional additional headers; the rest of the packet consists of payload.
|
||||
// All header fields are read as big-endian. Packets are 188 bytes in length,
|
||||
// but the communication medium may add additional information:
|
||||
// Forward error correction is added by ISDB & DVB (16 bytes) and ATSC
|
||||
// (20 bytes),[4] while the M2TS format prefixes packets with a 4-byte
|
||||
// copyright and timestamp tag.
|
||||
//
|
||||
// TsPacket class is used to read data from the input stream or serialize data
|
||||
// into the output stream. In contrast to other classes/interfaces in
|
||||
// video/file, TsPacket does not use bidirectional BinaryIO interface, because
|
||||
// BinaryIO is 10-15% slower than BitReader/BitWriter, and for entity like
|
||||
// TsPacket this difference is tangible.
|
||||
//
|
||||
// TsPacket is a simple class. It does not know how to automatically update
|
||||
// AdaptationField::length and AdaptationFieldExtension::length. In order
|
||||
// to initialize TsPacket correctly, user has to set all its fields including
|
||||
// length. Length calculation helpers are provided, though:
|
||||
// * AdaptationFieldExtension::CalculateLength()
|
||||
// * AdaptationField::CalculateLength()
|
||||
// * TsPacket::PayloadSize()
|
||||
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "util/status.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
// NOTE(user): This class does not handle "adaptation field" or
|
||||
// "adaptation field extension" yet.
|
||||
// NOTE(user): This class does not support reading a TS packet yet.
|
||||
class TsPacket {
|
||||
public:
|
||||
enum AdaptationFieldControl {
|
||||
kAFCReserved = 0x00,
|
||||
kPayloadOnly = 0x01,
|
||||
kAdaptationFieldOnly = 0x02,
|
||||
kAdaptationFieldAndPayload = 0x03
|
||||
};
|
||||
|
||||
TsPacket() = default;
|
||||
TsPacket(const TsPacket&) = delete;
|
||||
TsPacket& operator=(const TsPacket&) = delete;
|
||||
virtual ~TsPacket() = default;
|
||||
|
||||
// Writes the packet into the provided output. Returns kOk on success.
|
||||
virtual util::Status Write(std::string* output) const;
|
||||
|
||||
// Returns the size of payload data for the current TS packet configuration.
|
||||
int32_t CalculatePayloadSize() const;
|
||||
|
||||
// Getters.
|
||||
bool transport_error_indication() const {
|
||||
return transport_error_indication_;
|
||||
}
|
||||
bool payload_unit_start_indicator() const {
|
||||
return payload_unit_start_indicator_;
|
||||
}
|
||||
bool transport_priority() const { return transport_priority_; }
|
||||
uint32_t pid() const { return pid_; }
|
||||
uint32_t transport_scrambling_control() const {
|
||||
return transport_scrambling_control_;
|
||||
}
|
||||
uint32_t adaptation_field_control() const { return adaptation_field_control_; }
|
||||
uint32_t continuity_counter() const { return continuity_counter_; }
|
||||
const std::string& payload() const { return payload_; }
|
||||
|
||||
// Setters.
|
||||
void set_transport_error_indication(bool v) {
|
||||
transport_error_indication_ = v;
|
||||
}
|
||||
void set_payload_unit_start_indicator(bool v) {
|
||||
payload_unit_start_indicator_ = v;
|
||||
}
|
||||
void set_transport_priority(bool v) { transport_priority_ = v; }
|
||||
void set_pid(uint32_t v) { pid_ = v; }
|
||||
void set_transport_scrambling_control(uint32_t v) {
|
||||
transport_scrambling_control_ = v;
|
||||
}
|
||||
void set_adaptation_field_control(uint32_t v) { adaptation_field_control_ = v; }
|
||||
void set_continuity_counter(uint32_t v) { continuity_counter_ = v; }
|
||||
void set_payload(const std::string& p) { payload_ = p; }
|
||||
|
||||
private:
|
||||
bool transport_error_indication_ = false;
|
||||
bool payload_unit_start_indicator_ = false;
|
||||
bool transport_priority_ = false;
|
||||
uint32_t pid_ = kInvalidPid;
|
||||
uint32_t transport_scrambling_control_ = 0;
|
||||
uint32_t adaptation_field_control_ = 0;
|
||||
uint32_t continuity_counter_ = 0;
|
||||
std::string payload_;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_TS_PACKET_H_
|
||||
84
media_cas_packager_sdk/internal/ts_packet_test.cc
Normal file
84
media_cas_packager_sdk/internal/ts_packet_test.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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/ts_packet.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
class TsPacketTest : public ::testing::Test {
|
||||
public:
|
||||
TsPacketTest() = default;
|
||||
|
||||
protected:
|
||||
void AddStuffing(std::string* bytes, int payload_size) {
|
||||
*bytes += std::string(kTsPacketSize - bytes->size() - payload_size, 0xFF);
|
||||
}
|
||||
|
||||
void ExpectWriteMatches(TsPacket* packet, const std::string& expected) {
|
||||
std::string bytes;
|
||||
ASSERT_OK(packet->Write(&bytes));
|
||||
EXPECT_EQ(expected, bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(TsPacketTest);
|
||||
};
|
||||
|
||||
TEST_F(TsPacketTest, Packet1) {
|
||||
TsPacket ts;
|
||||
ts.set_transport_error_indication(true);
|
||||
ts.set_payload_unit_start_indicator(false);
|
||||
ts.set_transport_priority(true);
|
||||
ts.set_pid(0x123);
|
||||
ts.set_transport_scrambling_control(3);
|
||||
ts.set_adaptation_field_control(TsPacket::kPayloadOnly);
|
||||
ts.set_continuity_counter(0x7);
|
||||
const std::string payload(ts.CalculatePayloadSize(), 0xFF);
|
||||
ts.set_payload(payload);
|
||||
std::string bytes = std::string("\x47\xA1\x23\xD7", 4) + payload;
|
||||
ExpectWriteMatches(&ts, bytes);
|
||||
EXPECT_EQ(kMaxTsPayloadSize, ts.CalculatePayloadSize());
|
||||
}
|
||||
|
||||
TEST_F(TsPacketTest, Packet2) {
|
||||
TsPacket ts;
|
||||
ts.set_transport_error_indication(false);
|
||||
ts.set_payload_unit_start_indicator(true);
|
||||
ts.set_transport_priority(false);
|
||||
ts.set_pid(0x345);
|
||||
ts.set_transport_scrambling_control(1);
|
||||
ts.set_adaptation_field_control(TsPacket::kPayloadOnly);
|
||||
ts.set_continuity_counter(0x4);
|
||||
const std::string payload(ts.CalculatePayloadSize(), 0xFF);
|
||||
ts.set_payload(payload);
|
||||
std::string bytes = std::string("\x47\x43\x45\x54", 4) + payload;
|
||||
ExpectWriteMatches(&ts, bytes);
|
||||
EXPECT_EQ(184, ts.CalculatePayloadSize());
|
||||
}
|
||||
|
||||
TEST_F(TsPacketTest, BlankPacket184BytePayload) {
|
||||
TsPacket ts;
|
||||
ts.set_pid(0x0);
|
||||
ts.set_adaptation_field_control(TsPacket::kPayloadOnly);
|
||||
const std::string payload(ts.CalculatePayloadSize(), 0xFF);
|
||||
ts.set_payload(payload);
|
||||
std::string bytes = std::string("\x47\x00\x00\x10", 4) + payload;
|
||||
ExpectWriteMatches(&ts, bytes);
|
||||
EXPECT_EQ(184, ts.CalculatePayloadSize());
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
@@ -9,12 +9,23 @@
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "media_cas_packager_sdk/internal/ts_packet.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
namespace {
|
||||
|
||||
constexpr size_t kSectionHeaderSize = 4;
|
||||
constexpr size_t kTsFillValue = 0xFF;
|
||||
|
||||
ContinuityCounter Increment(ContinuityCounter continuity_counter) {
|
||||
return ++continuity_counter & 0xf;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void BigEndianToHost16(uint16_t* destination, const void* source) {
|
||||
DCHECK(destination);
|
||||
@@ -24,5 +35,56 @@ void BigEndianToHost16(uint16_t* destination, const void* source) {
|
||||
*destination = ntohs(big_endian_number);
|
||||
}
|
||||
|
||||
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
||||
uint8_t table_id, ContinuityCounter* cc,
|
||||
uint8_t* buffer, ssize_t* bytes_modified) {
|
||||
DCHECK(cc);
|
||||
DCHECK(buffer);
|
||||
DCHECK(bytes_modified);
|
||||
DCHECK_EQ(*bytes_modified % kTsPacketSize, 0);
|
||||
|
||||
// Create a TS payload of 184 bytes (Max TS size - ts header length).
|
||||
CHECK_LE(ecm.size(), kMaxTsPayloadSize);
|
||||
std::string payload(kMaxTsPayloadSize, kTsFillValue);
|
||||
// Reference https://en.wikipedia.org/wiki/Program-specific_information
|
||||
static constexpr uint8_t kPointerField = '\x00';
|
||||
// Here are the 8 bits covers multiple fields from
|
||||
// "section syntax indicator" to the first two bits of "section length".
|
||||
// We always set the first two bits of "section length" to 0 because
|
||||
// the data follows (i.e. the ECM) cannot be more than 180 bytes, so
|
||||
// the remaining 8 bits in "section length" is sufficient to store its
|
||||
// length.
|
||||
static constexpr uint8_t kSyntaxtIndicatorToLength = '\x70';
|
||||
uint8_t ecm_length = ecm.size();
|
||||
// Section header.
|
||||
memcpy(&payload.at(0), &kPointerField, 1);
|
||||
memcpy(&payload.at(1), &table_id, 1);
|
||||
memcpy(&payload.at(2), &kSyntaxtIndicatorToLength, 1);
|
||||
memcpy(&payload.at(3), &ecm_length, 1);
|
||||
// ECM follows the header.
|
||||
memcpy(&payload.at(kSectionHeaderSize), ecm.data(), ecm.size());
|
||||
|
||||
// Wrap the data with a TS header.
|
||||
TsPacket ecm_packet;
|
||||
ecm_packet.set_payload_unit_start_indicator(true);
|
||||
ecm_packet.set_pid(pid);
|
||||
ecm_packet.set_payload(payload);
|
||||
ecm_packet.set_adaptation_field_control(0x1);
|
||||
ecm_packet.set_continuity_counter(*cc);
|
||||
*cc = Increment(*cc);
|
||||
|
||||
// And write the packet.
|
||||
std::string ecm_ts_packet;
|
||||
util::Status status = ecm_packet.Write(&ecm_ts_packet);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
memcpy(buffer + *bytes_modified, ecm_ts_packet.data(), ecm_ts_packet.size());
|
||||
*bytes_modified += ecm_ts_packet.size();
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_UTIL_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_UTIL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/status.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -19,6 +24,28 @@ namespace cas {
|
||||
// the result in |destination|.
|
||||
void BigEndianToHost16(uint16_t* destination, const void* source);
|
||||
|
||||
// Packages an ECM as a TS packet and inserts it into a buffer.
|
||||
// Args:
|
||||
// - |ecm| is the serialized ECM.
|
||||
// - |pid| is the PID used for ECMs in the TS header.
|
||||
// - |table_id| is the table ID byte put in the section header, it should be
|
||||
// either 0x80 or 0x81. Changing table ID from 0x80 or 0x81 or
|
||||
// 0x81 to 0x80 is used to singal to the client that the key contained
|
||||
// in the ECM has changed. In other words, if you are building an ECM
|
||||
// with a new key that was not in any previous ECM, you should flip the
|
||||
// table ID so the client knows this is an important ECM it should process.
|
||||
// - |cc| is the continuity counter to use in the header, which will also be
|
||||
// incremented by this function.
|
||||
// - |buffer| is the output buffer. Must be big enough to hold an additional
|
||||
// 188 bytes.
|
||||
// - |bytes_modified| the number of bytes which have already been modified in
|
||||
// the |buffer| and is used as an offset.
|
||||
// |bytes_modified| will be incremented by 188 if insertion of ECM into
|
||||
// |buffer| is successful.
|
||||
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
||||
uint8_t table_id, ContinuityCounter* cc,
|
||||
uint8_t* buffer, ssize_t* bytes_modified);
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
|
||||
77
media_cas_packager_sdk/internal/util_test.cc
Normal file
77
media_cas_packager_sdk/internal/util_test.cc
Normal file
@@ -0,0 +1,77 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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/util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// ECM payload data taken from a CETS encrypted file at Google Fiber
|
||||
// default_key_id = 0000000000000002, random_iv = 0x30EB, 0xDAF4, 0xC66D, 0x57DC
|
||||
constexpr char kEcmPayload[] = {'\x5F', '\x08', '\x30', '\x30', '\x30', '\x30',
|
||||
'\x30', '\x30', '\x30', '\x30', '\x30', '\x30',
|
||||
'\x30', '\x30', '\x30', '\x30', '\x30', '\x32',
|
||||
'\x41', '\x70', '\x30', '\xEB', '\xDA', '\xF4',
|
||||
'\xC6', '\x6D', '\x57', '\xDC'};
|
||||
// ECM packet for Video PID (TSheader + payload) taken from a CETS encrypted
|
||||
// file, payload_unit_start = 1, adaptation_field_control = 1, cc = 0
|
||||
constexpr char kExpectedEcmPacket[] = {
|
||||
// TS header.
|
||||
'\x47', '\x5F', '\xFD', '\x10',
|
||||
// Section header.
|
||||
'\x00', '\x80', '\x70', '\x1C',
|
||||
// ECM.
|
||||
'\x5F', '\x08', '\x30', '\x30', '\x30', '\x30', '\x30', '\x30', '\x30',
|
||||
'\x30', '\x30', '\x30', '\x30', '\x30', '\x30', '\x30', '\x30', '\x32',
|
||||
'\x41', '\x70', '\x30', '\xEB', '\xDA', '\xF4', '\xC6', '\x6D', '\x57',
|
||||
'\xDC',
|
||||
// Padding.
|
||||
'\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', '\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', '\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', '\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', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
|
||||
'\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
|
||||
'\xFF', '\xFF', '\xFF'};
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
TEST(InsertEcmAsTsPacketTest, BasicHappyPath) {
|
||||
// declare variables used by InsertEcmAsTsPacket()
|
||||
ContinuityCounter ecm_cc_ = 0;
|
||||
ProgramId video_ecm_pid_ = 0x1FFD;
|
||||
uint8_t buffer[188]; // output ts packet size
|
||||
ssize_t output_bytes_modified = 0;
|
||||
// convert kEcmPayload[] into a std::string to be accepted by the method
|
||||
std::string ecm_data(kEcmPayload);
|
||||
EXPECT_OK(InsertEcmAsTsPacket(ecm_data, video_ecm_pid_, kTsPacketTableId80,
|
||||
&ecm_cc_, buffer, &output_bytes_modified));
|
||||
EXPECT_EQ(0, memcmp(kExpectedEcmPacket, buffer, sizeof(buffer)));
|
||||
EXPECT_EQ(1, ecm_cc_);
|
||||
EXPECT_EQ(188, output_bytes_modified);
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
Reference in New Issue
Block a user