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