Shaka Packager SDK
Loading...
Searching...
No Matches
cluster_builder.cc
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <packager/media/formats/webm/cluster_builder.h>
6
7#include <cstdint>
8#include <cstring>
9#include <memory>
10#include <utility>
11
12#include <absl/log/check.h>
13
14#include <packager/media/formats/webm/webm_constants.h>
15
16namespace shaka {
17namespace media {
18
19static const uint8_t kClusterHeader[] = {
20 // clang-format off
21 0x1F, 0x43, 0xB6, 0x75, // CLUSTER ID
22 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cluster(size = 0)
23 0xE7, // Timecode ID
24 0x88, // timecode(size=8)
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value
26 // clang-format on
27};
28
29static const uint8_t kSimpleBlockHeader[] = {
30 // clang-format off
31 0xA3, // SimpleBlock ID
32 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
33 // clang-format on
34};
35
36static const uint8_t kBlockGroupHeader[] = {
37 // clang-format off
38 0xA0, // BlockGroup ID
39 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
40 0x9B, // BlockDuration ID
41 0x88, // BlockDuration(size = 8)
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // duration
43 0xA1, // Block ID
44 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
45 // clang-format on
46};
47
48static const uint8_t kBlockGroupHeaderWithoutBlockDuration[] = {
49 // clang-format off
50 0xA0, // BlockGroup ID
51 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
52 0xA1, // Block ID
53 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
54 // clang-format on
55};
56
57static const uint8_t kBlockGroupReferenceBlock[] = {
58 // clang-format off
59 0xFB, // ReferenceBlock ID
60 0x81, 0x00, // ReferenceBlock (size=1, value=0)
61 // clang-format on
62};
63
64enum {
65 kClusterSizeOffset = 4,
66 kClusterTimecodeOffset = 14,
67
68 kSimpleBlockSizeOffset = 1,
69
70 kBlockGroupSizeOffset = 1,
71 kBlockGroupWithoutBlockDurationBlockSizeOffset = 10,
72 kBlockGroupDurationOffset = 11,
73 kBlockGroupBlockSizeOffset = 20,
74
75 kInitialBufferSize = 32768,
76};
77
78Cluster::Cluster(std::unique_ptr<uint8_t[]> data, int size)
79 : data_(std::move(data)), size_(size) {}
80Cluster::~Cluster() {}
81
82ClusterBuilder::ClusterBuilder() {
83 Reset();
84}
85ClusterBuilder::~ClusterBuilder() {}
86
87void ClusterBuilder::SetClusterTimecode(int64_t cluster_timecode) {
88 DCHECK_EQ(cluster_timecode_, -1);
89
90 cluster_timecode_ = cluster_timecode;
91
92 // Write the timecode into the header.
93 uint8_t* buf = buffer_.get() + kClusterTimecodeOffset;
94 for (int i = 7; i >= 0; --i) {
95 buf[i] = cluster_timecode & 0xff;
96 cluster_timecode >>= 8;
97 }
98}
99
100void ClusterBuilder::AddSimpleBlock(int track_num,
101 int64_t timecode,
102 int flags,
103 const uint8_t* data,
104 int size) {
105 int block_size = size + 4;
106 int bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
107 if (bytes_needed > (buffer_size_ - bytes_used_))
108 ExtendBuffer(bytes_needed);
109
110 uint8_t* buf = buffer_.get() + bytes_used_;
111 int block_offset = bytes_used_;
112 memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader));
113 UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
114 buf += sizeof(kSimpleBlockHeader);
115
116 WriteBlock(buf, track_num, timecode, flags, data, size);
117
118 bytes_used_ += bytes_needed;
119}
120
121void ClusterBuilder::AddBlockGroup(int track_num,
122 int64_t timecode,
123 int duration,
124 int flags,
125 bool is_key_frame,
126 const uint8_t* data,
127 int size) {
128 AddBlockGroupInternal(track_num, timecode, true, duration, flags,
129 is_key_frame, data, size);
130}
131
132void ClusterBuilder::AddBlockGroupWithoutBlockDuration(int track_num,
133 int64_t timecode,
134 int flags,
135 bool is_key_frame,
136 const uint8_t* data,
137 int size) {
138 AddBlockGroupInternal(track_num, timecode, false, 0, flags, is_key_frame,
139 data, size);
140}
141
142void ClusterBuilder::AddBlockGroupInternal(int track_num,
143 int64_t timecode,
144 bool include_block_duration,
145 int duration,
146 int flags,
147 bool is_key_frame,
148 const uint8_t* data,
149 int size) {
150 int block_size = size + 4;
151 int bytes_needed = block_size;
152 if (include_block_duration) {
153 bytes_needed += sizeof(kBlockGroupHeader);
154 } else {
155 bytes_needed += sizeof(kBlockGroupHeaderWithoutBlockDuration);
156 }
157 if (!is_key_frame) {
158 bytes_needed += sizeof(kBlockGroupReferenceBlock);
159 }
160
161 int block_group_size = bytes_needed - 9;
162
163 if (bytes_needed > (buffer_size_ - bytes_used_))
164 ExtendBuffer(bytes_needed);
165
166 uint8_t* buf = buffer_.get() + bytes_used_;
167 int block_group_offset = bytes_used_;
168 if (include_block_duration) {
169 memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
170 UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration);
171 UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
172 buf += sizeof(kBlockGroupHeader);
173 } else {
174 memcpy(buf, kBlockGroupHeaderWithoutBlockDuration,
175 sizeof(kBlockGroupHeaderWithoutBlockDuration));
176 UpdateUInt64(
177 block_group_offset + kBlockGroupWithoutBlockDurationBlockSizeOffset,
178 block_size);
179 buf += sizeof(kBlockGroupHeaderWithoutBlockDuration);
180 }
181
182 UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size);
183
184 // Make sure the 4 most-significant bits are 0.
185 // http://www.matroska.org/technical/specs/index.html#block_structure
186 flags &= 0x0f;
187
188 WriteBlock(buf, track_num, timecode, flags, data, size);
189 buf += size + 4;
190
191 if (!is_key_frame)
192 memcpy(buf, kBlockGroupReferenceBlock, sizeof(kBlockGroupReferenceBlock));
193 bytes_used_ += bytes_needed;
194}
195
196void ClusterBuilder::WriteBlock(uint8_t* buf,
197 int track_num,
198 int64_t timecode,
199 int flags,
200 const uint8_t* data,
201 int size) {
202 DCHECK_GE(track_num, 0);
203 DCHECK_LE(track_num, 126);
204 DCHECK_GE(flags, 0);
205 DCHECK_LE(flags, 0xff);
206 DCHECK(data);
207 DCHECK_GT(size, 0);
208 DCHECK_NE(cluster_timecode_, -1);
209
210 int64_t timecode_delta = timecode - cluster_timecode_;
211 DCHECK_GE(timecode_delta, -32768);
212 DCHECK_LE(timecode_delta, 32767);
213
214 buf[0] = 0x80 | (track_num & 0x7F);
215 buf[1] = (timecode_delta >> 8) & 0xff;
216 buf[2] = timecode_delta & 0xff;
217 buf[3] = flags & 0xff;
218 memcpy(buf + 4, data, size);
219}
220
221std::unique_ptr<Cluster> ClusterBuilder::Finish() {
222 DCHECK_NE(cluster_timecode_, -1);
223
224 UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
225
226 std::unique_ptr<Cluster> ret(new Cluster(std::move(buffer_), bytes_used_));
227 Reset();
228 return ret;
229}
230
231std::unique_ptr<Cluster> ClusterBuilder::FinishWithUnknownSize() {
232 DCHECK_NE(cluster_timecode_, -1);
233
234 UpdateUInt64(kClusterSizeOffset, kWebMUnknownSize);
235
236 std::unique_ptr<Cluster> ret(new Cluster(std::move(buffer_), bytes_used_));
237 Reset();
238 return ret;
239}
240
241void ClusterBuilder::Reset() {
242 buffer_size_ = kInitialBufferSize;
243 buffer_.reset(new uint8_t[buffer_size_]);
244 memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
245 bytes_used_ = sizeof(kClusterHeader);
246 cluster_timecode_ = -1;
247}
248
249void ClusterBuilder::ExtendBuffer(int bytes_needed) {
250 int new_buffer_size = 2 * buffer_size_;
251
252 while ((new_buffer_size - bytes_used_) < bytes_needed)
253 new_buffer_size *= 2;
254
255 std::unique_ptr<uint8_t[]> new_buffer(new uint8_t[new_buffer_size]);
256
257 memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
258 buffer_.reset(new_buffer.release());
259 buffer_size_ = new_buffer_size;
260}
261
262void ClusterBuilder::UpdateUInt64(int offset, int64_t value) {
263 DCHECK_LE(offset + 7, buffer_size_);
264 uint8_t* buf = buffer_.get() + offset;
265
266 // Fill the last 7 bytes of size field in big-endian order.
267 for (int i = 7; i > 0; i--) {
268 buf[i] = value & 0xff;
269 value >>= 8;
270 }
271}
272
273} // namespace media
274} // namespace shaka
All the methods that are virtual are virtual for mocking.