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.
5 #include "media/formats/webm/webm_tracks_parser.h"
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"
16 static TextKind
CodecIdToTextKind(const std::string
& codec_id
) {
17 if (codec_id
== kWebMCodecSubtitles
)
18 return kTextSubtitles
;
20 if (codec_id
== kWebMCodecCaptions
)
23 if (codec_id
== kWebMCodecDescriptions
)
24 return kTextDescriptions
;
26 if (codec_id
== kWebMCodecMetadata
)
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();
37 int64 mult
= duration_in_ns
/ 1000;
38 mult
/= timecode_scale_in_us
;
40 return kNoTimestamp();
42 mult
= static_cast<double>(mult
) * timecode_scale_in_us
;
43 return base::TimeDelta::FromMicroseconds(mult
);
46 WebMTracksParser::WebMTracksParser(const scoped_refptr
<MediaLog
>& media_log
,
47 bool ignore_text_tracks
)
52 default_duration_(-1),
54 audio_default_duration_(-1),
56 video_default_duration_(-1),
57 ignore_text_tracks_(ignore_text_tracks
),
58 media_log_(media_log
),
59 audio_client_(media_log
),
60 video_client_(media_log
) {
63 WebMTracksParser::~WebMTracksParser() {}
65 int WebMTracksParser::Parse(const uint8
* buf
, int size
) {
68 default_duration_
= -1;
70 track_language_
.clear();
71 audio_track_num_
= -1;
72 audio_default_duration_
= -1;
73 audio_decoder_config_
= AudioDecoderConfig();
74 video_track_num_
= -1;
75 video_default_duration_
= -1;
76 video_decoder_config_
= VideoDecoderConfig();
78 ignored_tracks_
.clear();
80 WebMListParser
parser(kWebMIdTracks
, this);
81 int result
= parser
.Parse(buf
, size
);
86 // For now we do all or nothing parsing.
87 return parser
.IsParsingComplete() ? result
: 0;
90 base::TimeDelta
WebMTracksParser::GetAudioDefaultDuration(
91 const double timecode_scale_in_us
) const {
92 return PrecisionCappedDefaultDuration(timecode_scale_in_us
,
93 audio_default_duration_
);
96 base::TimeDelta
WebMTracksParser::GetVideoDefaultDuration(
97 const double timecode_scale_in_us
) const {
98 return PrecisionCappedDefaultDuration(timecode_scale_in_us
,
99 video_default_duration_
);
102 WebMParserClient
* WebMTracksParser::OnListStart(int id
) {
103 if (id
== kWebMIdContentEncodings
) {
104 DCHECK(!track_content_encodings_client_
.get());
105 track_content_encodings_client_
.reset(
106 new WebMContentEncodingsClient(media_log_
));
107 return track_content_encodings_client_
->OnListStart(id
);
110 if (id
== kWebMIdTrackEntry
) {
113 default_duration_
= -1;
115 track_language_
.clear();
117 codec_private_
.clear();
118 audio_client_
.Reset();
119 video_client_
.Reset();
123 if (id
== kWebMIdAudio
)
124 return &audio_client_
;
126 if (id
== kWebMIdVideo
)
127 return &video_client_
;
132 bool WebMTracksParser::OnListEnd(int id
) {
133 if (id
== kWebMIdContentEncodings
) {
134 DCHECK(track_content_encodings_client_
.get());
135 return track_content_encodings_client_
->OnListEnd(id
);
138 if (id
== kWebMIdTrackEntry
) {
139 if (track_type_
== -1 || track_num_
== -1) {
140 MEDIA_LOG(ERROR
, media_log_
) << "Missing TrackEntry data for "
141 << " TrackType " << track_type_
142 << " TrackNum " << track_num_
;
146 if (track_type_
!= kWebMTrackTypeAudio
&&
147 track_type_
!= kWebMTrackTypeVideo
&&
148 track_type_
!= kWebMTrackTypeSubtitlesOrCaptions
&&
149 track_type_
!= kWebMTrackTypeDescriptionsOrMetadata
) {
150 MEDIA_LOG(ERROR
, media_log_
) << "Unexpected TrackType " << track_type_
;
154 TextKind text_track_kind
= kTextNone
;
155 if (track_type_
== kWebMTrackTypeSubtitlesOrCaptions
) {
156 text_track_kind
= CodecIdToTextKind(codec_id_
);
157 if (text_track_kind
== kTextNone
) {
158 MEDIA_LOG(ERROR
, media_log_
) << "Missing TrackEntry CodecID"
159 << " TrackNum " << track_num_
;
163 if (text_track_kind
!= kTextSubtitles
&&
164 text_track_kind
!= kTextCaptions
) {
165 MEDIA_LOG(ERROR
, media_log_
) << "Wrong TrackEntry CodecID"
166 << " TrackNum " << track_num_
;
169 } else if (track_type_
== kWebMTrackTypeDescriptionsOrMetadata
) {
170 text_track_kind
= CodecIdToTextKind(codec_id_
);
171 if (text_track_kind
== kTextNone
) {
172 MEDIA_LOG(ERROR
, media_log_
) << "Missing TrackEntry CodecID"
173 << " TrackNum " << track_num_
;
177 if (text_track_kind
!= kTextDescriptions
&&
178 text_track_kind
!= kTextMetadata
) {
179 MEDIA_LOG(ERROR
, media_log_
) << "Wrong TrackEntry CodecID"
180 << " TrackNum " << track_num_
;
185 std::string encryption_key_id
;
186 if (track_content_encodings_client_
) {
187 DCHECK(!track_content_encodings_client_
->content_encodings().empty());
188 // If we have multiple ContentEncoding in one track. Always choose the
189 // key id in the first ContentEncoding as the key id of the track.
190 encryption_key_id
= track_content_encodings_client_
->
191 content_encodings()[0]->encryption_key_id();
194 if (track_type_
== kWebMTrackTypeAudio
) {
195 if (audio_track_num_
== -1) {
196 audio_track_num_
= track_num_
;
197 audio_encryption_key_id_
= encryption_key_id
;
199 if (default_duration_
== 0) {
200 MEDIA_LOG(ERROR
, media_log_
) << "Illegal 0ns audio TrackEntry "
204 audio_default_duration_
= default_duration_
;
206 DCHECK(!audio_decoder_config_
.IsValidConfig());
207 if (!audio_client_
.InitializeConfig(
208 codec_id_
, codec_private_
, seek_preroll_
, codec_delay_
,
209 !audio_encryption_key_id_
.empty(), &audio_decoder_config_
)) {
213 MEDIA_LOG(DEBUG
, media_log_
) << "Ignoring audio track " << track_num_
;
214 ignored_tracks_
.insert(track_num_
);
216 } else if (track_type_
== kWebMTrackTypeVideo
) {
217 if (video_track_num_
== -1) {
218 video_track_num_
= track_num_
;
219 video_encryption_key_id_
= encryption_key_id
;
221 if (default_duration_
== 0) {
222 MEDIA_LOG(ERROR
, media_log_
) << "Illegal 0ns video TrackEntry "
226 video_default_duration_
= default_duration_
;
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_
)) {
235 MEDIA_LOG(DEBUG
, media_log_
) << "Ignoring video track " << track_num_
;
236 ignored_tracks_
.insert(track_num_
);
238 } else if (track_type_
== kWebMTrackTypeSubtitlesOrCaptions
||
239 track_type_
== kWebMTrackTypeDescriptionsOrMetadata
) {
240 if (ignore_text_tracks_
) {
241 MEDIA_LOG(DEBUG
, media_log_
) << "Ignoring text track " << track_num_
;
242 ignored_tracks_
.insert(track_num_
);
244 std::string track_num
= base::Int64ToString(track_num_
);
245 text_tracks_
[track_num_
] = TextTrackConfig(
246 text_track_kind
, track_name_
, track_language_
, track_num
);
249 MEDIA_LOG(ERROR
, media_log_
) << "Unexpected TrackType " << track_type_
;
255 default_duration_
= -1;
257 track_language_
.clear();
259 codec_private_
.clear();
260 track_content_encodings_client_
.reset();
262 audio_client_
.Reset();
263 video_client_
.Reset();
270 bool WebMTracksParser::OnUInt(int id
, int64 val
) {
274 case kWebMIdTrackNumber
:
277 case kWebMIdTrackType
:
280 case kWebMIdSeekPreRoll
:
281 dst
= &seek_preroll_
;
283 case kWebMIdCodecDelay
:
286 case kWebMIdDefaultDuration
:
287 dst
= &default_duration_
;
294 MEDIA_LOG(ERROR
, media_log_
) << "Multiple values for id " << std::hex
<< id
303 bool WebMTracksParser::OnFloat(int id
, double val
) {
307 bool WebMTracksParser::OnBinary(int id
, const uint8
* data
, int size
) {
308 if (id
== kWebMIdCodecPrivate
) {
309 if (!codec_private_
.empty()) {
310 MEDIA_LOG(ERROR
, media_log_
)
311 << "Multiple CodecPrivate fields in a track.";
314 codec_private_
.assign(data
, data
+ size
);
320 bool WebMTracksParser::OnString(int id
, const std::string
& str
) {
321 if (id
== kWebMIdCodecID
) {
322 if (!codec_id_
.empty()) {
323 MEDIA_LOG(ERROR
, media_log_
) << "Multiple CodecID fields in a track";
331 if (id
== kWebMIdName
) {
336 if (id
== kWebMIdLanguage
) {
337 track_language_
= str
;