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:
Fang Yu
2018-12-07 10:16:38 -08:00
parent fb96918196
commit 121d554c20
63 changed files with 4834 additions and 560 deletions

View File

@@ -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",
],
)

View File

@@ -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(

View 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_

View 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

View 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_

View 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

View File

@@ -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

View File

@@ -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

View 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