1// Copyright (c) 2012 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/webm/cluster_builder.h"
6
7#include "base/logging.h"
8#include "media/base/data_buffer.h"
9
10namespace media {
11
12static const uint8 kClusterHeader[] = {
13  0x1F, 0x43, 0xB6, 0x75,  // CLUSTER ID
14  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // cluster(size = 0)
15  0xE7,  // Timecode ID
16  0x88,  // timecode(size=8)
17  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // timecode value
18};
19
20static const uint8 kSimpleBlockHeader[] = {
21  0xA3,  // SimpleBlock ID
22  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // SimpleBlock(size = 0)
23};
24
25static const uint8 kBlockGroupHeader[] = {
26  0xA0,  // BlockGroup ID
27  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // BlockGroup(size = 0)
28  0x9B,  // BlockDuration ID
29  0x88,  // BlockDuration(size = 8)
30  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // duration
31  0xA1,  // Block ID
32  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // Block(size = 0)
33};
34
35enum {
36  kClusterSizeOffset = 4,
37  kClusterTimecodeOffset = 14,
38
39  kSimpleBlockSizeOffset = 1,
40
41  kBlockGroupSizeOffset = 1,
42  kBlockGroupDurationOffset = 11,
43  kBlockGroupBlockSizeOffset = 20,
44
45  kInitialBufferSize = 32768,
46};
47
48Cluster::Cluster(scoped_ptr<uint8[]> data, int size)
49    : data_(data.Pass()), size_(size) {}
50Cluster::~Cluster() {}
51
52ClusterBuilder::ClusterBuilder() { Reset(); }
53ClusterBuilder::~ClusterBuilder() {}
54
55void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode) {
56  DCHECK_EQ(cluster_timecode_, -1);
57
58  cluster_timecode_ = cluster_timecode;
59
60  // Write the timecode into the header.
61  uint8* buf = buffer_.get() + kClusterTimecodeOffset;
62  for (int i = 7; i >= 0; --i) {
63    buf[i] = cluster_timecode & 0xff;
64    cluster_timecode >>= 8;
65  }
66}
67
68void ClusterBuilder::AddSimpleBlock(int track_num, int64 timecode, int flags,
69                                    const uint8* data, int size) {
70  int block_size = size + 4;
71  int bytes_needed = sizeof(kSimpleBlockHeader) + block_size;
72  if (bytes_needed > (buffer_size_ - bytes_used_))
73    ExtendBuffer(bytes_needed);
74
75  uint8* buf = buffer_.get() + bytes_used_;
76  int block_offset = bytes_used_;
77  memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader));
78  UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
79  buf += sizeof(kSimpleBlockHeader);
80
81  WriteBlock(buf, track_num, timecode, flags, data, size);
82
83  bytes_used_ += bytes_needed;
84}
85
86void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration,
87                                   int flags, const uint8* data, int size) {
88  int block_size = size + 4;
89  int bytes_needed = sizeof(kBlockGroupHeader) + block_size;
90  int block_group_size = bytes_needed - 9;
91
92  if (bytes_needed > (buffer_size_ - bytes_used_))
93    ExtendBuffer(bytes_needed);
94
95  uint8* buf = buffer_.get() + bytes_used_;
96  int block_group_offset = bytes_used_;
97  memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
98  UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size);
99  UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration);
100  UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
101  buf += sizeof(kBlockGroupHeader);
102
103  // Make sure the 4 most-significant bits are 0.
104  // http://www.matroska.org/technical/specs/index.html#block_structure
105  flags &= 0x0f;
106
107  WriteBlock(buf, track_num, timecode, flags, data, size);
108
109  bytes_used_ += bytes_needed;
110}
111
112void ClusterBuilder::WriteBlock(uint8* buf, int track_num, int64 timecode,
113                                int flags, const uint8* data, int size) {
114  DCHECK_GE(track_num, 0);
115  DCHECK_LE(track_num, 126);
116  DCHECK_GE(flags, 0);
117  DCHECK_LE(flags, 0xff);
118  DCHECK(data);
119  DCHECK_GT(size, 0);
120  DCHECK_NE(cluster_timecode_, -1);
121
122  int64 timecode_delta = timecode - cluster_timecode_;
123  DCHECK_GE(timecode_delta, -32768);
124  DCHECK_LE(timecode_delta, 32767);
125
126  buf[0] = 0x80 | (track_num & 0x7F);
127  buf[1] = (timecode_delta >> 8) & 0xff;
128  buf[2] = timecode_delta & 0xff;
129  buf[3] = flags & 0xff;
130  memcpy(buf + 4, data, size);
131}
132
133scoped_ptr<Cluster> ClusterBuilder::Finish() {
134  DCHECK_NE(cluster_timecode_, -1);
135
136  UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
137
138  scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
139  Reset();
140  return ret.Pass();
141}
142
143void ClusterBuilder::Reset() {
144  buffer_size_ = kInitialBufferSize;
145  buffer_.reset(new uint8[buffer_size_]);
146  memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
147  bytes_used_ = sizeof(kClusterHeader);
148  cluster_timecode_ = -1;
149}
150
151void ClusterBuilder::ExtendBuffer(int bytes_needed) {
152  int new_buffer_size = 2 * buffer_size_;
153
154  while ((new_buffer_size - bytes_used_) < bytes_needed)
155    new_buffer_size *= 2;
156
157  scoped_ptr<uint8[]> new_buffer(new uint8[new_buffer_size]);
158
159  memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
160  buffer_.reset(new_buffer.release());
161  buffer_size_ = new_buffer_size;
162}
163
164void ClusterBuilder::UpdateUInt64(int offset, int64 value) {
165  DCHECK_LE(offset + 7, buffer_size_);
166  uint8* buf = buffer_.get() + offset;
167
168  // Fill the last 7 bytes of size field in big-endian order.
169  for (int i = 7; i > 0; i--) {
170    buf[i] = value & 0xff;
171    value >>= 8;
172  }
173}
174
175}  // namespace media
176