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.
5 #include "media/webm/webm_stream_parser.h"
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"
21 WebMStreamParser::WebMStreamParser()
22 : state_(kWaitingForInit
),
23 waiting_for_buffers_(false) {
26 WebMStreamParser::~WebMStreamParser() {
27 STLDeleteValues(&text_track_map_
);
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());
49 ChangeState(kParsingHeaders
);
51 config_cb_
= config_cb
;
52 new_buffers_cb_
= new_buffers_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
;
61 void WebMStreamParser::Flush() {
62 DCHECK_NE(state_
, kWaitingForInit
);
66 if (state_
!= kParsingClusters
)
69 cluster_parser_
->Reset();
72 bool WebMStreamParser::Parse(const uint8
* buf
, int size
) {
73 DCHECK_NE(state_
, kWaitingForInit
);
78 byte_queue_
.Push(buf
, size
);
82 const uint8
* cur
= NULL
;
85 byte_queue_
.Peek(&cur
, &cur_size
);
86 while (cur_size
> 0) {
87 State oldState
= state_
;
90 result
= ParseInfoAndTracks(cur
, cur_size
);
93 case kParsingClusters
:
94 result
= ParseCluster(cur
, cur_size
);
107 if (state_
== oldState
&& result
== 0)
110 DCHECK_GE(result
, 0);
113 bytes_parsed
+= result
;
116 byte_queue_
.Pop(bytes_parsed
);
120 void WebMStreamParser::ChangeState(State new_state
) {
121 DVLOG(1) << "ChangeState() : " << state_
<< " -> " << new_state
;
125 int WebMStreamParser::ParseInfoAndTracks(const uint8
* data
, int size
) {
126 DVLOG(2) << "ParseInfoAndTracks()";
130 const uint8
* cur
= data
;
132 int bytes_parsed
= 0;
136 int result
= WebMParseElementHeader(cur
, cur_size
, &id
, &element_size
);
142 case kWebMIdEBMLHeader
:
143 case kWebMIdSeekHead
:
147 case kWebMIdChapters
:
148 if (cur_size
< (result
+ element_size
)) {
149 // We don't have the whole element yet. Signal we need more data.
153 return result
+ element_size
;
156 // Just consume the segment header.
160 // We've found the element we are looking for.
163 MEDIA_LOG(log_cb_
) << "Unexpected element ID 0x" << std::hex
<< id
;
168 WebMInfoParser info_parser
;
169 result
= info_parser
.Parse(cur
, cur_size
);
176 bytes_parsed
+= result
;
178 WebMTracksParser
tracks_parser(log_cb_
, add_text_track_cb_
.is_null());
179 result
= tracks_parser
.Parse(cur
, cur_size
);
184 bytes_parsed
+= result
;
186 base::TimeDelta duration
= kInfiniteDuration();
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
);
194 const AudioDecoderConfig
& audio_config
= tracks_parser
.audio_decoder_config();
195 if (audio_config
.is_encrypted())
196 FireNeedKey(tracks_parser
.audio_encryption_key_id());
198 const VideoDecoderConfig
& video_config
= tracks_parser
.video_decoder_config();
199 if (video_config
.is_encrypted())
200 FireNeedKey(tracks_parser
.video_encryption_key_id());
202 if (!config_cb_
.Run(audio_config
, video_config
)) {
203 DVLOG(1) << "New config data isn't allowed.";
207 typedef WebMTracksParser::TextTracks TextTracks
;
208 const TextTracks
& text_tracks
= tracks_parser
.text_tracks();
210 for (TextTracks::const_iterator itr
= text_tracks
.begin();
211 itr
!= text_tracks
.end(); ++itr
) {
212 const WebMTracksParser::TextTrackInfo
& text_track_info
= itr
->second
;
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
);
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.)
225 text_track_map_
.insert(std::make_pair(itr
->first
, text_track
.release()));
228 cluster_parser_
.reset(new WebMClusterParser(
229 info_parser
.timecode_scale(),
230 tracks_parser
.audio_track_num(),
231 tracks_parser
.video_track_num(),
233 tracks_parser
.ignored_tracks(),
234 tracks_parser
.audio_encryption_key_id(),
235 tracks_parser
.video_encryption_key_id(),
238 ChangeState(kParsingClusters
);
240 if (!init_cb_
.is_null()) {
241 init_cb_
.Run(true, duration
);
248 int WebMStreamParser::ParseCluster(const uint8
* data
, int size
) {
249 if (!cluster_parser_
)
254 int result
= WebMParseElementHeader(data
, size
, &id
, &element_size
);
259 if (id
== kWebMIdCluster
)
260 waiting_for_buffers_
= true;
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.
269 return result
+ element_size
;
272 if (id
== kWebMIdEBMLHeader
) {
273 ChangeState(kParsingHeaders
);
277 int bytes_parsed
= cluster_parser_
->Parse(data
, size
);
279 if (bytes_parsed
<= 0)
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();
286 if (waiting_for_buffers_
&&
287 cluster_parser_
->cluster_start_time() != kNoTimestamp()) {
288 new_segment_cb_
.Run();
289 waiting_for_buffers_
= false;
292 if ((!audio_buffers
.empty() || !video_buffers
.empty()) &&
293 !new_buffers_cb_
.Run(audio_buffers
, video_buffers
)) {
297 WebMClusterParser::TextTrackIterator text_track_iter
=
298 cluster_parser_
->CreateTextTrackIterator();
301 const BufferQueue
* text_buffers
;
303 while (text_track_iter(&text_track_num
, &text_buffers
)) {
304 TextTrackMap::iterator find_result
= text_track_map_
.find(text_track_num
);
306 if (find_result
== text_track_map_
.end())
309 TextTrack
* const text_track
= find_result
->second
;
311 if (!text_buffers
->empty() && !text_cb_
.Run(text_track
, *text_buffers
))
316 end_of_segment_cb_
.Run();
321 void WebMStreamParser::FireNeedKey(const std::string
& key_id
) {
322 std::vector
<uint8
> key_id_vector(key_id
.begin(), key_id
.end());
323 need_key_cb_
.Run(kWebMEncryptInitDataType
, key_id_vector
);