Home | History | Annotate | Download | only in webm
      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 
     57 Cluster::Cluster(scoped_ptr<uint8[]> data, int size)
     58     : data_(data.Pass()), size_(size) {}
     59 Cluster::~Cluster() {}
     60 
     61 ClusterBuilder::ClusterBuilder() { Reset(); }
     62 ClusterBuilder::~ClusterBuilder() {}
     63 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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