DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
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 enum {
44  kClusterSizeOffset = 4,
45  kClusterTimecodeOffset = 14,
46 
47  kSimpleBlockSizeOffset = 1,
48 
49  kBlockGroupSizeOffset = 1,
50  kBlockGroupWithoutBlockDurationBlockSizeOffset = 10,
51  kBlockGroupDurationOffset = 11,
52  kBlockGroupBlockSizeOffset = 20,
53 
54  kInitialBufferSize = 32768,
55 };
56 
57 Cluster::Cluster(scoped_ptr<uint8_t[]> data, int size)
58  : data_(data.Pass()), size_(size) {}
59 Cluster::~Cluster() {}
60 
61 ClusterBuilder::ClusterBuilder() { Reset(); }
62 ClusterBuilder::~ClusterBuilder() {}
63 
64 void ClusterBuilder::SetClusterTimecode(int64_t cluster_timecode) {
65  DCHECK_EQ(cluster_timecode_, -1);
66 
67  cluster_timecode_ = cluster_timecode;
68 
69  // Write the timecode into the header.
70  uint8_t* buf = buffer_.get() + kClusterTimecodeOffset;
71  for (int i = 7; i >= 0; --i) {
72  buf[i] = cluster_timecode & 0xff;
73  cluster_timecode >>= 8;
74  }
75 }
76 
77 void ClusterBuilder::AddSimpleBlock(int track_num,
78  int64_t timecode,
79  int flags,
80  const uint8_t* data,
81  int size) {
82  int block_size = size + 4;
83  int bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
84  if (bytes_needed > (buffer_size_ - bytes_used_))
85  ExtendBuffer(bytes_needed);
86 
87  uint8_t* buf = buffer_.get() + bytes_used_;
88  int block_offset = bytes_used_;
89  memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader));
90  UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
91  buf += sizeof(kSimpleBlockHeader);
92 
93  WriteBlock(buf, track_num, timecode, flags, data, size);
94 
95  bytes_used_ += bytes_needed;
96 }
97 
98 void ClusterBuilder::AddBlockGroup(int track_num,
99  int64_t timecode,
100  int duration,
101  int flags,
102  const uint8_t* data,
103  int size) {
104  AddBlockGroupInternal(track_num, timecode, true, duration, flags, data, size);
105 }
106 
107 void ClusterBuilder::AddBlockGroupWithoutBlockDuration(int track_num,
108  int64_t timecode,
109  int flags,
110  const uint8_t* data,
111  int size) {
112  AddBlockGroupInternal(track_num, timecode, false, 0, flags, data, size);
113 }
114 
115 void ClusterBuilder::AddBlockGroupInternal(int track_num,
116  int64_t timecode,
117  bool include_block_duration,
118  int duration,
119  int flags,
120  const uint8_t* data,
121  int size) {
122  int block_size = size + 4;
123  int bytes_needed = block_size;
124  if (include_block_duration) {
125  bytes_needed += sizeof(kBlockGroupHeader);
126  } else {
127  bytes_needed += sizeof(kBlockGroupHeaderWithoutBlockDuration);
128  }
129 
130  int block_group_size = bytes_needed - 9;
131 
132  if (bytes_needed > (buffer_size_ - bytes_used_))
133  ExtendBuffer(bytes_needed);
134 
135  uint8_t* buf = buffer_.get() + bytes_used_;
136  int block_group_offset = bytes_used_;
137  if (include_block_duration) {
138  memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
139  UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration);
140  UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
141  buf += sizeof(kBlockGroupHeader);
142  } else {
143  memcpy(buf, kBlockGroupHeaderWithoutBlockDuration,
144  sizeof(kBlockGroupHeaderWithoutBlockDuration));
145  UpdateUInt64(
146  block_group_offset + kBlockGroupWithoutBlockDurationBlockSizeOffset,
147  block_size);
148  buf += sizeof(kBlockGroupHeaderWithoutBlockDuration);
149  }
150 
151  UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size);
152 
153  // Make sure the 4 most-significant bits are 0.
154  // http://www.matroska.org/technical/specs/index.html#block_structure
155  flags &= 0x0f;
156 
157  WriteBlock(buf, track_num, timecode, flags, data, size);
158 
159  bytes_used_ += bytes_needed;
160 }
161 
162 void ClusterBuilder::WriteBlock(uint8_t* buf,
163  int track_num,
164  int64_t timecode,
165  int flags,
166  const uint8_t* data,
167  int size) {
168  DCHECK_GE(track_num, 0);
169  DCHECK_LE(track_num, 126);
170  DCHECK_GE(flags, 0);
171  DCHECK_LE(flags, 0xff);
172  DCHECK(data);
173  DCHECK_GT(size, 0);
174  DCHECK_NE(cluster_timecode_, -1);
175 
176  int64_t timecode_delta = timecode - cluster_timecode_;
177  DCHECK_GE(timecode_delta, -32768);
178  DCHECK_LE(timecode_delta, 32767);
179 
180  buf[0] = 0x80 | (track_num & 0x7F);
181  buf[1] = (timecode_delta >> 8) & 0xff;
182  buf[2] = timecode_delta & 0xff;
183  buf[3] = flags & 0xff;
184  memcpy(buf + 4, data, size);
185 }
186 
187 scoped_ptr<Cluster> ClusterBuilder::Finish() {
188  DCHECK_NE(cluster_timecode_, -1);
189 
190  UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
191 
192  scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
193  Reset();
194  return ret.Pass();
195 }
196 
197 scoped_ptr<Cluster> ClusterBuilder::FinishWithUnknownSize() {
198  DCHECK_NE(cluster_timecode_, -1);
199 
200  UpdateUInt64(kClusterSizeOffset, kWebMUnknownSize);
201 
202  scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
203  Reset();
204  return ret.Pass();
205 }
206 
207 void ClusterBuilder::Reset() {
208  buffer_size_ = kInitialBufferSize;
209  buffer_.reset(new uint8_t[buffer_size_]);
210  memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
211  bytes_used_ = sizeof(kClusterHeader);
212  cluster_timecode_ = -1;
213 }
214 
215 void ClusterBuilder::ExtendBuffer(int bytes_needed) {
216  int new_buffer_size = 2 * buffer_size_;
217 
218  while ((new_buffer_size - bytes_used_) < bytes_needed)
219  new_buffer_size *= 2;
220 
221  scoped_ptr<uint8_t[]> new_buffer(new uint8_t[new_buffer_size]);
222 
223  memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
224  buffer_.reset(new_buffer.release());
225  buffer_size_ = new_buffer_size;
226 }
227 
228 void ClusterBuilder::UpdateUInt64(int offset, int64_t value) {
229  DCHECK_LE(offset + 7, buffer_size_);
230  uint8_t* buf = buffer_.get() + offset;
231 
232  // Fill the last 7 bytes of size field in big-endian order.
233  for (int i = 7; i > 0; i--) {
234  buf[i] = value & 0xff;
235  value >>= 8;
236  }
237 }
238 
239 } // namespace media
240 } // namespace shaka