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/tracks_builder.h"
      6 
      7 #include "base/logging.h"
      8 #include "media/formats/webm/webm_constants.h"
      9 
     10 namespace media {
     11 
     12 // Returns size of an integer, formatted using Matroska serialization.
     13 static int GetUIntMkvSize(uint64 value) {
     14   if (value < 0x07FULL)
     15     return 1;
     16   if (value < 0x03FFFULL)
     17     return 2;
     18   if (value < 0x01FFFFFULL)
     19     return 3;
     20   if (value < 0x0FFFFFFFULL)
     21     return 4;
     22   if (value < 0x07FFFFFFFFULL)
     23     return 5;
     24   if (value < 0x03FFFFFFFFFFULL)
     25     return 6;
     26   if (value < 0x01FFFFFFFFFFFFULL)
     27     return 7;
     28   return 8;
     29 }
     30 
     31 // Returns the minimium size required to serialize an integer value.
     32 static int GetUIntSize(uint64 value) {
     33   if (value < 0x0100ULL)
     34     return 1;
     35   if (value < 0x010000ULL)
     36     return 2;
     37   if (value < 0x01000000ULL)
     38     return 3;
     39   if (value < 0x0100000000ULL)
     40     return 4;
     41   if (value < 0x010000000000ULL)
     42     return 5;
     43   if (value < 0x01000000000000ULL)
     44     return 6;
     45   if (value < 0x0100000000000000ULL)
     46     return 7;
     47   return 8;
     48 }
     49 
     50 static int MasterElementSize(int element_id, int payload_size) {
     51   return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
     52 }
     53 
     54 static int IntElementSize(int element_id, int value) {
     55   return GetUIntSize(element_id) + 1 + GetUIntSize(value);
     56 }
     57 
     58 static int DoubleElementSize(int element_id) {
     59   return GetUIntSize(element_id) + 1 + 8;
     60 }
     61 
     62 static int StringElementSize(int element_id, const std::string& value) {
     63  return GetUIntSize(element_id) +
     64         GetUIntMkvSize(value.length()) +
     65         value.length();
     66 }
     67 
     68 static void SerializeInt(uint8** buf_ptr, int* buf_size_ptr,
     69                          int64 value, int size) {
     70   uint8*& buf = *buf_ptr;
     71   int& buf_size = *buf_size_ptr;
     72 
     73   for (int idx = 1; idx <= size; ++idx) {
     74     *buf++ = static_cast<uint8>(value >> ((size - idx) * 8));
     75     --buf_size;
     76   }
     77 }
     78 
     79 static void SerializeDouble(uint8** buf_ptr, int* buf_size_ptr,
     80                             double value) {
     81   // Use a union to convert |value| to native endian integer bit pattern.
     82   union {
     83     double src;
     84     int64 dst;
     85   } tmp;
     86   tmp.src = value;
     87 
     88   // Write the bytes from native endian |tmp.dst| to big-endian form in |buf|.
     89   SerializeInt(buf_ptr, buf_size_ptr, tmp.dst, 8);
     90 }
     91 
     92 static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
     93   SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
     94 }
     95 
     96 static void WriteUInt(uint8** buf, int* buf_size, uint64 value) {
     97   const int size = GetUIntMkvSize(value);
     98   value |= (1ULL << (size * 7));  // Matroska formatting
     99   SerializeInt(buf, buf_size, value, size);
    100 }
    101 
    102 static void WriteMasterElement(uint8** buf, int* buf_size,
    103                                int element_id, int payload_size) {
    104   WriteElementId(buf, buf_size, element_id);
    105   WriteUInt(buf, buf_size, payload_size);
    106 }
    107 
    108 static void WriteIntElement(uint8** buf, int* buf_size,
    109                             int element_id, int value) {
    110   WriteElementId(buf, buf_size, element_id);
    111 
    112   const int size = GetUIntSize(value);
    113   WriteUInt(buf, buf_size, size);
    114 
    115   SerializeInt(buf, buf_size, value, size);
    116 }
    117 
    118 static void WriteDoubleElement(uint8** buf, int* buf_size,
    119                                int element_id, double value) {
    120   WriteElementId(buf, buf_size, element_id);
    121   WriteUInt(buf, buf_size, 8);
    122   SerializeDouble(buf, buf_size, value);
    123 }
    124 
    125 static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
    126                                int element_id, const std::string& value) {
    127   uint8*& buf = *buf_ptr;
    128   int& buf_size = *buf_size_ptr;
    129 
    130   WriteElementId(&buf, &buf_size, element_id);
    131 
    132   const uint64 size = value.length();
    133   WriteUInt(&buf, &buf_size, size);
    134 
    135   memcpy(buf, value.data(), size);
    136   buf += size;
    137   buf_size -= size;
    138 }
    139 
    140 TracksBuilder::TracksBuilder(bool allow_invalid_values)
    141     : allow_invalid_values_(allow_invalid_values) {}
    142 TracksBuilder::TracksBuilder()
    143     : allow_invalid_values_(false) {}
    144 TracksBuilder::~TracksBuilder() {}
    145 
    146 void TracksBuilder::AddVideoTrack(
    147     int track_num,
    148     int track_uid,
    149     const std::string& codec_id,
    150     const std::string& name,
    151     const std::string& language,
    152     int default_duration,
    153     int video_pixel_width,
    154     int video_pixel_height) {
    155   AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
    156                    language, default_duration, video_pixel_width,
    157                    video_pixel_height, -1, -1);
    158 }
    159 
    160 void TracksBuilder::AddAudioTrack(
    161     int track_num,
    162     int track_uid,
    163     const std::string& codec_id,
    164     const std::string& name,
    165     const std::string& language,
    166     int default_duration,
    167     int audio_channels,
    168     double audio_sampling_frequency) {
    169   AddTrackInternal(track_num, kWebMTrackTypeAudio, track_uid, codec_id, name,
    170                    language, default_duration, -1, -1, audio_channels,
    171                    audio_sampling_frequency);
    172 }
    173 
    174 void TracksBuilder::AddTextTrack(
    175     int track_num,
    176     int track_uid,
    177     const std::string& codec_id,
    178     const std::string& name,
    179     const std::string& language) {
    180   AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
    181                    codec_id, name, language, -1, -1, -1, -1, -1);
    182 }
    183 
    184 std::vector<uint8> TracksBuilder::Finish() {
    185   // Allocate the storage
    186   std::vector<uint8> buffer;
    187   buffer.resize(GetTracksSize());
    188 
    189   // Populate the storage with a tracks header
    190   WriteTracks(&buffer[0], buffer.size());
    191 
    192   return buffer;
    193 }
    194 
    195 void TracksBuilder::AddTrackInternal(
    196     int track_num,
    197     int track_type,
    198     int track_uid,
    199     const std::string& codec_id,
    200     const std::string& name,
    201     const std::string& language,
    202     int default_duration,
    203     int video_pixel_width,
    204     int video_pixel_height,
    205     int audio_channels,
    206     double audio_sampling_frequency) {
    207   tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
    208                           language, default_duration, video_pixel_width,
    209                           video_pixel_height, audio_channels,
    210                           audio_sampling_frequency, allow_invalid_values_));
    211 }
    212 
    213 int TracksBuilder::GetTracksSize() const {
    214   return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
    215 }
    216 
    217 int TracksBuilder::GetTracksPayloadSize() const {
    218   int payload_size = 0;
    219 
    220   for (TrackList::const_iterator itr = tracks_.begin();
    221        itr != tracks_.end(); ++itr) {
    222     payload_size += itr->GetSize();
    223   }
    224 
    225   return payload_size;
    226 }
    227 
    228 void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
    229   WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
    230 
    231   for (TrackList::const_iterator itr = tracks_.begin();
    232        itr != tracks_.end(); ++itr) {
    233     itr->Write(&buf, &buf_size);
    234   }
    235 }
    236 
    237 TracksBuilder::Track::Track(int track_num, int track_type, int track_uid,
    238                             const std::string& codec_id,
    239                             const std::string& name,
    240                             const std::string& language,
    241                             int default_duration,
    242                             int video_pixel_width, int video_pixel_height,
    243                             int audio_channels, double audio_sampling_frequency,
    244                             bool allow_invalid_values)
    245     : track_num_(track_num),
    246       track_type_(track_type),
    247       track_uid_(track_uid),
    248       codec_id_(codec_id),
    249       name_(name),
    250       language_(language),
    251       default_duration_(default_duration),
    252       video_pixel_width_(video_pixel_width),
    253       video_pixel_height_(video_pixel_height),
    254       audio_channels_(audio_channels),
    255       audio_sampling_frequency_(audio_sampling_frequency) {
    256   if (!allow_invalid_values) {
    257     CHECK_GT(track_num_, 0);
    258     CHECK_GT(track_type_, 0);
    259     CHECK_LT(track_type_, 255);
    260     CHECK_GT(track_uid_, 0);
    261     if (track_type != kWebMTrackTypeVideo &&
    262         track_type != kWebMTrackTypeAudio) {
    263       CHECK_EQ(default_duration_, -1);
    264     } else {
    265       CHECK(default_duration_ == -1 || default_duration_ > 0);
    266     }
    267 
    268     if (track_type == kWebMTrackTypeVideo) {
    269       CHECK_GT(video_pixel_width_, 0);
    270       CHECK_GT(video_pixel_height_, 0);
    271     } else {
    272       CHECK_EQ(video_pixel_width_, -1);
    273       CHECK_EQ(video_pixel_height_, -1);
    274     }
    275 
    276     if (track_type == kWebMTrackTypeAudio) {
    277       CHECK_GT(audio_channels_, 0);
    278       CHECK_GT(audio_sampling_frequency_, 0.0);
    279     } else {
    280       CHECK_EQ(audio_channels_, -1);
    281       CHECK_EQ(audio_sampling_frequency_, -1.0);
    282     }
    283   }
    284 }
    285 
    286 int TracksBuilder::Track::GetSize() const {
    287   return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
    288 }
    289 
    290 int TracksBuilder::Track::GetVideoPayloadSize() const {
    291   int payload_size = 0;
    292 
    293   if (video_pixel_width_ >= 0)
    294     payload_size += IntElementSize(kWebMIdPixelWidth, video_pixel_width_);
    295   if (video_pixel_height_ >= 0)
    296     payload_size += IntElementSize(kWebMIdPixelHeight, video_pixel_height_);
    297 
    298   return payload_size;
    299 }
    300 
    301 int TracksBuilder::Track::GetAudioPayloadSize() const {
    302   int payload_size = 0;
    303 
    304   if (audio_channels_ >= 0)
    305     payload_size += IntElementSize(kWebMIdChannels, audio_channels_);
    306   if (audio_sampling_frequency_ >= 0)
    307     payload_size += DoubleElementSize(kWebMIdSamplingFrequency);
    308 
    309   return payload_size;
    310 }
    311 
    312 int TracksBuilder::Track::GetPayloadSize() const {
    313   int size = 0;
    314 
    315   size += IntElementSize(kWebMIdTrackNumber, track_num_);
    316   size += IntElementSize(kWebMIdTrackType, track_type_);
    317   size += IntElementSize(kWebMIdTrackUID, track_uid_);
    318 
    319   if (default_duration_ >= 0)
    320     size += IntElementSize(kWebMIdDefaultDuration, default_duration_);
    321 
    322   if (!codec_id_.empty())
    323     size += StringElementSize(kWebMIdCodecID, codec_id_);
    324 
    325   if (!name_.empty())
    326     size += StringElementSize(kWebMIdName, name_);
    327 
    328   if (!language_.empty())
    329     size += StringElementSize(kWebMIdLanguage, language_);
    330 
    331   if (GetVideoPayloadSize() > 0) {
    332     size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
    333   }
    334 
    335   if (GetAudioPayloadSize() > 0) {
    336     size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
    337   }
    338 
    339   return size;
    340 }
    341 
    342 void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
    343   WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
    344 
    345   WriteIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
    346   WriteIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
    347   WriteIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);
    348 
    349   if (default_duration_ >= 0)
    350     WriteIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);
    351 
    352   if (!codec_id_.empty())
    353     WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
    354 
    355   if (!name_.empty())
    356     WriteStringElement(buf, buf_size, kWebMIdName, name_);
    357 
    358   if (!language_.empty())
    359     WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
    360 
    361   if (GetVideoPayloadSize() > 0) {
    362     WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());
    363 
    364     if (video_pixel_width_ >= 0)
    365       WriteIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);
    366 
    367     if (video_pixel_height_ >= 0)
    368       WriteIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
    369   }
    370 
    371   if (GetAudioPayloadSize() > 0) {
    372     WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());
    373 
    374     if (audio_channels_ >= 0)
    375       WriteIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);
    376 
    377     if (audio_sampling_frequency_ >= 0) {
    378       WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
    379           audio_sampling_frequency_);
    380     }
    381   }
    382 }
    383 
    384 }  // namespace media
    385