Home | History | Annotate | Download | only in webm
      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