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