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_stream_parser.h"
      6 
      7 #include <string>
      8 
      9 #include "base/callback.h"
     10 #include "base/logging.h"
     11 #include "base/stl_util.h"
     12 #include "media/webm/webm_cluster_parser.h"
     13 #include "media/webm/webm_constants.h"
     14 #include "media/webm/webm_content_encodings.h"
     15 #include "media/webm/webm_crypto_helpers.h"
     16 #include "media/webm/webm_info_parser.h"
     17 #include "media/webm/webm_tracks_parser.h"
     18 
     19 namespace media {
     20 
     21 WebMStreamParser::WebMStreamParser()
     22     : state_(kWaitingForInit),
     23       waiting_for_buffers_(false) {
     24 }
     25 
     26 WebMStreamParser::~WebMStreamParser() {
     27   STLDeleteValues(&text_track_map_);
     28 }
     29 
     30 void WebMStreamParser::Init(const InitCB& init_cb,
     31                             const NewConfigCB& config_cb,
     32                             const NewBuffersCB& new_buffers_cb,
     33                             const NewTextBuffersCB& text_cb,
     34                             const NeedKeyCB& need_key_cb,
     35                             const AddTextTrackCB& add_text_track_cb,
     36                             const NewMediaSegmentCB& new_segment_cb,
     37                             const base::Closure& end_of_segment_cb,
     38                             const LogCB& log_cb) {
     39   DCHECK_EQ(state_, kWaitingForInit);
     40   DCHECK(init_cb_.is_null());
     41   DCHECK(!init_cb.is_null());
     42   DCHECK(!config_cb.is_null());
     43   DCHECK(!new_buffers_cb.is_null());
     44   DCHECK(!text_cb.is_null());
     45   DCHECK(!need_key_cb.is_null());
     46   DCHECK(!new_segment_cb.is_null());
     47   DCHECK(!end_of_segment_cb.is_null());
     48 
     49   ChangeState(kParsingHeaders);
     50   init_cb_ = init_cb;
     51   config_cb_ = config_cb;
     52   new_buffers_cb_ = new_buffers_cb;
     53   text_cb_ = text_cb;
     54   need_key_cb_ = need_key_cb;
     55   add_text_track_cb_ = add_text_track_cb;
     56   new_segment_cb_ = new_segment_cb;
     57   end_of_segment_cb_ = end_of_segment_cb;
     58   log_cb_ = log_cb;
     59 }
     60 
     61 void WebMStreamParser::Flush() {
     62   DCHECK_NE(state_, kWaitingForInit);
     63 
     64   byte_queue_.Reset();
     65 
     66   if (state_ != kParsingClusters)
     67     return;
     68 
     69   cluster_parser_->Reset();
     70 }
     71 
     72 bool WebMStreamParser::Parse(const uint8* buf, int size) {
     73   DCHECK_NE(state_, kWaitingForInit);
     74 
     75   if (state_ == kError)
     76     return false;
     77 
     78   byte_queue_.Push(buf, size);
     79 
     80   int result = 0;
     81   int bytes_parsed = 0;
     82   const uint8* cur = NULL;
     83   int cur_size = 0;
     84 
     85   byte_queue_.Peek(&cur, &cur_size);
     86   while (cur_size > 0) {
     87     State oldState = state_;
     88     switch (state_) {
     89       case kParsingHeaders:
     90         result = ParseInfoAndTracks(cur, cur_size);
     91         break;
     92 
     93       case kParsingClusters:
     94         result = ParseCluster(cur, cur_size);
     95         break;
     96 
     97       case kWaitingForInit:
     98       case kError:
     99         return false;
    100     }
    101 
    102     if (result < 0) {
    103       ChangeState(kError);
    104       return false;
    105     }
    106 
    107     if (state_ == oldState && result == 0)
    108       break;
    109 
    110     DCHECK_GE(result, 0);
    111     cur += result;
    112     cur_size -= result;
    113     bytes_parsed += result;
    114   }
    115 
    116   byte_queue_.Pop(bytes_parsed);
    117   return true;
    118 }
    119 
    120 void WebMStreamParser::ChangeState(State new_state) {
    121   DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
    122   state_ = new_state;
    123 }
    124 
    125 int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
    126   DVLOG(2) << "ParseInfoAndTracks()";
    127   DCHECK(data);
    128   DCHECK_GT(size, 0);
    129 
    130   const uint8* cur = data;
    131   int cur_size = size;
    132   int bytes_parsed = 0;
    133 
    134   int id;
    135   int64 element_size;
    136   int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
    137 
    138   if (result <= 0)
    139     return result;
    140 
    141   switch (id) {
    142     case kWebMIdEBMLHeader:
    143     case kWebMIdSeekHead:
    144     case kWebMIdVoid:
    145     case kWebMIdCRC32:
    146     case kWebMIdCues:
    147     case kWebMIdChapters:
    148       if (cur_size < (result + element_size)) {
    149         // We don't have the whole element yet. Signal we need more data.
    150         return 0;
    151       }
    152       // Skip the element.
    153       return result + element_size;
    154       break;
    155     case kWebMIdSegment:
    156       // Just consume the segment header.
    157       return result;
    158       break;
    159     case kWebMIdInfo:
    160       // We've found the element we are looking for.
    161       break;
    162     default: {
    163       MEDIA_LOG(log_cb_) << "Unexpected element ID 0x" << std::hex << id;
    164       return -1;
    165     }
    166   }
    167 
    168   WebMInfoParser info_parser;
    169   result = info_parser.Parse(cur, cur_size);
    170 
    171   if (result <= 0)
    172     return result;
    173 
    174   cur += result;
    175   cur_size -= result;
    176   bytes_parsed += result;
    177 
    178   WebMTracksParser tracks_parser(log_cb_, add_text_track_cb_.is_null());
    179   result = tracks_parser.Parse(cur, cur_size);
    180 
    181   if (result <= 0)
    182     return result;
    183 
    184   bytes_parsed += result;
    185 
    186   base::TimeDelta duration = kInfiniteDuration();
    187 
    188   if (info_parser.duration() > 0) {
    189     double mult = info_parser.timecode_scale() / 1000.0;
    190     int64 duration_in_us = info_parser.duration() * mult;
    191     duration = base::TimeDelta::FromMicroseconds(duration_in_us);
    192   }
    193 
    194   const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config();
    195   if (audio_config.is_encrypted())
    196     FireNeedKey(tracks_parser.audio_encryption_key_id());
    197 
    198   const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config();
    199   if (video_config.is_encrypted())
    200     FireNeedKey(tracks_parser.video_encryption_key_id());
    201 
    202   if (!config_cb_.Run(audio_config, video_config)) {
    203     DVLOG(1) << "New config data isn't allowed.";
    204     return -1;
    205   }
    206 
    207   typedef WebMTracksParser::TextTracks TextTracks;
    208   const TextTracks& text_tracks = tracks_parser.text_tracks();
    209 
    210   for (TextTracks::const_iterator itr = text_tracks.begin();
    211        itr != text_tracks.end(); ++itr) {
    212     const WebMTracksParser::TextTrackInfo& text_track_info = itr->second;
    213 
    214     // TODO(matthewjheaney): verify that WebVTT uses ISO 639-2 for lang
    215     scoped_ptr<TextTrack> text_track =
    216         add_text_track_cb_.Run(text_track_info.kind,
    217                                text_track_info.name,
    218                                text_track_info.language);
    219 
    220     // Assume ownership of pointer, and cache the text track object, for use
    221     // later when we have text track buffers. (The text track objects are
    222     // deallocated in the dtor for this class.)
    223 
    224     if (text_track)
    225       text_track_map_.insert(std::make_pair(itr->first, text_track.release()));
    226   }
    227 
    228   cluster_parser_.reset(new WebMClusterParser(
    229       info_parser.timecode_scale(),
    230       tracks_parser.audio_track_num(),
    231       tracks_parser.video_track_num(),
    232       text_tracks,
    233       tracks_parser.ignored_tracks(),
    234       tracks_parser.audio_encryption_key_id(),
    235       tracks_parser.video_encryption_key_id(),
    236       log_cb_));
    237 
    238   ChangeState(kParsingClusters);
    239 
    240   if (!init_cb_.is_null()) {
    241     init_cb_.Run(true, duration);
    242     init_cb_.Reset();
    243   }
    244 
    245   return bytes_parsed;
    246 }
    247 
    248 int WebMStreamParser::ParseCluster(const uint8* data, int size) {
    249   if (!cluster_parser_)
    250     return -1;
    251 
    252   int id;
    253   int64 element_size;
    254   int result = WebMParseElementHeader(data, size, &id, &element_size);
    255 
    256   if (result <= 0)
    257     return result;
    258 
    259   if (id == kWebMIdCluster)
    260     waiting_for_buffers_ = true;
    261 
    262   // TODO(matthewjheaney): implement support for chapters
    263   if (id == kWebMIdCues || id == kWebMIdChapters) {
    264     if (size < (result + element_size)) {
    265       // We don't have the whole element yet. Signal we need more data.
    266       return 0;
    267     }
    268     // Skip the element.
    269     return result + element_size;
    270   }
    271 
    272   if (id == kWebMIdEBMLHeader) {
    273     ChangeState(kParsingHeaders);
    274     return 0;
    275   }
    276 
    277   int bytes_parsed = cluster_parser_->Parse(data, size);
    278 
    279   if (bytes_parsed <= 0)
    280     return bytes_parsed;
    281 
    282   const BufferQueue& audio_buffers = cluster_parser_->audio_buffers();
    283   const BufferQueue& video_buffers = cluster_parser_->video_buffers();
    284   bool cluster_ended = cluster_parser_->cluster_ended();
    285 
    286   if (waiting_for_buffers_ &&
    287       cluster_parser_->cluster_start_time() != kNoTimestamp()) {
    288     new_segment_cb_.Run();
    289     waiting_for_buffers_ = false;
    290   }
    291 
    292   if ((!audio_buffers.empty() || !video_buffers.empty()) &&
    293       !new_buffers_cb_.Run(audio_buffers, video_buffers)) {
    294     return -1;
    295   }
    296 
    297   WebMClusterParser::TextTrackIterator text_track_iter =
    298     cluster_parser_->CreateTextTrackIterator();
    299 
    300   int text_track_num;
    301   const BufferQueue* text_buffers;
    302 
    303   while (text_track_iter(&text_track_num, &text_buffers)) {
    304     TextTrackMap::iterator find_result = text_track_map_.find(text_track_num);
    305 
    306     if (find_result == text_track_map_.end())
    307       continue;
    308 
    309     TextTrack* const text_track = find_result->second;
    310 
    311     if (!text_buffers->empty() && !text_cb_.Run(text_track, *text_buffers))
    312       return -1;
    313   }
    314 
    315   if (cluster_ended)
    316     end_of_segment_cb_.Run();
    317 
    318   return bytes_parsed;
    319 }
    320 
    321 void WebMStreamParser::FireNeedKey(const std::string& key_id) {
    322   int key_id_size = key_id.size();
    323   DCHECK_GT(key_id_size, 0);
    324   scoped_ptr<uint8[]> key_id_array(new uint8[key_id_size]);
    325   memcpy(key_id_array.get(), key_id.data(), key_id_size);
    326   need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size);
    327 }
    328 
    329 }  // namespace media
    330