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