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/webm_cluster_parser.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/logging.h"
     10 #include "base/sys_byteorder.h"
     11 #include "media/base/buffers.h"
     12 #include "media/base/decrypt_config.h"
     13 #include "media/webm/webm_constants.h"
     14 #include "media/webm/webm_crypto_helpers.h"
     15 
     16 namespace media {
     17 
     18 WebMClusterParser::TextTrackIterator::TextTrackIterator(
     19     const TextTrackMap& text_track_map) :
     20     iterator_(text_track_map.begin()),
     21     iterator_end_(text_track_map.end()) {
     22 }
     23 
     24 WebMClusterParser::TextTrackIterator::TextTrackIterator(
     25     const TextTrackIterator& rhs) :
     26     iterator_(rhs.iterator_),
     27     iterator_end_(rhs.iterator_end_) {
     28 }
     29 
     30 WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
     31 }
     32 
     33 bool WebMClusterParser::TextTrackIterator::operator()(
     34   int* track_num,
     35   const BufferQueue** buffers) {
     36   if (iterator_ == iterator_end_) {
     37     *track_num = 0;
     38     *buffers = NULL;
     39 
     40     return false;
     41   }
     42 
     43   *track_num = iterator_->first;
     44   *buffers = &iterator_->second.buffers();
     45 
     46   ++iterator_;
     47   return true;
     48 }
     49 
     50 WebMClusterParser::WebMClusterParser(
     51     int64 timecode_scale, int audio_track_num, int video_track_num,
     52     const WebMTracksParser::TextTracks& text_tracks,
     53     const std::set<int64>& ignored_tracks,
     54     const std::string& audio_encryption_key_id,
     55     const std::string& video_encryption_key_id,
     56     const LogCB& log_cb)
     57     : timecode_multiplier_(timecode_scale / 1000.0),
     58       ignored_tracks_(ignored_tracks),
     59       audio_encryption_key_id_(audio_encryption_key_id),
     60       video_encryption_key_id_(video_encryption_key_id),
     61       parser_(kWebMIdCluster, this),
     62       last_block_timecode_(-1),
     63       block_data_size_(-1),
     64       block_duration_(-1),
     65       block_add_id_(-1),
     66       block_additional_data_size_(-1),
     67       cluster_timecode_(-1),
     68       cluster_start_time_(kNoTimestamp()),
     69       cluster_ended_(false),
     70       audio_(audio_track_num, false),
     71       video_(video_track_num, true),
     72       log_cb_(log_cb) {
     73   for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
     74        it != text_tracks.end();
     75        ++it) {
     76     text_track_map_.insert(std::make_pair(it->first, Track(it->first, false)));
     77   }
     78 }
     79 
     80 WebMClusterParser::~WebMClusterParser() {}
     81 
     82 void WebMClusterParser::Reset() {
     83   last_block_timecode_ = -1;
     84   cluster_timecode_ = -1;
     85   cluster_start_time_ = kNoTimestamp();
     86   cluster_ended_ = false;
     87   parser_.Reset();
     88   audio_.Reset();
     89   video_.Reset();
     90   ResetTextTracks();
     91 }
     92 
     93 int WebMClusterParser::Parse(const uint8* buf, int size) {
     94   audio_.Reset();
     95   video_.Reset();
     96   ResetTextTracks();
     97 
     98   int result = parser_.Parse(buf, size);
     99 
    100   if (result < 0) {
    101     cluster_ended_ = false;
    102     return result;
    103   }
    104 
    105   cluster_ended_ = parser_.IsParsingComplete();
    106   if (cluster_ended_) {
    107     // If there were no buffers in this cluster, set the cluster start time to
    108     // be the |cluster_timecode_|.
    109     if (cluster_start_time_ == kNoTimestamp()) {
    110       DCHECK_GT(cluster_timecode_, -1);
    111       cluster_start_time_ = base::TimeDelta::FromMicroseconds(
    112           cluster_timecode_ * timecode_multiplier_);
    113     }
    114 
    115     // Reset the parser if we're done parsing so that
    116     // it is ready to accept another cluster on the next
    117     // call.
    118     parser_.Reset();
    119 
    120     last_block_timecode_ = -1;
    121     cluster_timecode_ = -1;
    122   }
    123 
    124   return result;
    125 }
    126 
    127 WebMClusterParser::TextTrackIterator
    128 WebMClusterParser::CreateTextTrackIterator() const {
    129   return TextTrackIterator(text_track_map_);
    130 }
    131 
    132 WebMParserClient* WebMClusterParser::OnListStart(int id) {
    133   if (id == kWebMIdCluster) {
    134     cluster_timecode_ = -1;
    135     cluster_start_time_ = kNoTimestamp();
    136   } else if (id == kWebMIdBlockGroup) {
    137     block_data_.reset();
    138     block_data_size_ = -1;
    139     block_duration_ = -1;
    140   } else if (id == kWebMIdBlockAdditions) {
    141     block_add_id_ = -1;
    142     block_additional_data_.reset();
    143     block_additional_data_size_ = -1;
    144   }
    145 
    146   return this;
    147 }
    148 
    149 bool WebMClusterParser::OnListEnd(int id) {
    150   if (id != kWebMIdBlockGroup)
    151     return true;
    152 
    153   // Make sure the BlockGroup actually had a Block.
    154   if (block_data_size_ == -1) {
    155     MEDIA_LOG(log_cb_) << "Block missing from BlockGroup.";
    156     return false;
    157   }
    158 
    159   bool result = ParseBlock(false, block_data_.get(), block_data_size_,
    160                            block_additional_data_.get(),
    161                            block_additional_data_size_, block_duration_);
    162   block_data_.reset();
    163   block_data_size_ = -1;
    164   block_duration_ = -1;
    165   block_add_id_ = -1;
    166   block_additional_data_.reset();
    167   block_additional_data_size_ = -1;
    168   return result;
    169 }
    170 
    171 bool WebMClusterParser::OnUInt(int id, int64 val) {
    172   int64* dst;
    173   switch (id) {
    174     case kWebMIdTimecode:
    175       dst = &cluster_timecode_;
    176       break;
    177     case kWebMIdBlockDuration:
    178       dst = &block_duration_;
    179       break;
    180     case kWebMIdBlockAddID:
    181       dst = &block_add_id_;
    182       break;
    183     default:
    184       return true;
    185   }
    186   if (*dst != -1)
    187     return false;
    188   *dst = val;
    189   return true;
    190 }
    191 
    192 bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
    193                                    int size, const uint8* additional,
    194                                    int additional_size, int duration) {
    195   if (size < 4)
    196     return false;
    197 
    198   // Return an error if the trackNum > 127. We just aren't
    199   // going to support large track numbers right now.
    200   if (!(buf[0] & 0x80)) {
    201     MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported";
    202     return false;
    203   }
    204 
    205   int track_num = buf[0] & 0x7f;
    206   int timecode = buf[1] << 8 | buf[2];
    207   int flags = buf[3] & 0xff;
    208   int lacing = (flags >> 1) & 0x3;
    209 
    210   if (lacing) {
    211     MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
    212     return false;
    213   }
    214 
    215   // Sign extend negative timecode offsets.
    216   if (timecode & 0x8000)
    217     timecode |= (-1 << 16);
    218 
    219   const uint8* frame_data = buf + 4;
    220   int frame_size = size - (frame_data - buf);
    221   return OnBlock(is_simple_block, track_num, timecode, duration, flags,
    222                  frame_data, frame_size, additional, additional_size);
    223 }
    224 
    225 bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
    226   switch (id) {
    227     case kWebMIdSimpleBlock:
    228       return ParseBlock(true, data, size, NULL, -1, -1);
    229 
    230     case kWebMIdBlock:
    231       if (block_data_) {
    232         MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not "
    233                               "supported.";
    234         return false;
    235       }
    236       block_data_.reset(new uint8[size]);
    237       memcpy(block_data_.get(), data, size);
    238       block_data_size_ = size;
    239       return true;
    240 
    241     case kWebMIdBlockAdditional: {
    242       uint64 block_add_id = base::HostToNet64(block_add_id_);
    243       if (block_additional_data_) {
    244         // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
    245         // as per matroska spec. But for now we don't have a use case to
    246         // support parsing of such files. Take a look at this again when such a
    247         // case arises.
    248         MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is "
    249                               "not supported.";
    250         return false;
    251       }
    252       // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
    253       // element's value in Big Endian format. This is done to mimic ffmpeg
    254       // demuxer's behavior.
    255       block_additional_data_size_ = size + sizeof(block_add_id);
    256       block_additional_data_.reset(new uint8[block_additional_data_size_]);
    257       memcpy(block_additional_data_.get(), &block_add_id,
    258              sizeof(block_add_id));
    259       memcpy(block_additional_data_.get() + 8, data, size);
    260       return true;
    261     }
    262 
    263     default:
    264       return true;
    265   }
    266 }
    267 
    268 bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
    269                                 int timecode,
    270                                 int  block_duration,
    271                                 int flags,
    272                                 const uint8* data, int size,
    273                                 const uint8* additional, int additional_size) {
    274   DCHECK_GE(size, 0);
    275   if (cluster_timecode_ == -1) {
    276     MEDIA_LOG(log_cb_) << "Got a block before cluster timecode.";
    277     return false;
    278   }
    279 
    280   if (timecode < 0) {
    281     MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset "
    282                        << timecode;
    283     return false;
    284   }
    285 
    286   if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
    287     MEDIA_LOG(log_cb_)
    288         << "Got a block with a timecode before the previous block.";
    289     return false;
    290   }
    291 
    292   Track* track = NULL;
    293   std::string encryption_key_id;
    294   if (track_num == audio_.track_num()) {
    295     track = &audio_;
    296     encryption_key_id = audio_encryption_key_id_;
    297   } else if (track_num == video_.track_num()) {
    298     track = &video_;
    299     encryption_key_id = video_encryption_key_id_;
    300   } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
    301     return true;
    302   } else if (Track* const text_track = FindTextTrack(track_num)) {
    303     if (is_simple_block)  // BlockGroup is required for WebVTT cues
    304       return false;
    305     if (block_duration < 0)  // not specified
    306       return false;
    307     track = text_track;
    308   } else {
    309     MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num;
    310     return false;
    311   }
    312 
    313   last_block_timecode_ = timecode;
    314 
    315   base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
    316       (cluster_timecode_ + timecode) * timecode_multiplier_);
    317 
    318   // The first bit of the flags is set when a SimpleBlock contains only
    319   // keyframes. If this is a Block, then inspection of the payload is
    320   // necessary to determine whether it contains a keyframe or not.
    321   // http://www.matroska.org/technical/specs/index.html
    322   bool is_keyframe =
    323       is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
    324 
    325   scoped_refptr<StreamParserBuffer> buffer =
    326       StreamParserBuffer::CopyFrom(data, size, additional, additional_size,
    327                                    is_keyframe);
    328 
    329   // Every encrypted Block has a signal byte and IV prepended to it. Current
    330   // encrypted WebM request for comments specification is here
    331   // http://wiki.webmproject.org/encryption/webm-encryption-rfc
    332   if (!encryption_key_id.empty()) {
    333     scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
    334         data, size,
    335         reinterpret_cast<const uint8*>(encryption_key_id.data()),
    336         encryption_key_id.size()));
    337     if (!config)
    338       return false;
    339     buffer->set_decrypt_config(config.Pass());
    340   }
    341 
    342   buffer->set_timestamp(timestamp);
    343   if (cluster_start_time_ == kNoTimestamp())
    344     cluster_start_time_ = timestamp;
    345 
    346   if (block_duration >= 0) {
    347     buffer->set_duration(base::TimeDelta::FromMicroseconds(
    348         block_duration * timecode_multiplier_));
    349   }
    350 
    351   return track->AddBuffer(buffer);
    352 }
    353 
    354 WebMClusterParser::Track::Track(int track_num, bool is_video)
    355     : track_num_(track_num),
    356       is_video_(is_video) {
    357 }
    358 
    359 WebMClusterParser::Track::~Track() {}
    360 
    361 bool WebMClusterParser::Track::AddBuffer(
    362     const scoped_refptr<StreamParserBuffer>& buffer) {
    363   DVLOG(2) << "AddBuffer() : " << track_num_
    364            << " ts " << buffer->timestamp().InSecondsF()
    365            << " dur " << buffer->duration().InSecondsF()
    366            << " kf " << buffer->IsKeyframe()
    367            << " size " << buffer->data_size();
    368 
    369   buffers_.push_back(buffer);
    370   return true;
    371 }
    372 
    373 void WebMClusterParser::Track::Reset() {
    374   buffers_.clear();
    375 }
    376 
    377 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
    378   // For now, assume that all blocks are keyframes for datatypes other than
    379   // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
    380   if (!is_video_)
    381     return true;
    382 
    383   // Make sure the block is big enough for the minimal keyframe header size.
    384   if (size < 7)
    385     return false;
    386 
    387   // The LSb of the first byte must be a 0 for a keyframe.
    388   // http://tools.ietf.org/html/rfc6386 Section 19.1
    389   if ((data[0] & 0x01) != 0)
    390     return false;
    391 
    392   // Verify VP8 keyframe startcode.
    393   // http://tools.ietf.org/html/rfc6386 Section 19.1
    394   if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
    395     return false;
    396 
    397   return true;
    398 }
    399 
    400 void WebMClusterParser::ResetTextTracks() {
    401   for (TextTrackMap::iterator it = text_track_map_.begin();
    402        it != text_track_map_.end();
    403        ++it) {
    404     it->second.Reset();
    405   }
    406 }
    407 
    408 WebMClusterParser::Track*
    409 WebMClusterParser::FindTextTrack(int track_num) {
    410   const TextTrackMap::iterator it = text_track_map_.find(track_num);
    411 
    412   if (it == text_track_map_.end())
    413     return NULL;
    414 
    415   return &it->second;
    416 }
    417 
    418 }  // namespace media
    419