Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/webm/webm_stream_parser.h" | 5 #include "media/webm/webm_stream_parser.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "media/webm/webm_cluster_parser.h" | 11 #include "media/webm/webm_cluster_parser.h" |
| 12 #include "media/webm/webm_constants.h" | 12 #include "media/webm/webm_constants.h" |
| 13 #include "media/webm/webm_content_encodings.h" | 13 #include "media/webm/webm_content_encodings.h" |
| 14 #include "media/webm/webm_crypto_helpers.h" | 14 #include "media/webm/webm_crypto_helpers.h" |
| 15 #include "media/webm/webm_info_parser.h" | 15 #include "media/webm/webm_info_parser.h" |
| 16 #include "media/webm/webm_tracks_parser.h" | 16 #include "media/webm/webm_tracks_parser.h" |
| 17 | 17 |
| 18 namespace media { | 18 namespace media { |
| 19 | 19 |
| 20 // To fetch lines of text from embedded WebVTT cues. | |
| 21 class WebVTTParser { | |
|
acolwell GONE FROM CHROMIUM
2013/05/10 02:22:08
This should be in its own file.
Matthew Heaney (Chromium)
2013/05/10 05:21:08
Done.
| |
| 22 public: | |
| 23 // The payload is the embedded WebVTT cue, stored in a WebM block. | |
| 24 // The parser treats this as a UTF-8 byte stream. | |
| 25 WebVTTParser(const uint8* payload, int payload_size); | |
| 26 | |
| 27 // Parse the cue identifier, settings, and content from the stream. | |
| 28 void operator()(std::string* id, std::string* settings, std::string* content); | |
| 29 private: | |
| 30 // Remove a byte from the stream, advancing the stream pointer. | |
| 31 // Returns true if a character was returned; false means "end of stream". | |
| 32 bool GetChar(char* c); | |
| 33 | |
| 34 // Backup the stream pointer. | |
| 35 void UngetChar(); | |
| 36 | |
| 37 // Parse a line of text from the stream. | |
| 38 void ParseLine(std::string* line); | |
| 39 | |
| 40 // Represents the portion of the stream that has not been consumed yet. | |
| 41 const uint8* ptr_; | |
| 42 const uint8* const ptr_end_; | |
| 43 | |
| 44 DISALLOW_COPY_AND_ASSIGN(WebVTTParser); | |
| 45 }; | |
| 46 | |
| 47 WebVTTParser::WebVTTParser(const uint8* payload, int payload_size) | |
| 48 : ptr_(payload), | |
| 49 ptr_end_(payload + payload_size) { | |
| 50 } | |
| 51 | |
| 52 void WebVTTParser::operator()(std::string* id, | |
| 53 std::string* settings, | |
| 54 std::string* content) { | |
| 55 ParseLine(id); | |
| 56 ParseLine(settings); | |
| 57 content->assign(ptr_, ptr_end_); | |
| 58 } | |
| 59 | |
| 60 bool WebVTTParser::GetChar(char* c) { | |
| 61 if (ptr_ >= ptr_end_) | |
| 62 return false; // EOF | |
| 63 | |
| 64 *c = *ptr_++; | |
| 65 return true; | |
| 66 } | |
| 67 | |
| 68 void WebVTTParser::UngetChar() { | |
| 69 --ptr_; | |
| 70 } | |
| 71 | |
| 72 void WebVTTParser::ParseLine(std::string* line_ptr) { | |
| 73 std::string& line = *line_ptr; | |
| 74 line.clear(); | |
| 75 | |
| 76 // Consume characters from the stream, until we reach end-of-line. | |
| 77 | |
| 78 // The WebVTT spec states that lines may be terminated in any of the following | |
| 79 // three ways: | |
| 80 // LF | |
| 81 // CR | |
| 82 // CR LF | |
| 83 | |
| 84 // The spec is here: | |
| 85 // http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm | |
| 86 | |
| 87 enum { | |
| 88 kLF = '\x0A', | |
| 89 kCR = '\x0D' | |
| 90 }; | |
| 91 | |
| 92 for (;;) { | |
| 93 char c; | |
| 94 | |
| 95 if (!GetChar(&c) || c == kLF) | |
| 96 return; | |
| 97 | |
| 98 if (c == kCR) { | |
| 99 if (GetChar(&c) && c != kLF) | |
| 100 UngetChar(); | |
| 101 | |
| 102 return; | |
| 103 } | |
| 104 | |
| 105 line.push_back(c); | |
| 106 } | |
| 107 } | |
| 108 | |
| 20 WebMStreamParser::WebMStreamParser() | 109 WebMStreamParser::WebMStreamParser() |
| 21 : state_(kWaitingForInit), | 110 : state_(kWaitingForInit), |
| 22 waiting_for_buffers_(false) { | 111 waiting_for_buffers_(false) { |
| 23 } | 112 } |
| 24 | 113 |
| 25 WebMStreamParser::~WebMStreamParser() {} | 114 WebMStreamParser::~WebMStreamParser() {} |
| 26 | 115 |
| 27 void WebMStreamParser::Init(const InitCB& init_cb, | 116 void WebMStreamParser::Init(const InitCB& init_cb, |
| 28 const NewConfigCB& config_cb, | 117 const NewConfigCB& config_cb, |
| 29 const NewBuffersCB& audio_cb, | 118 const NewBuffersCB& audio_cb, |
| 30 const NewBuffersCB& video_cb, | 119 const NewBuffersCB& video_cb, |
| 31 const NeedKeyCB& need_key_cb, | 120 const NeedKeyCB& need_key_cb, |
| 121 const AddTextTrackCB& add_text_track_cb, | |
| 32 const NewMediaSegmentCB& new_segment_cb, | 122 const NewMediaSegmentCB& new_segment_cb, |
| 33 const base::Closure& end_of_segment_cb, | 123 const base::Closure& end_of_segment_cb, |
| 34 const LogCB& log_cb) { | 124 const LogCB& log_cb) { |
| 35 DCHECK_EQ(state_, kWaitingForInit); | 125 DCHECK_EQ(state_, kWaitingForInit); |
| 36 DCHECK(init_cb_.is_null()); | 126 DCHECK(init_cb_.is_null()); |
| 37 DCHECK(!init_cb.is_null()); | 127 DCHECK(!init_cb.is_null()); |
| 38 DCHECK(!config_cb.is_null()); | 128 DCHECK(!config_cb.is_null()); |
| 39 DCHECK(!audio_cb.is_null() || !video_cb.is_null()); | 129 DCHECK(!audio_cb.is_null() || !video_cb.is_null()); |
| 40 DCHECK(!need_key_cb.is_null()); | 130 DCHECK(!need_key_cb.is_null()); |
| 41 DCHECK(!new_segment_cb.is_null()); | 131 DCHECK(!new_segment_cb.is_null()); |
| 42 DCHECK(!end_of_segment_cb.is_null()); | 132 DCHECK(!end_of_segment_cb.is_null()); |
| 43 | 133 |
| 44 ChangeState(kParsingHeaders); | 134 ChangeState(kParsingHeaders); |
| 45 init_cb_ = init_cb; | 135 init_cb_ = init_cb; |
| 46 config_cb_ = config_cb; | 136 config_cb_ = config_cb; |
| 47 audio_cb_ = audio_cb; | 137 audio_cb_ = audio_cb; |
| 48 video_cb_ = video_cb; | 138 video_cb_ = video_cb; |
| 49 need_key_cb_ = need_key_cb; | 139 need_key_cb_ = need_key_cb; |
| 140 add_text_track_cb_ = add_text_track_cb; | |
| 50 new_segment_cb_ = new_segment_cb; | 141 new_segment_cb_ = new_segment_cb; |
| 51 end_of_segment_cb_ = end_of_segment_cb; | 142 end_of_segment_cb_ = end_of_segment_cb; |
| 52 log_cb_ = log_cb; | 143 log_cb_ = log_cb; |
| 53 } | 144 } |
| 54 | 145 |
| 55 void WebMStreamParser::Flush() { | 146 void WebMStreamParser::Flush() { |
| 56 DCHECK_NE(state_, kWaitingForInit); | 147 DCHECK_NE(state_, kWaitingForInit); |
| 57 | 148 |
| 58 byte_queue_.Reset(); | 149 byte_queue_.Reset(); |
| 59 | 150 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 | 282 |
| 192 const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config(); | 283 const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config(); |
| 193 if (video_config.is_encrypted()) | 284 if (video_config.is_encrypted()) |
| 194 FireNeedKey(tracks_parser.video_encryption_key_id()); | 285 FireNeedKey(tracks_parser.video_encryption_key_id()); |
| 195 | 286 |
| 196 if (!config_cb_.Run(audio_config, video_config)) { | 287 if (!config_cb_.Run(audio_config, video_config)) { |
| 197 DVLOG(1) << "New config data isn't allowed."; | 288 DVLOG(1) << "New config data isn't allowed."; |
| 198 return -1; | 289 return -1; |
| 199 } | 290 } |
| 200 | 291 |
| 292 typedef std::set<int> TextTracksSet; | |
| 293 const TextTracksSet& text_tracks = tracks_parser.text_tracks(); | |
| 294 | |
| 295 for (TextTracksSet::const_iterator itr = text_tracks.begin(); | |
| 296 itr != text_tracks.end(); ++itr) { | |
| 297 // TODO(matthewjheaney): parse track kind, name, lang | |
| 298 // Must also resolve ptr type | |
| 299 TextTrack* const text_cb = add_text_track_cb_.Run(kTextSubtitles, "", ""); | |
| 300 | |
| 301 // TODO(matthewjheaney): we need to explicitly deallocate text_cb, | |
| 302 // unless a scoped_ptr or somesuch is used. | |
| 303 text_track_map_.insert(std::make_pair(*itr, text_cb)); | |
| 304 } | |
| 305 | |
| 201 cluster_parser_.reset(new WebMClusterParser( | 306 cluster_parser_.reset(new WebMClusterParser( |
| 202 info_parser.timecode_scale(), | 307 info_parser.timecode_scale(), |
| 203 tracks_parser.audio_track_num(), | 308 tracks_parser.audio_track_num(), |
| 204 tracks_parser.video_track_num(), | 309 tracks_parser.video_track_num(), |
| 205 tracks_parser.text_tracks(), | 310 tracks_parser.text_tracks(), |
| 206 tracks_parser.ignored_tracks(), | 311 tracks_parser.ignored_tracks(), |
| 207 tracks_parser.audio_encryption_key_id(), | 312 tracks_parser.audio_encryption_key_id(), |
| 208 tracks_parser.video_encryption_key_id(), | 313 tracks_parser.video_encryption_key_id(), |
| 209 log_cb_)); | 314 log_cb_)); |
| 210 | 315 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 new_segment_cb_.Run(cluster_start_time); | 366 new_segment_cb_.Run(cluster_start_time); |
| 262 waiting_for_buffers_ = false; | 367 waiting_for_buffers_ = false; |
| 263 } | 368 } |
| 264 | 369 |
| 265 if (!audio_buffers.empty() && !audio_cb_.Run(audio_buffers)) | 370 if (!audio_buffers.empty() && !audio_cb_.Run(audio_buffers)) |
| 266 return -1; | 371 return -1; |
| 267 | 372 |
| 268 if (!video_buffers.empty() && !video_cb_.Run(video_buffers)) | 373 if (!video_buffers.empty() && !video_cb_.Run(video_buffers)) |
| 269 return -1; | 374 return -1; |
| 270 | 375 |
| 376 WebMClusterParser::TextTrackIterator tt_iter = | |
| 377 cluster_parser_->CreateTextTrackIterator(); | |
| 378 | |
| 379 int tt_num; | |
| 380 const BufferQueue* tt_buffers; | |
| 381 | |
| 382 while (tt_iter(&tt_num, &tt_buffers)) { | |
| 383 TextTrackMap::iterator cb_iter = text_track_map_.find(tt_num); | |
| 384 | |
| 385 if (cb_iter == text_track_map_.end()) | |
| 386 continue; | |
| 387 | |
| 388 TextTrack* const text_cb = cb_iter->second; | |
|
acolwell GONE FROM CHROMIUM
2013/05/10 02:22:08
nit: Just inline this in the call below.
Matthew Heaney (Chromium)
2013/05/10 05:21:08
Done.
Matthew Heaney (Chromium)
2013/05/10 05:21:08
Done.
| |
| 389 OnTextBuffers(*tt_buffers, text_cb); | |
| 390 } | |
| 391 | |
| 271 if (cluster_ended) | 392 if (cluster_ended) |
| 272 end_of_segment_cb_.Run(); | 393 end_of_segment_cb_.Run(); |
| 273 | 394 |
| 274 return bytes_parsed; | 395 return bytes_parsed; |
| 275 } | 396 } |
| 276 | 397 |
| 277 void WebMStreamParser::FireNeedKey(const std::string& key_id) { | 398 void WebMStreamParser::FireNeedKey(const std::string& key_id) { |
| 278 int key_id_size = key_id.size(); | 399 int key_id_size = key_id.size(); |
| 279 DCHECK_GT(key_id_size, 0); | 400 DCHECK_GT(key_id_size, 0); |
| 280 scoped_ptr<uint8[]> key_id_array(new uint8[key_id_size]); | 401 scoped_ptr<uint8[]> key_id_array(new uint8[key_id_size]); |
| 281 memcpy(key_id_array.get(), key_id.data(), key_id_size); | 402 memcpy(key_id_array.get(), key_id.data(), key_id_size); |
| 282 need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size); | 403 need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size); |
| 283 } | 404 } |
| 284 | 405 |
| 406 void WebMStreamParser::OnTextBuffers(const StreamParser::BufferQueue& buffers, | |
| 407 TextTrack* text_cb) { | |
| 408 // TODO(matthewjheaney): where is this done? | |
| 409 //AdjustBufferTimestamps(buffers); | |
|
acolwell GONE FROM CHROMIUM
2013/05/10 02:22:08
This happens in SourceState::OnBuffers so you this
Matthew Heaney (Chromium)
2013/05/10 05:21:08
Done.
| |
| 410 | |
| 411 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | |
| 412 itr != buffers.end(); ++itr) { | |
| 413 OnTextBuffer(*itr, text_cb); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 void WebMStreamParser::OnTextBuffer(const StreamParserBuffer* buffer, | |
| 418 TextTrack* text_cb) { | |
| 419 const base::TimeDelta start = buffer->GetTimestamp(); | |
|
acolwell GONE FROM CHROMIUM
2013/05/10 02:22:08
nit: Since most of these locals appear to only be
Matthew Heaney (Chromium)
2013/05/10 05:21:08
This function was cleaned up a bit because the par
| |
| 420 const base::TimeDelta end = start + buffer->GetDuration(); | |
| 421 | |
| 422 const uint8* const payload = buffer->GetData(); | |
| 423 const int payload_size = buffer->GetDataSize(); | |
| 424 | |
| 425 WebVTTParser parser(payload, payload_size); | |
| 426 std::string id, settings, content; | |
| 427 | |
| 428 parser(&id, &settings, &content); | |
|
acolwell GONE FROM CHROMIUM
2013/05/10 02:22:08
Seeing as it looks like you could rearrange this c
Matthew Heaney (Chromium)
2013/05/10 05:21:08
I moved the webvtt parser to its own class, and ad
| |
| 429 | |
| 430 text_cb->addWebVTTCue(start, end, id, content, settings); | |
| 431 } | |
| 432 | |
| 285 } // namespace media | 433 } // namespace media |
| OLD | NEW |