Shaka Packager SDK
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 
12 namespace shaka {
13 namespace media {
14 
15 static 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 
23 static const uint8_t kSimpleBlockHeader[] = {
24  0xA3, // SimpleBlock ID
25  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
26 };
27 
28 static 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 
38 static 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 
45 static const uint8_t kBlockGroupReferenceBlock[] = {
46  0xFB, // ReferenceBlock ID
47  0x81, 0x00, // ReferenceBlock (size=1, value=0)
48 };
49 
50 enum {
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 
64 Cluster::Cluster(std::unique_ptr<uint8_t[]> data, int size)
65  : data_(std::move(data)), size_(size) {}
66 Cluster::~Cluster() {}
67 
68 ClusterBuilder::ClusterBuilder() { Reset(); }
69 ClusterBuilder::~ClusterBuilder() {}
70 
71 void 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 
84 void 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 
105 void 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 
116 void 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 
126 void 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 
180 void 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 
205 std::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 
215 std::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 
225 void 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 
233 void 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 
246 void 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.
Definition: crypto_flags.cc:66