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