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