cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / webm / webm_stream_parser.cc
blob12be44926842e3d0bd09abc082cd4d9b0adf2830
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"
7 #include <string>
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"
19 namespace media {
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);
50 init_cb_ = init_cb;
51 config_cb_ = config_cb;
52 new_buffers_cb_ = new_buffers_cb;
53 text_cb_ = text_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;
58 log_cb_ = log_cb;
61 void WebMStreamParser::Flush() {
62 DCHECK_NE(state_, kWaitingForInit);
64 byte_queue_.Reset();
66 if (state_ != kParsingClusters)
67 return;
69 cluster_parser_->Reset();
72 bool WebMStreamParser::Parse(const uint8* buf, int size) {
73 DCHECK_NE(state_, kWaitingForInit);
75 if (state_ == kError)
76 return false;
78 byte_queue_.Push(buf, size);
80 int result = 0;
81 int bytes_parsed = 0;
82 const uint8* cur = NULL;
83 int cur_size = 0;
85 byte_queue_.Peek(&cur, &cur_size);
86 while (cur_size > 0) {
87 State oldState = state_;
88 switch (state_) {
89 case kParsingHeaders:
90 result = ParseInfoAndTracks(cur, cur_size);
91 break;
93 case kParsingClusters:
94 result = ParseCluster(cur, cur_size);
95 break;
97 case kWaitingForInit:
98 case kError:
99 return false;
102 if (result < 0) {
103 ChangeState(kError);
104 return false;
107 if (state_ == oldState && result == 0)
108 break;
110 DCHECK_GE(result, 0);
111 cur += result;
112 cur_size -= result;
113 bytes_parsed += result;
116 byte_queue_.Pop(bytes_parsed);
117 return true;
120 void WebMStreamParser::ChangeState(State new_state) {
121 DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
122 state_ = new_state;
125 int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
126 DVLOG(2) << "ParseInfoAndTracks()";
127 DCHECK(data);
128 DCHECK_GT(size, 0);
130 const uint8* cur = data;
131 int cur_size = size;
132 int bytes_parsed = 0;
134 int id;
135 int64 element_size;
136 int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
138 if (result <= 0)
139 return result;
141 switch (id) {
142 case kWebMIdEBMLHeader:
143 case kWebMIdSeekHead:
144 case kWebMIdVoid:
145 case kWebMIdCRC32:
146 case kWebMIdCues:
147 case kWebMIdChapters:
148 if (cur_size < (result + element_size)) {
149 // We don't have the whole element yet. Signal we need more data.
150 return 0;
152 // Skip the element.
153 return result + element_size;
154 break;
155 case kWebMIdSegment:
156 // Just consume the segment header.
157 return result;
158 break;
159 case kWebMIdInfo:
160 // We've found the element we are looking for.
161 break;
162 default: {
163 MEDIA_LOG(log_cb_) << "Unexpected element ID 0x" << std::hex << id;
164 return -1;
168 WebMInfoParser info_parser;
169 result = info_parser.Parse(cur, cur_size);
171 if (result <= 0)
172 return result;
174 cur += result;
175 cur_size -= result;
176 bytes_parsed += result;
178 WebMTracksParser tracks_parser(log_cb_, add_text_track_cb_.is_null());
179 result = tracks_parser.Parse(cur, cur_size);
181 if (result <= 0)
182 return result;
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.";
204 return -1;
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.)
224 if (text_track)
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(),
232 text_tracks,
233 tracks_parser.ignored_tracks(),
234 tracks_parser.audio_encryption_key_id(),
235 tracks_parser.video_encryption_key_id(),
236 log_cb_));
238 ChangeState(kParsingClusters);
240 if (!init_cb_.is_null()) {
241 init_cb_.Run(true, duration);
242 init_cb_.Reset();
245 return bytes_parsed;
248 int WebMStreamParser::ParseCluster(const uint8* data, int size) {
249 if (!cluster_parser_)
250 return -1;
252 int id;
253 int64 element_size;
254 int result = WebMParseElementHeader(data, size, &id, &element_size);
256 if (result <= 0)
257 return result;
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.
266 return 0;
268 // Skip the element.
269 return result + element_size;
272 if (id == kWebMIdEBMLHeader) {
273 ChangeState(kParsingHeaders);
274 return 0;
277 int bytes_parsed = cluster_parser_->Parse(data, size);
279 if (bytes_parsed <= 0)
280 return bytes_parsed;
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)) {
294 return -1;
297 WebMClusterParser::TextTrackIterator text_track_iter =
298 cluster_parser_->CreateTextTrackIterator();
300 int text_track_num;
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())
307 continue;
309 TextTrack* const text_track = find_result->second;
311 if (!text_buffers->empty() && !text_cb_.Run(text_track, *text_buffers))
312 return -1;
315 if (cluster_ended)
316 end_of_segment_cb_.Run();
318 return bytes_parsed;
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);
326 } // namespace media