Shaka Packager SDK
protection_system_specific_info.cc
1 // Copyright 2016 Google LLC. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include <packager/media/base/protection_system_specific_info.h>
8 
9 #include <map>
10 
11 #include <absl/log/check.h>
12 
13 #include <packager/media/base/buffer_reader.h>
14 #include <packager/media/base/buffer_writer.h>
15 #include <packager/media/base/fourccs.h>
16 #include <packager/media/base/rcheck.h>
17 
18 #define RETURN_NULL_IF_FALSE(x) \
19  do { \
20  if (!(x)) { \
21  LOG(ERROR) << "Failure while processing: " << #x; \
22  return nullptr; \
23  } \
24  } while (0)
25 
26 namespace shaka {
27 namespace media {
28 
29 namespace {
30 const size_t kSystemIdSize = 16u;
31 // 4-byte size, 4-byte fourcc, 4-byte version_and_flags.
32 const size_t kPsshBoxHeaderSize = 12u;
33 const size_t kKeyIdSize = 16u;
34 } // namespace
35 
37  const uint8_t* data,
38  size_t data_size,
39  std::vector<ProtectionSystemSpecificInfo>* pssh_infos) {
40  std::map<std::vector<uint8_t>, size_t> info_map;
41  pssh_infos->clear();
42 
43  BufferReader reader(data, data_size);
44  while (reader.HasBytes(1)) {
45  uint32_t size;
46  RCHECK(reader.Read4(&size));
47  RCHECK(reader.SkipBytes(size - 4));
48  RCHECK(size > kPsshBoxHeaderSize + kSystemIdSize);
49 
50  const std::vector<uint8_t> system_id(
51  data + kPsshBoxHeaderSize, data + kPsshBoxHeaderSize + kSystemIdSize);
52  auto iter = info_map.find(system_id);
53  if (iter != info_map.end()) {
54  ProtectionSystemSpecificInfo& info = (*pssh_infos)[iter->second];
55  info.psshs.insert(info.psshs.end(), data, data + size);
56  } else {
57  pssh_infos->push_back(
58  {system_id, std::vector<uint8_t>(data, data + size)});
59  info_map[system_id] = pssh_infos->size() - 1;
60  }
61 
62  data += size;
63  }
64 
65  return true;
66 }
67 
68 std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
69  const uint8_t* data,
70  size_t data_size) {
71  std::unique_ptr<PsshBoxBuilder> pssh_builder(new PsshBoxBuilder);
72  BufferReader reader(data, data_size);
73 
74  uint32_t size;
75  uint32_t box_type;
76  uint32_t version_and_flags;
77  RETURN_NULL_IF_FALSE(reader.Read4(&size));
78  RETURN_NULL_IF_FALSE(reader.Read4(&box_type));
79  RETURN_NULL_IF_FALSE(box_type == FOURCC_pssh);
80  RETURN_NULL_IF_FALSE(reader.Read4(&version_and_flags));
81 
82  pssh_builder->version_ = (version_and_flags >> 24);
83  RETURN_NULL_IF_FALSE(pssh_builder->version_ < 2);
84 
85  RETURN_NULL_IF_FALSE(
86  reader.ReadToVector(&pssh_builder->system_id_, kSystemIdSize));
87 
88  if (pssh_builder->version_ == 1) {
89  uint32_t key_id_count;
90  RETURN_NULL_IF_FALSE(reader.Read4(&key_id_count));
91 
92  pssh_builder->key_ids_.resize(key_id_count);
93  for (uint32_t i = 0; i < key_id_count; i++) {
94  RETURN_NULL_IF_FALSE(
95  reader.ReadToVector(&pssh_builder->key_ids_[i], kKeyIdSize));
96  }
97  }
98 
99  // TODO: Consider parsing key IDs from Widevine PSSH data.
100  uint32_t pssh_data_size;
101  RETURN_NULL_IF_FALSE(reader.Read4(&pssh_data_size));
102  RETURN_NULL_IF_FALSE(
103  reader.ReadToVector(&pssh_builder->pssh_data_, pssh_data_size));
104 
105  // Ignore extra data if there is any.
106  return pssh_builder;
107 }
108 
109 std::vector<uint8_t> PsshBoxBuilder::CreateBox() const {
110  DCHECK_EQ(kSystemIdSize, system_id_.size());
111 
112  const uint32_t box_type = FOURCC_pssh;
113  const uint32_t version_and_flags = (static_cast<uint32_t>(version_) << 24);
114  const uint32_t pssh_data_size = pssh_data_.size();
115 
116  const uint32_t key_id_count = key_ids_.size();
117  const uint32_t key_ids_size =
118  sizeof(key_id_count) + kKeyIdSize * key_id_count;
119  const uint32_t extra_size = version_ == 1 ? key_ids_size : 0;
120 
121  const uint32_t total_size =
122  sizeof(total_size) + sizeof(box_type) + sizeof(version_and_flags) +
123  kSystemIdSize + extra_size + sizeof(pssh_data_size) + pssh_data_size;
124 
125  BufferWriter writer;
126  writer.AppendInt(total_size);
127  writer.AppendInt(box_type);
128  writer.AppendInt(version_and_flags);
129  writer.AppendVector(system_id_);
130  if (version_ == 1) {
131  writer.AppendInt(key_id_count);
132  for (size_t i = 0; i < key_id_count; i++) {
133  DCHECK_EQ(kKeyIdSize, key_ids_[i].size());
134  writer.AppendVector(key_ids_[i]);
135  }
136  }
137  writer.AppendInt(pssh_data_size);
138  writer.AppendVector(pssh_data_);
139 
140  DCHECK_EQ(total_size, writer.Size());
141  return std::vector<uint8_t>(writer.Buffer(), writer.Buffer() + writer.Size());
142 }
143 
144 } // namespace media
145 } // namespace shaka
bool HasBytes(size_t count)
Definition: buffer_reader.h:30
bool SkipBytes(size_t num_bytes)
const uint8_t * Buffer() const
Definition: buffer_writer.h:62
std::vector< uint8_t > CreateBox() const
Creates a PSSH box for the current data.
static std::unique_ptr< PsshBoxBuilder > ParseFromBox(const uint8_t *data, size_t data_size)
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66
static bool ParseBoxes(const uint8_t *data, size_t data_size, std::vector< ProtectionSystemSpecificInfo > *pssh_boxes)