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 LogCB
& log_cb
, bool ignore_text_tracks
)
51 default_duration_(-1),
53 audio_default_duration_(-1),
55 video_default_duration_(-1),
56 ignore_text_tracks_(ignore_text_tracks
),
58 audio_client_(log_cb
),
59 video_client_(log_cb
) {
62 WebMTracksParser::~WebMTracksParser() {}
64 int WebMTracksParser::Parse(const uint8
* buf
, int size
) {
67 default_duration_
= -1;
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();
77 ignored_tracks_
.clear();
79 WebMListParser
parser(kWebMIdTracks
, this);
80 int result
= parser
.Parse(buf
, size
);
85 // For now we do all or nothing parsing.
86 return parser
.IsParsingComplete() ? result
: 0;
89 base::TimeDelta
WebMTracksParser::GetAudioDefaultDuration(
90 const double timecode_scale_in_us
) const {
91 return PrecisionCappedDefaultDuration(timecode_scale_in_us
,
92 audio_default_duration_
);
95 base::TimeDelta
WebMTracksParser::GetVideoDefaultDuration(
96 const double timecode_scale_in_us
) const {
97 return PrecisionCappedDefaultDuration(timecode_scale_in_us
,
98 video_default_duration_
);
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
);
109 if (id
== kWebMIdTrackEntry
) {
112 default_duration_
= -1;
114 track_language_
.clear();
116 codec_private_
.clear();
117 audio_client_
.Reset();
118 video_client_
.Reset();
122 if (id
== kWebMIdAudio
)
123 return &audio_client_
;
125 if (id
== kWebMIdVideo
)
126 return &video_client_
;
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
);
137 if (id
== kWebMIdTrackEntry
) {
138 if (track_type_
== -1 || track_num_
== -1) {
139 MEDIA_LOG(ERROR
, log_cb_
) << "Missing TrackEntry data for "
140 << " TrackType " << track_type_
<< " TrackNum "
145 if (track_type_
!= kWebMTrackTypeAudio
&&
146 track_type_
!= kWebMTrackTypeVideo
&&
147 track_type_
!= kWebMTrackTypeSubtitlesOrCaptions
&&
148 track_type_
!= kWebMTrackTypeDescriptionsOrMetadata
) {
149 MEDIA_LOG(ERROR
, log_cb_
) << "Unexpected TrackType " << track_type_
;
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(ERROR
, log_cb_
) << "Missing TrackEntry CodecID"
158 << " TrackNum " << track_num_
;
162 if (text_track_kind
!= kTextSubtitles
&&
163 text_track_kind
!= kTextCaptions
) {
164 MEDIA_LOG(ERROR
, log_cb_
) << "Wrong TrackEntry CodecID"
165 << " TrackNum " << track_num_
;
168 } else if (track_type_
== kWebMTrackTypeDescriptionsOrMetadata
) {
169 text_track_kind
= CodecIdToTextKind(codec_id_
);
170 if (text_track_kind
== kTextNone
) {
171 MEDIA_LOG(ERROR
, log_cb_
) << "Missing TrackEntry CodecID"
172 << " TrackNum " << track_num_
;
176 if (text_track_kind
!= kTextDescriptions
&&
177 text_track_kind
!= kTextMetadata
) {
178 MEDIA_LOG(ERROR
, log_cb_
) << "Wrong TrackEntry CodecID"
179 << " TrackNum " << track_num_
;
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();
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
;
198 if (default_duration_
== 0) {
199 MEDIA_LOG(ERROR
, log_cb_
) << "Illegal 0ns audio TrackEntry "
203 audio_default_duration_
= default_duration_
;
205 DCHECK(!audio_decoder_config_
.IsValidConfig());
206 if (!audio_client_
.InitializeConfig(
207 codec_id_
, codec_private_
, seek_preroll_
, codec_delay_
,
208 !audio_encryption_key_id_
.empty(), &audio_decoder_config_
)) {
212 MEDIA_LOG(DEBUG
, log_cb_
) << "Ignoring audio track " << track_num_
;
213 ignored_tracks_
.insert(track_num_
);
215 } else if (track_type_
== kWebMTrackTypeVideo
) {
216 if (video_track_num_
== -1) {
217 video_track_num_
= track_num_
;
218 video_encryption_key_id_
= encryption_key_id
;
220 if (default_duration_
== 0) {
221 MEDIA_LOG(ERROR
, log_cb_
) << "Illegal 0ns video TrackEntry "
225 video_default_duration_
= default_duration_
;
227 DCHECK(!video_decoder_config_
.IsValidConfig());
228 if (!video_client_
.InitializeConfig(
229 codec_id_
, codec_private_
, !video_encryption_key_id_
.empty(),
230 &video_decoder_config_
)) {
234 MEDIA_LOG(DEBUG
, log_cb_
) << "Ignoring video track " << track_num_
;
235 ignored_tracks_
.insert(track_num_
);
237 } else if (track_type_
== kWebMTrackTypeSubtitlesOrCaptions
||
238 track_type_
== kWebMTrackTypeDescriptionsOrMetadata
) {
239 if (ignore_text_tracks_
) {
240 MEDIA_LOG(DEBUG
, log_cb_
) << "Ignoring text track " << track_num_
;
241 ignored_tracks_
.insert(track_num_
);
243 std::string track_num
= base::Int64ToString(track_num_
);
244 text_tracks_
[track_num_
] = TextTrackConfig(
245 text_track_kind
, track_name_
, track_language_
, track_num
);
248 MEDIA_LOG(ERROR
, log_cb_
) << "Unexpected TrackType " << track_type_
;
254 default_duration_
= -1;
256 track_language_
.clear();
258 codec_private_
.clear();
259 track_content_encodings_client_
.reset();
261 audio_client_
.Reset();
262 video_client_
.Reset();
269 bool WebMTracksParser::OnUInt(int id
, int64 val
) {
273 case kWebMIdTrackNumber
:
276 case kWebMIdTrackType
:
279 case kWebMIdSeekPreRoll
:
280 dst
= &seek_preroll_
;
282 case kWebMIdCodecDelay
:
285 case kWebMIdDefaultDuration
:
286 dst
= &default_duration_
;
293 MEDIA_LOG(ERROR
, log_cb_
) << "Multiple values for id " << std::hex
<< id
302 bool WebMTracksParser::OnFloat(int id
, double val
) {
306 bool WebMTracksParser::OnBinary(int id
, const uint8
* data
, int size
) {
307 if (id
== kWebMIdCodecPrivate
) {
308 if (!codec_private_
.empty()) {
309 MEDIA_LOG(ERROR
, log_cb_
) << "Multiple CodecPrivate fields in a track.";
312 codec_private_
.assign(data
, data
+ size
);
318 bool WebMTracksParser::OnString(int id
, const std::string
& str
) {
319 if (id
== kWebMIdCodecID
) {
320 if (!codec_id_
.empty()) {
321 MEDIA_LOG(ERROR
, log_cb_
) << "Multiple CodecID fields in a track";
329 if (id
== kWebMIdName
) {
334 if (id
== kWebMIdLanguage
) {
335 track_language_
= str
;