• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 namespace media {
12 
13 static 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 
21 static const uint8 kSimpleBlockHeader[] = {
22   0xA3,  // SimpleBlock ID
23   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // SimpleBlock(size = 0)
24 };
25 
26 static 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 
36 static 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 
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 
Cluster(scoped_ptr<uint8[]> data,int size)57 Cluster::Cluster(scoped_ptr<uint8[]> data, int size)
58     : data_(data.Pass()), size_(size) {}
~Cluster()59 Cluster::~Cluster() {}
60 
ClusterBuilder()61 ClusterBuilder::ClusterBuilder() { Reset(); }
~ClusterBuilder()62 ClusterBuilder::~ClusterBuilder() {}
63 
SetClusterTimecode(int64 cluster_timecode)64 void 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 
AddSimpleBlock(int track_num,int64 timecode,int flags,const uint8 * data,int size)77 void 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 
AddBlockGroup(int track_num,int64 timecode,int duration,int flags,const uint8 * data,int size)95 void 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 
AddBlockGroupWithoutBlockDuration(int track_num,int64 timecode,int flags,const uint8 * data,int size)100 void 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 
AddBlockGroupInternal(int track_num,int64 timecode,bool include_block_duration,int duration,int flags,const uint8 * data,int size)109 void 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 
WriteBlock(uint8 * buf,int track_num,int64 timecode,int flags,const uint8 * data,int size)153 void 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 
Finish()174 scoped_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 
FinishWithUnknownSize()184 scoped_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 
Reset()194 void 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 
ExtendBuffer(int bytes_needed)202 void 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 
UpdateUInt64(int offset,int64 value)215 void 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