diff options
Diffstat (limited to 'media/formats/webm/cluster_builder.cc')
-rw-r--r-- | media/formats/webm/cluster_builder.cc | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/media/formats/webm/cluster_builder.cc b/media/formats/webm/cluster_builder.cc new file mode 100644 index 0000000..ec95616 --- /dev/null +++ b/media/formats/webm/cluster_builder.cc @@ -0,0 +1,175 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/formats/webm/cluster_builder.h" + +#include "base/logging.h" +#include "media/base/data_buffer.h" + +namespace media { + +static const uint8 kClusterHeader[] = { + 0x1F, 0x43, 0xB6, 0x75, // CLUSTER ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cluster(size = 0) + 0xE7, // Timecode ID + 0x88, // timecode(size=8) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value +}; + +static const uint8 kSimpleBlockHeader[] = { + 0xA3, // SimpleBlock ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0) +}; + +static const uint8 kBlockGroupHeader[] = { + 0xA0, // BlockGroup ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0) + 0x9B, // BlockDuration ID + 0x88, // BlockDuration(size = 8) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // duration + 0xA1, // Block ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0) +}; + +enum { + kClusterSizeOffset = 4, + kClusterTimecodeOffset = 14, + + kSimpleBlockSizeOffset = 1, + + kBlockGroupSizeOffset = 1, + kBlockGroupDurationOffset = 11, + kBlockGroupBlockSizeOffset = 20, + + kInitialBufferSize = 32768, +}; + +Cluster::Cluster(scoped_ptr<uint8[]> data, int size) + : data_(data.Pass()), size_(size) {} +Cluster::~Cluster() {} + +ClusterBuilder::ClusterBuilder() { Reset(); } +ClusterBuilder::~ClusterBuilder() {} + +void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode) { + DCHECK_EQ(cluster_timecode_, -1); + + cluster_timecode_ = cluster_timecode; + + // Write the timecode into the header. + uint8* buf = buffer_.get() + kClusterTimecodeOffset; + for (int i = 7; i >= 0; --i) { + buf[i] = cluster_timecode & 0xff; + cluster_timecode >>= 8; + } +} + +void ClusterBuilder::AddSimpleBlock(int track_num, int64 timecode, int flags, + const uint8* data, int size) { + int block_size = size + 4; + int bytes_needed = sizeof(kSimpleBlockHeader) + block_size; + if (bytes_needed > (buffer_size_ - bytes_used_)) + ExtendBuffer(bytes_needed); + + uint8* buf = buffer_.get() + bytes_used_; + int block_offset = bytes_used_; + memcpy(buf, kSimpleBlockHeader, sizeof(kSimpleBlockHeader)); + UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size); + buf += sizeof(kSimpleBlockHeader); + + WriteBlock(buf, track_num, timecode, flags, data, size); + + bytes_used_ += bytes_needed; +} + +void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration, + int flags, const uint8* data, int size) { + int block_size = size + 4; + int bytes_needed = sizeof(kBlockGroupHeader) + block_size; + int block_group_size = bytes_needed - 9; + + if (bytes_needed > (buffer_size_ - bytes_used_)) + ExtendBuffer(bytes_needed); + + uint8* buf = buffer_.get() + bytes_used_; + int block_group_offset = bytes_used_; + memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader)); + UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size); + UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration); + UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size); + buf += sizeof(kBlockGroupHeader); + + // Make sure the 4 most-significant bits are 0. + // http://www.matroska.org/technical/specs/index.html#block_structure + flags &= 0x0f; + + WriteBlock(buf, track_num, timecode, flags, data, size); + + bytes_used_ += bytes_needed; +} + +void ClusterBuilder::WriteBlock(uint8* buf, int track_num, int64 timecode, + int flags, const uint8* data, int size) { + DCHECK_GE(track_num, 0); + DCHECK_LE(track_num, 126); + DCHECK_GE(flags, 0); + DCHECK_LE(flags, 0xff); + DCHECK(data); + DCHECK_GT(size, 0); + DCHECK_NE(cluster_timecode_, -1); + + int64 timecode_delta = timecode - cluster_timecode_; + DCHECK_GE(timecode_delta, -32768); + DCHECK_LE(timecode_delta, 32767); + + buf[0] = 0x80 | (track_num & 0x7F); + buf[1] = (timecode_delta >> 8) & 0xff; + buf[2] = timecode_delta & 0xff; + buf[3] = flags & 0xff; + memcpy(buf + 4, data, size); +} + +scoped_ptr<Cluster> ClusterBuilder::Finish() { + DCHECK_NE(cluster_timecode_, -1); + + UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8)); + + scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_)); + Reset(); + return ret.Pass(); +} + +void ClusterBuilder::Reset() { + buffer_size_ = kInitialBufferSize; + buffer_.reset(new uint8[buffer_size_]); + memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader)); + bytes_used_ = sizeof(kClusterHeader); + cluster_timecode_ = -1; +} + +void ClusterBuilder::ExtendBuffer(int bytes_needed) { + int new_buffer_size = 2 * buffer_size_; + + while ((new_buffer_size - bytes_used_) < bytes_needed) + new_buffer_size *= 2; + + scoped_ptr<uint8[]> new_buffer(new uint8[new_buffer_size]); + + memcpy(new_buffer.get(), buffer_.get(), bytes_used_); + buffer_.reset(new_buffer.release()); + buffer_size_ = new_buffer_size; +} + +void ClusterBuilder::UpdateUInt64(int offset, int64 value) { + DCHECK_LE(offset + 7, buffer_size_); + uint8* buf = buffer_.get() + offset; + + // Fill the last 7 bytes of size field in big-endian order. + for (int i = 7; i > 0; i--) { + buf[i] = value & 0xff; + value >>= 8; + } +} + +} // namespace media |