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 "media/formats/webm/cluster_builder.h"
6
7#include "base/logging.h"
8#include "media/base/data_buffer.h"
9#include "media/formats/webm/webm_constants.h"
10
11namespace media {
12
13static const uint8 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
21static const uint8 kSimpleBlockHeader[] = {
22  0xA3,  // SimpleBlock ID
23  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // SimpleBlock(size = 0)
24};
25
26static const uint8 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
36static const uint8 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
43enum {
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
57Cluster::Cluster(scoped_ptr<uint8[]> data, int size)
58    : data_(data.Pass()), size_(size) {}
59Cluster::~Cluster() {}
60
61ClusterBuilder::ClusterBuilder() { Reset(); }
62ClusterBuilder::~ClusterBuilder() {}
63
64void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode) {
65  DCHECK_EQ(cluster_timecode_, -1);
66
67  cluster_timecode_ = cluster_timecode;
68
69  // Write the timecode into the header.
70  uint8* 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
77void ClusterBuilder::AddSimpleBlock(int track_num, int64 timecode, int flags,
78                                    const uint8* data, int size) {
79  int block_size = size + 4;
80  int bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
81  if (bytes_needed > (buffer_size_ - bytes_used_))
82    ExtendBuffer(bytes_needed);
83
84  uint8* buf = buffer_.get() + bytes_used_;
85  int block_offset = bytes_used_;
86  memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader));
87  UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
88  buf += sizeof(kSimpleBlockHeader);
89
90  WriteBlock(buf, track_num, timecode, flags, data, size);
91
92  bytes_used_ += bytes_needed;
93}
94
95void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration,
96                                   int flags, const uint8* data, int size) {
97  AddBlockGroupInternal(track_num, timecode, true, duration, flags, data, size);
98}
99
100void ClusterBuilder::AddBlockGroupWithoutBlockDuration(int track_num,
101                                                       int64 timecode,
102                                                       int flags,
103                                                       const uint8* data,
104                                                       int size) {
105  AddBlockGroupInternal(track_num, timecode, false, 0, flags, data, size);
106}
107
108
109void ClusterBuilder::AddBlockGroupInternal(int track_num, int64 timecode,
110                                           bool include_block_duration,
111                                           int duration, int flags,
112                                           const uint8* data, int size) {
113  int block_size = size + 4;
114  int bytes_needed = block_size;
115  if (include_block_duration) {
116    bytes_needed += sizeof(kBlockGroupHeader);
117  } else {
118    bytes_needed += sizeof(kBlockGroupHeaderWithoutBlockDuration);
119  }
120
121  int block_group_size = bytes_needed - 9;
122
123  if (bytes_needed > (buffer_size_ - bytes_used_))
124    ExtendBuffer(bytes_needed);
125
126  uint8* buf = buffer_.get() + bytes_used_;
127  int block_group_offset = bytes_used_;
128  if (include_block_duration) {
129    memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
130    UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration);
131    UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
132    buf += sizeof(kBlockGroupHeader);
133  } else {
134    memcpy(buf, kBlockGroupHeaderWithoutBlockDuration,
135           sizeof(kBlockGroupHeaderWithoutBlockDuration));
136    UpdateUInt64(
137        block_group_offset + kBlockGroupWithoutBlockDurationBlockSizeOffset,
138        block_size);
139    buf += sizeof(kBlockGroupHeaderWithoutBlockDuration);
140  }
141
142  UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size);
143
144  // Make sure the 4 most-significant bits are 0.
145  // http://www.matroska.org/technical/specs/index.html#block_structure
146  flags &= 0x0f;
147
148  WriteBlock(buf, track_num, timecode, flags, data, size);
149
150  bytes_used_ += bytes_needed;
151}
152
153void ClusterBuilder::WriteBlock(uint8* buf, int track_num, int64 timecode,
154                                int flags, const uint8* data, int size) {
155  DCHECK_GE(track_num, 0);
156  DCHECK_LE(track_num, 126);
157  DCHECK_GE(flags, 0);
158  DCHECK_LE(flags, 0xff);
159  DCHECK(data);
160  DCHECK_GT(size, 0);
161  DCHECK_NE(cluster_timecode_, -1);
162
163  int64 timecode_delta = timecode - cluster_timecode_;
164  DCHECK_GE(timecode_delta, -32768);
165  DCHECK_LE(timecode_delta, 32767);
166
167  buf[0] = 0x80 | (track_num & 0x7F);
168  buf[1] = (timecode_delta >> 8) & 0xff;
169  buf[2] = timecode_delta & 0xff;
170  buf[3] = flags & 0xff;
171  memcpy(buf + 4, data, size);
172}
173
174scoped_ptr<Cluster> ClusterBuilder::Finish() {
175  DCHECK_NE(cluster_timecode_, -1);
176
177  UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
178
179  scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
180  Reset();
181  return ret.Pass();
182}
183
184scoped_ptr<Cluster> ClusterBuilder::FinishWithUnknownSize() {
185  DCHECK_NE(cluster_timecode_, -1);
186
187  UpdateUInt64(kClusterSizeOffset, kWebMUnknownSize);
188
189  scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
190  Reset();
191  return ret.Pass();
192}
193
194void ClusterBuilder::Reset() {
195  buffer_size_ = kInitialBufferSize;
196  buffer_.reset(new uint8[buffer_size_]);
197  memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
198  bytes_used_ = sizeof(kClusterHeader);
199  cluster_timecode_ = -1;
200}
201
202void ClusterBuilder::ExtendBuffer(int bytes_needed) {
203  int new_buffer_size = 2 * buffer_size_;
204
205  while ((new_buffer_size - bytes_used_) < bytes_needed)
206    new_buffer_size *= 2;
207
208  scoped_ptr<uint8[]> new_buffer(new uint8[new_buffer_size]);
209
210  memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
211  buffer_.reset(new_buffer.release());
212  buffer_size_ = new_buffer_size;
213}
214
215void ClusterBuilder::UpdateUInt64(int offset, int64 value) {
216  DCHECK_LE(offset + 7, buffer_size_);
217  uint8* buf = buffer_.get() + offset;
218
219  // Fill the last 7 bytes of size field in big-endian order.
220  for (int i = 7; i > 0; i--) {
221    buf[i] = value & 0xff;
222    value >>= 8;
223  }
224}
225
226}  // namespace media
227