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 10 namespace media { 11 12 static 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 20 static const uint8 kSimpleBlockHeader[] = { 21 0xA3, // SimpleBlock ID 22 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0) 23 }; 24 25 static 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 35 enum { 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 48 Cluster::Cluster(scoped_ptr<uint8[]> data, int size) 49 : data_(data.Pass()), size_(size) {} 50 Cluster::~Cluster() {} 51 52 ClusterBuilder::ClusterBuilder() { Reset(); } 53 ClusterBuilder::~ClusterBuilder() {} 54 55 void 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 68 void 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 86 void 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 112 void 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 133 scoped_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 143 void 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 151 void 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 164 void 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