From cb773116a6b020becad2dbf563082b7931b95a55 Mon Sep 17 00:00:00 2001 From: Fang Yu Date: Mon, 29 Oct 2018 11:29:14 -0700 Subject: [PATCH] Move WV CAS Descriptor Generator to the media_cas_packager_sdk. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=219162536 --- media_cas_packager_sdk/public/BUILD | 30 +++ .../public/wv_cas_ca_descriptor.cc | 122 ++++++++++ .../public/wv_cas_ca_descriptor.h | 75 ++++++ .../public/wv_cas_ca_descriptor_test.cc | 216 ++++++++++++++++++ 4 files changed, 443 insertions(+) create mode 100644 media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc create mode 100644 media_cas_packager_sdk/public/wv_cas_ca_descriptor.h create mode 100644 media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc diff --git a/media_cas_packager_sdk/public/BUILD b/media_cas_packager_sdk/public/BUILD index a1e3e29..ae0de77 100644 --- a/media_cas_packager_sdk/public/BUILD +++ b/media_cas_packager_sdk/public/BUILD @@ -29,6 +29,7 @@ cc_binary( name = "libmedia_cas_packager_sdk.so", linkshared = 1, deps = [ + ":wv_cas_ca_descriptor", ":wv_cas_ecm", ":wv_cas_status", ], @@ -40,6 +41,7 @@ cc_library( hdrs = glob(["*.h"]), deps = [ "//base", + "@abseil_repo//absl/base:core_headers", "//util:status", "//media_cas_packager_sdk/internal:ecm", "//media_cas_packager_sdk/internal:ecm_generator", @@ -57,6 +59,34 @@ cc_binary( ], ) +cc_library( + name = "wv_cas_ca_descriptor", + srcs = ["wv_cas_ca_descriptor.cc"], + hdrs = ["wv_cas_ca_descriptor.h"], + copts = PUBLIC_COPTS, + deps = [ + ":wv_cas_status", + "//base", + "@abseil_repo//absl/base:core_headers", + "@abseil_repo//absl/strings", + "//util:status", + "//common:string_util", + "//protos/public:media_cas_proto", + ], +) + +cc_test( + name = "wv_cas_ca_descriptor_test", + size = "small", + srcs = ["wv_cas_ca_descriptor_test.cc"], + deps = [ + ":wv_cas_ca_descriptor", + ":wv_cas_status", + "//testing:gunit_main", + "//protos/public:media_cas_proto", + ], +) + cc_library( name = "wv_cas_ecm", srcs = ["wv_cas_ecm.cc"], diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc new file mode 100644 index 0000000..982bd18 --- /dev/null +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.cc @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/public/wv_cas_ca_descriptor.h" + +#include + +#include "glog/logging.h" +#include "absl/strings/str_cat.h" +#include "util/status.h" +#include "common/string_util.h" +#include "media_cas_packager_sdk/public/wv_cas_status.h" +#include "protos/public/media_cas.pb.h" + +namespace widevine { +namespace cas { + +namespace { + +// Size of fixed portion of CA descriptor (before any private bytes). +static constexpr uint32_t kCaDescriptorBaseSize = 6; + +// Size of fixed portion of CA descriptor that follows the length field. +// This and the size of any private bytes must be placed in the length field. +static constexpr uint32_t kCaDescriptorBasePostLengthSize = 4; + +// Bitfield lengths for the CA descriptor fields +static constexpr int kNumBitsCaDescriptorTagField = 8; +static constexpr int kNumBitsCaDescriptorLengthField = 8; +static constexpr int kNumBitsCaSystemIdField = 16; +static constexpr int kNumBitsCaDescriptorReservedField = 3; +static constexpr int kNumBitsCaDescriptorPidField = 13; + +// Bitfield constants for the CA descriptor fields. + +// CA descriptor tag value, from table 2-45. +static constexpr uint32_t kCaDescriptorTag = 9; + +// CA System ID for Widevine. From table in +// https://en.wikipedia.org/wiki/Conditional_access +static constexpr uint32_t kWidevineCaSystemId = 0x4AD4; + +// Value for CA descriptor reserved field. +static constexpr uint32_t kUnusedZero = 0; + +// The range of valid PIDs, from section 2.4.3.3, and table 2-3. +static constexpr uint32_t kMinValidPID = 0x0010; +static constexpr uint32_t kMaxValidPID = 0x1FFE; + +} // namespace + +WvCasStatus WvCasCaDescriptor::GenerateCaDescriptor( + uint16_t ca_pid, const std::string& provider, const std::string& content_id, + std::string* serialized_ca_desc) const { + if (serialized_ca_desc == nullptr) { + LOG(ERROR) << "Return CA descriptor std::string pointer is nullptr."; + return INVALID_ARGUMENT; + } + if (ca_pid < kMinValidPID || ca_pid > kMaxValidPID) { + LOG(ERROR) << "PID value is out of the valid range."; + return INVALID_ARGUMENT; + } + + std::string private_data = ""; + if (!provider.empty() && !content_id.empty()) { + private_data = GeneratePrivateData(provider, content_id); + } + + const size_t descriptor_length = + kCaDescriptorBasePostLengthSize + private_data.size(); + if (0xFF < descriptor_length) { + LOG(ERROR) << "Private data std::string is too large."; + return INVALID_ARGUMENT; + } + + std::bitset tag(kCaDescriptorTag); + // Length field: insert count of bytes that follow this field, + // including private bytes. + std::bitset length(descriptor_length); + std::bitset ca_system_id(kWidevineCaSystemId); + std::bitset reserved(kUnusedZero); + std::bitset pid(ca_pid); + + // Converts bitset to a std::string where each char represents a bit. + std::string descriptor_bitset = absl::StrCat( + tag.to_string(), length.to_string(), ca_system_id.to_string(), + reserved.to_string(), pid.to_string()); + if (descriptor_bitset.size() != kCaDescriptorBaseSize * 8) { + LOG(ERROR) << "Error creating CA descriptor."; + return INTERNAL; + } + std::string descriptor; + util::Status status = + string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor); + + *serialized_ca_desc = descriptor; + if (!private_data.empty()) { + *serialized_ca_desc += private_data; + } + + return OK; +} + +size_t WvCasCaDescriptor::CaDescriptorBaseSize() const { + return kCaDescriptorBaseSize; +} + +std::string WvCasCaDescriptor::GeneratePrivateData(const std::string& provider, + const std::string& content_id) const { + CaDescriptorPrivateData private_data; + private_data.set_provider(provider); + private_data.set_content_id(content_id); + return private_data.SerializeAsString(); +} + +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h new file mode 100644 index 0000000..baea1bf --- /dev/null +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor.h @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_ +#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_ + +#include +#include + +#include "media_cas_packager_sdk/public/wv_cas_status.h" + +namespace widevine { +namespace cas { + +// Simple CA Descriptor generator for Widevine CAS. +// +// The CA descriptor format is defined in ISO/IEC 13818-1:2015 (table 2-59): +// CA_descriptor() { +// descriptor_tag 8 uimsbf +// descriptor_length 8 uimsbf +// CA_system_id 16 uimsbf +// reserved 3 bslbf +// CA_PID 13 uimsbf +// for (i = 0; i < N; i++) { +// private_data_byte uimsbf +// } +// } +// +// Class is not thread safe. +class WvCasCaDescriptor { + public: + WvCasCaDescriptor() = default; + WvCasCaDescriptor(const WvCasCaDescriptor&) = delete; + WvCasCaDescriptor& operator=(const WvCasCaDescriptor&) = delete; + virtual ~WvCasCaDescriptor() = default; + + // Generate a CA descriptor for the ECM stream for an encrypted program + // stream. + // + // Args: + // |ca_pid| the 13-bit PID of the ECMs + // |provider| provider name, put in private data for client to construct pssh + // |content_id| content ID, put in private data for client to construct pssh + // |serialized_ca_desc| a std::string object to receive the encoded descriptor. + // + // Notes: + // The descriptor generated by this call may be inserted into a CA Table + // section (for an EMM stream) or into a TS Program Map Table section (for an + // ECM stream). The descriptor will be 6 bytes plus any bytes added as + // (user-defined) private data. + virtual WvCasStatus GenerateCaDescriptor(uint16_t ca_pid, + const std::string& provider, + const std::string& content_id, + std::string* serialized_ca_desc) const; + + // Return the base size (before private data is added) of the CA + // descriptor. The user can call this to plan the layout of the Table section + // where the descriptor will be added. + virtual size_t CaDescriptorBaseSize() const; + + protected: + // Protected visibility to support unit testing. + virtual std::string GeneratePrivateData(const std::string& provider, + const std::string& content_id) const; +}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_CA_DESCRIPTOR_H_ diff --git a/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc b/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc new file mode 100644 index 0000000..594bb36 --- /dev/null +++ b/media_cas_packager_sdk/public/wv_cas_ca_descriptor_test.cc @@ -0,0 +1,216 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/public/wv_cas_ca_descriptor.h" + +#include + +#include "testing/gunit.h" +#include "media_cas_packager_sdk/public/wv_cas_status.h" +#include "protos/public/media_cas.pb.h" + +using ::testing::Test; + +namespace widevine { +namespace cas { + +namespace { + +// Random value for PID +static constexpr int kTestPid = 50; +static constexpr char kProvider[] = "widevine_test"; +static constexpr char kContentId[] = "1234"; + +} // namespace + +class WvCasCaDescriptorTest : public Test { + protected: + WvCasCaDescriptorTest() {} + WvCasCaDescriptor ca_descriptor_; + std::string actual_ca_descriptor_; +}; + +TEST_F(WvCasCaDescriptorTest, BaseSize) { + EXPECT_EQ(6, ca_descriptor_.CaDescriptorBaseSize()); +} + +TEST_F(WvCasCaDescriptorTest, BasicGoodGen) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x00\x32", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, NoReturnStringFail) { + EXPECT_EQ(INVALID_ARGUMENT, + ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "", nullptr)); +} + +TEST_F(WvCasCaDescriptorTest, PidTooLowFail) { + const uint32_t bad_pid = 0x10 - 1; + EXPECT_EQ(INVALID_ARGUMENT, ca_descriptor_.GenerateCaDescriptor( + bad_pid, "", "", &actual_ca_descriptor_)); +} + +TEST_F(WvCasCaDescriptorTest, PidMinOK) { + const uint32_t min_pid = 0x10; + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(min_pid, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x00\x10", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidMaxOK) { + const uint32_t max_pid = 0x1FFE; + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(max_pid, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x1f\xfe"); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidTooHighFail) { + const uint32_t bad_pid = 0x1FFF; + EXPECT_EQ(INVALID_ARGUMENT, ca_descriptor_.GenerateCaDescriptor( + bad_pid, "", "", &actual_ca_descriptor_)); +} + +TEST_F(WvCasCaDescriptorTest, PidOneByte) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(255, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x00\xff", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidSecondByte) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x1F00, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x1f\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidTwelveBits) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0xFFF, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x0f\xff"); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x1000, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x10\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidTwelthBit) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x800, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x08\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidElevenththBit) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x400, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x04\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidTenthBit) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x200, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x02\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PidNinthBit) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(0x100, "", "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x01\x00", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateDataOnlyProviderIgnored) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, "", + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x00\x32", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateDataOnlyContentIdIgnored) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor(kTestPid, "", kContentId, + &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\x00\x32", 6); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateData) { + EXPECT_EQ(OK, ca_descriptor_.GenerateCaDescriptor( + kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x19\x4a\xd4\x00\x32", 6); + CaDescriptorPrivateData private_data; + private_data.set_provider(kProvider); + private_data.set_content_id(kContentId); + EXPECT_EQ(expected_ca_descriptor + private_data.SerializeAsString(), + actual_ca_descriptor_); +} + +class FakePrivateDataCaDescriptor : public WvCasCaDescriptor { + public: + void set_private_data(std::string private_data) { private_data_ = private_data; } + + protected: + std::string GeneratePrivateData(const std::string& provider, + const std::string& content_id) const override { + return private_data_; + } + + private: + std::string private_data_; +}; + +TEST_F(WvCasCaDescriptorTest, PrivateDataOneByte) { + FakePrivateDataCaDescriptor fake_descriptor; + fake_descriptor.set_private_data("X"); + EXPECT_EQ(OK, fake_descriptor.GenerateCaDescriptor( + kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x05\x4a\xd4\x00\x32X", 7); + EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateDataMultipleBytes) { + const std::string private_data_bytes("X1234abcde"); + FakePrivateDataCaDescriptor fake_descriptor; + fake_descriptor.set_private_data(private_data_bytes); + EXPECT_EQ(OK, fake_descriptor.GenerateCaDescriptor( + kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\x0e\x4a\xd4\x00\x32", 6); + EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateDataMaxNumberBytes) { + const std::string private_data_bytes(251, 'x'); + FakePrivateDataCaDescriptor fake_descriptor; + fake_descriptor.set_private_data(private_data_bytes); + EXPECT_EQ(OK, fake_descriptor.GenerateCaDescriptor( + kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); + const std::string expected_ca_descriptor("\x09\xff\x4a\xd4\x00\x32", 6); + EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_); +} + +TEST_F(WvCasCaDescriptorTest, PrivateDataTooManyBytesFail) { + const std::string private_data_bytes(252, 'X'); + FakePrivateDataCaDescriptor fake_descriptor; + fake_descriptor.set_private_data(private_data_bytes); + EXPECT_EQ(INVALID_ARGUMENT, + fake_descriptor.GenerateCaDescriptor( + kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); +} + +} // namespace cas +} // namespace widevine