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/webm_tracks_parser.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/strings/string_util.h"
     10 #include "media/base/buffers.h"
     11 #include "media/formats/webm/webm_constants.h"
     12 #include "media/formats/webm/webm_content_encodings.h"
     13 
     14 namespace media {
     15 
     16 static TextKind CodecIdToTextKind(const std::string& codec_id) {
     17   if (codec_id == kWebMCodecSubtitles)
     18     return kTextSubtitles;
     19 
     20   if (codec_id == kWebMCodecCaptions)
     21     return kTextCaptions;
     22 
     23   if (codec_id == kWebMCodecDescriptions)
     24     return kTextDescriptions;
     25 
     26   if (codec_id == kWebMCodecMetadata)
     27     return kTextMetadata;
     28 
     29   return kTextNone;
     30 }
     31 
     32 static base::TimeDelta PrecisionCappedDefaultDuration(
     33     const double timecode_scale_in_us, const int64 duration_in_ns) {
     34   if (duration_in_ns <= 0)
     35     return kNoTimestamp();
     36 
     37   int64 mult = duration_in_ns / 1000;
     38   mult /= timecode_scale_in_us;
     39   if (mult == 0)
     40     return kNoTimestamp();
     41 
     42   mult = static_cast<double>(mult) * timecode_scale_in_us;
     43   return base::TimeDelta::FromMicroseconds(mult);
     44 }
     45 
     46 WebMTracksParser::WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks)
     47     : track_type_(-1),
     48       track_num_(-1),
     49       seek_preroll_(-1),
     50       codec_delay_(-1),
     51       default_duration_(-1),
     52       audio_track_num_(-1),
     53       audio_default_duration_(-1),
     54       video_track_num_(-1),
     55       video_default_duration_(-1),
     56       ignore_text_tracks_(ignore_text_tracks),
     57       log_cb_(log_cb),
     58       audio_client_(log_cb),
     59       video_client_(log_cb) {
     60 }
     61 
     62 WebMTracksParser::~WebMTracksParser() {}
     63 
     64 int WebMTracksParser::Parse(const uint8* buf, int size) {
     65   track_type_ =-1;
     66   track_num_ = -1;
     67   default_duration_ = -1;
     68   track_name_.clear();
     69   track_language_.clear();
     70   audio_track_num_ = -1;
     71   audio_default_duration_ = -1;
     72   audio_decoder_config_ = AudioDecoderConfig();
     73   video_track_num_ = -1;
     74   video_default_duration_ = -1;
     75   video_decoder_config_ = VideoDecoderConfig();
     76   text_tracks_.clear();
     77   ignored_tracks_.clear();
     78 
     79   WebMListParser parser(kWebMIdTracks, this);
     80   int result = parser.Parse(buf, size);
     81 
     82   if (result <= 0)
     83     return result;
     84 
     85   // For now we do all or nothing parsing.
     86   return parser.IsParsingComplete() ? result : 0;
     87 }
     88 
     89 base::TimeDelta WebMTracksParser::GetAudioDefaultDuration(
     90     const double timecode_scale_in_us) const {
     91   return PrecisionCappedDefaultDuration(timecode_scale_in_us,
     92                                         audio_default_duration_);
     93 }
     94 
     95 base::TimeDelta WebMTracksParser::GetVideoDefaultDuration(
     96     const double timecode_scale_in_us) const {
     97   return PrecisionCappedDefaultDuration(timecode_scale_in_us,
     98                                         video_default_duration_);
     99 }
    100 
    101 WebMParserClient* WebMTracksParser::OnListStart(int id) {
    102   if (id == kWebMIdContentEncodings) {
    103     DCHECK(!track_content_encodings_client_.get());
    104     track_content_encodings_client_.reset(
    105         new WebMContentEncodingsClient(log_cb_));
    106     return track_content_encodings_client_->OnListStart(id);
    107   }
    108 
    109   if (id == kWebMIdTrackEntry) {
    110     track_type_ = -1;
    111     track_num_ = -1;
    112     default_duration_ = -1;
    113     track_name_.clear();
    114     track_language_.clear();
    115     codec_id_ = "";
    116     codec_private_.clear();
    117     audio_client_.Reset();
    118     video_client_.Reset();
    119     return this;
    120   }
    121 
    122   if (id == kWebMIdAudio)
    123     return &audio_client_;
    124 
    125   if (id == kWebMIdVideo)
    126     return &video_client_;
    127 
    128   return this;
    129 }
    130 
    131 bool WebMTracksParser::OnListEnd(int id) {
    132   if (id == kWebMIdContentEncodings) {
    133     DCHECK(track_content_encodings_client_.get());
    134     return track_content_encodings_client_->OnListEnd(id);
    135   }
    136 
    137   if (id == kWebMIdTrackEntry) {
    138     if (track_type_ == -1 || track_num_ == -1) {
    139       MEDIA_LOG(log_cb_) << "Missing TrackEntry data for "
    140                          << " TrackType " << track_type_
    141                          << " TrackNum " << track_num_;
    142       return false;
    143     }
    144 
    145     if (track_type_ != kWebMTrackTypeAudio &&
    146         track_type_ != kWebMTrackTypeVideo &&
    147         track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
    148         track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
    149       MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
    150       return false;
    151     }
    152 
    153     TextKind text_track_kind = kTextNone;
    154     if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
    155       text_track_kind = CodecIdToTextKind(codec_id_);
    156       if (text_track_kind == kTextNone) {
    157         MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
    158                            << " TrackNum " << track_num_;
    159         return false;
    160       }
    161 
    162       if (text_track_kind != kTextSubtitles &&
    163           text_track_kind != kTextCaptions) {
    164         MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
    165                            << " TrackNum " << track_num_;
    166         return false;
    167       }
    168     } else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
    169       text_track_kind = CodecIdToTextKind(codec_id_);
    170       if (text_track_kind == kTextNone) {
    171         MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
    172                            << " TrackNum " << track_num_;
    173         return false;
    174       }
    175 
    176       if (text_track_kind != kTextDescriptions &&
    177           text_track_kind != kTextMetadata) {
    178         MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
    179                            << " TrackNum " << track_num_;
    180         return false;
    181       }
    182     }
    183 
    184     std::string encryption_key_id;
    185     if (track_content_encodings_client_) {
    186       DCHECK(!track_content_encodings_client_->content_encodings().empty());
    187       // If we have multiple ContentEncoding in one track. Always choose the
    188       // key id in the first ContentEncoding as the key id of the track.
    189       encryption_key_id = track_content_encodings_client_->
    190           content_encodings()[0]->encryption_key_id();
    191     }
    192 
    193     if (track_type_ == kWebMTrackTypeAudio) {
    194       if (audio_track_num_ == -1) {
    195         audio_track_num_ = track_num_;
    196         audio_encryption_key_id_ = encryption_key_id;
    197 
    198         if (default_duration_ == 0) {
    199           MEDIA_LOG(log_cb_) << "Illegal 0ns audio TrackEntry DefaultDuration";
    200           return false;
    201         }
    202         audio_default_duration_ = default_duration_;
    203 
    204         DCHECK(!audio_decoder_config_.IsValidConfig());
    205         if (!audio_client_.InitializeConfig(
    206                 codec_id_, codec_private_, seek_preroll_, codec_delay_,
    207                 !audio_encryption_key_id_.empty(), &audio_decoder_config_)) {
    208           return false;
    209         }
    210       } else {
    211         MEDIA_LOG(log_cb_) << "Ignoring audio track " << track_num_;
    212         ignored_tracks_.insert(track_num_);
    213       }
    214     } else if (track_type_ == kWebMTrackTypeVideo) {
    215       if (video_track_num_ == -1) {
    216         video_track_num_ = track_num_;
    217         video_encryption_key_id_ = encryption_key_id;
    218 
    219         if (default_duration_ == 0) {
    220           MEDIA_LOG(log_cb_) << "Illegal 0ns video TrackEntry DefaultDuration";
    221           return false;
    222         }
    223         video_default_duration_ = default_duration_;
    224 
    225         DCHECK(!video_decoder_config_.IsValidConfig());
    226         if (!video_client_.InitializeConfig(
    227                 codec_id_, codec_private_, !video_encryption_key_id_.empty(),
    228                 &video_decoder_config_)) {
    229           return false;
    230         }
    231       } else {
    232         MEDIA_LOG(log_cb_) << "Ignoring video track " << track_num_;
    233         ignored_tracks_.insert(track_num_);
    234       }
    235     } else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
    236                track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
    237       if (ignore_text_tracks_) {
    238         MEDIA_LOG(log_cb_) << "Ignoring text track " << track_num_;
    239         ignored_tracks_.insert(track_num_);
    240       } else {
    241         std::string track_num = base::Int64ToString(track_num_);
    242         text_tracks_[track_num_] = TextTrackConfig(
    243             text_track_kind, track_name_, track_language_, track_num);
    244       }
    245     } else {
    246       MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
    247       return false;
    248     }
    249 
    250     track_type_ = -1;
    251     track_num_ = -1;
    252     default_duration_ = -1;
    253     track_name_.clear();
    254     track_language_.clear();
    255     codec_id_ = "";
    256     codec_private_.clear();
    257     track_content_encodings_client_.reset();
    258 
    259     audio_client_.Reset();
    260     video_client_.Reset();
    261     return true;
    262   }
    263 
    264   return true;
    265 }
    266 
    267 bool WebMTracksParser::OnUInt(int id, int64 val) {
    268   int64* dst = NULL;
    269 
    270   switch (id) {
    271     case kWebMIdTrackNumber:
    272       dst = &track_num_;
    273       break;
    274     case kWebMIdTrackType:
    275       dst = &track_type_;
    276       break;
    277     case kWebMIdSeekPreRoll:
    278       dst = &seek_preroll_;
    279       break;
    280     case kWebMIdCodecDelay:
    281       dst = &codec_delay_;
    282       break;
    283     case kWebMIdDefaultDuration:
    284       dst = &default_duration_;
    285       break;
    286     default:
    287       return true;
    288   }
    289 
    290   if (*dst != -1) {
    291     MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
    292                        << " specified";
    293     return false;
    294   }
    295 
    296   *dst = val;
    297   return true;
    298 }
    299 
    300 bool WebMTracksParser::OnFloat(int id, double val) {
    301   return true;
    302 }
    303 
    304 bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
    305   if (id == kWebMIdCodecPrivate) {
    306     if (!codec_private_.empty()) {
    307       MEDIA_LOG(log_cb_) << "Multiple CodecPrivate fields in a track.";
    308       return false;
    309     }
    310     codec_private_.assign(data, data + size);
    311     return true;
    312   }
    313   return true;
    314 }
    315 
    316 bool WebMTracksParser::OnString(int id, const std::string& str) {
    317   if (id == kWebMIdCodecID) {
    318     if (!codec_id_.empty()) {
    319       MEDIA_LOG(log_cb_) << "Multiple CodecID fields in a track";
    320       return false;
    321     }
    322 
    323     codec_id_ = str;
    324     return true;
    325   }
    326 
    327   if (id == kWebMIdName) {
    328     track_name_ = str;
    329     return true;
    330   }
    331 
    332   if (id == kWebMIdLanguage) {
    333     track_language_ = str;
    334     return true;
    335   }
    336 
    337   return true;
    338 }
    339 
    340 }  // namespace media
    341