Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: media/formats/webm/webm_cluster_parser.cc

Issue 213253006: MSE: Populate WebM missing duration with DefaultDuration, derived, or default (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments and rebased (GenerateSpliceFrame() is now disabled in WMPI/WMPA in ToT) Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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/formats/webm/webm_cluster_parser.h" 5 #include "media/formats/webm/webm_cluster_parser.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/sys_byteorder.h" 10 #include "base/sys_byteorder.h"
11 #include "media/base/buffers.h" 11 #include "media/base/buffers.h"
12 #include "media/base/decrypt_config.h" 12 #include "media/base/decrypt_config.h"
13 #include "media/filters/webvtt_util.h" 13 #include "media/filters/webvtt_util.h"
14 #include "media/formats/webm/webm_constants.h" 14 #include "media/formats/webm/webm_constants.h"
15 #include "media/formats/webm/webm_crypto_helpers.h" 15 #include "media/formats/webm/webm_crypto_helpers.h"
16 #include "media/formats/webm/webm_webvtt_parser.h" 16 #include "media/formats/webm/webm_webvtt_parser.h"
17 17
18 // Arbitrarily-chosen numbers to estimate the duration of a buffer if none is
19 // set and there is not enough information to get a better estimate.
20 // TODO(wolenetz/acolwell): Parse audio codebook to determine missing audio
21 // frame durations. See http://crbug.com/351166.
22 static int kDefaultAudioBufferDurationInMs = 23; // Common 1k samples @44.1kHz
23 static int kDefaultVideoBufferDurationInMs = 50; // Larger may reduce stalls
acolwell GONE FROM CHROMIUM 2014/03/27 18:28:10 nit: This implies 20fps which is pretty uncommon.
wolenetz 2014/03/27 19:56:39 Ok. I'll use the longer of these (40) to help prev
24
18 namespace media { 25 namespace media {
19 26
20 WebMClusterParser::WebMClusterParser( 27 WebMClusterParser::WebMClusterParser(
21 int64 timecode_scale, int audio_track_num, int video_track_num, 28 int64 timecode_scale,
29 int audio_track_num,
30 base::TimeDelta audio_default_duration,
31 int video_track_num,
32 base::TimeDelta video_default_duration,
22 const WebMTracksParser::TextTracks& text_tracks, 33 const WebMTracksParser::TextTracks& text_tracks,
23 const std::set<int64>& ignored_tracks, 34 const std::set<int64>& ignored_tracks,
24 const std::string& audio_encryption_key_id, 35 const std::string& audio_encryption_key_id,
25 const std::string& video_encryption_key_id, 36 const std::string& video_encryption_key_id,
26 const LogCB& log_cb) 37 const LogCB& log_cb)
27 : timecode_multiplier_(timecode_scale / 1000.0), 38 : timecode_multiplier_(timecode_scale / 1000.0),
28 ignored_tracks_(ignored_tracks), 39 ignored_tracks_(ignored_tracks),
29 audio_encryption_key_id_(audio_encryption_key_id), 40 audio_encryption_key_id_(audio_encryption_key_id),
30 video_encryption_key_id_(video_encryption_key_id), 41 video_encryption_key_id_(video_encryption_key_id),
31 parser_(kWebMIdCluster, this), 42 parser_(kWebMIdCluster, this),
32 last_block_timecode_(-1), 43 last_block_timecode_(-1),
33 block_data_size_(-1), 44 block_data_size_(-1),
34 block_duration_(-1), 45 block_duration_(-1),
35 block_add_id_(-1), 46 block_add_id_(-1),
36 block_additional_data_size_(-1), 47 block_additional_data_size_(-1),
37 discard_padding_(-1), 48 discard_padding_(-1),
38 cluster_timecode_(-1), 49 cluster_timecode_(-1),
39 cluster_start_time_(kNoTimestamp()), 50 cluster_start_time_(kNoTimestamp()),
40 cluster_ended_(false), 51 cluster_ended_(false),
41 audio_(audio_track_num, false), 52 audio_(audio_track_num, false, audio_default_duration),
42 video_(video_track_num, true), 53 video_(video_track_num, true, video_default_duration),
43 log_cb_(log_cb) { 54 log_cb_(log_cb) {
44 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); 55 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
45 it != text_tracks.end(); 56 it != text_tracks.end();
46 ++it) { 57 ++it) {
47 text_track_map_.insert(std::make_pair(it->first, Track(it->first, false))); 58 text_track_map_.insert(std::make_pair(
59 it->first, Track(it->first, false, kNoTimestamp())));
48 } 60 }
49 } 61 }
50 62
51 WebMClusterParser::~WebMClusterParser() {} 63 WebMClusterParser::~WebMClusterParser() {}
52 64
53 void WebMClusterParser::Reset() { 65 void WebMClusterParser::Reset() {
54 last_block_timecode_ = -1; 66 last_block_timecode_ = -1;
55 cluster_timecode_ = -1; 67 cluster_timecode_ = -1;
56 cluster_start_time_ = kNoTimestamp(); 68 cluster_start_time_ = kNoTimestamp();
57 cluster_ended_ = false; 69 cluster_ended_ = false;
58 parser_.Reset(); 70 parser_.Reset();
59 audio_.Reset(); 71 audio_.Reset();
60 video_.Reset(); 72 video_.Reset();
61 ResetTextTracks(); 73 ResetTextTracks();
62 } 74 }
63 75
64 int WebMClusterParser::Parse(const uint8* buf, int size) { 76 int WebMClusterParser::Parse(const uint8* buf, int size) {
65 audio_.Reset(); 77 audio_.ClearBuffersButKeepLastIfMissingDuration();
66 video_.Reset(); 78 video_.ClearBuffersButKeepLastIfMissingDuration();
67 ResetTextTracks(); 79 ResetTextTracks();
68 80
69 int result = parser_.Parse(buf, size); 81 int result = parser_.Parse(buf, size);
70 82
71 if (result < 0) { 83 if (result < 0) {
72 cluster_ended_ = false; 84 cluster_ended_ = false;
73 return result; 85 return result;
74 } 86 }
75 87
76 cluster_ended_ = parser_.IsParsingComplete(); 88 cluster_ended_ = parser_.IsParsingComplete();
(...skipping 15 matching lines...) Expand all
92 // call. 104 // call.
93 parser_.Reset(); 105 parser_.Reset();
94 106
95 last_block_timecode_ = -1; 107 last_block_timecode_ = -1;
96 cluster_timecode_ = -1; 108 cluster_timecode_ = -1;
97 } 109 }
98 110
99 return result; 111 return result;
100 } 112 }
101 113
114 const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() {
115 if (cluster_ended_)
116 audio_.ApplyDurationDefaultOrEstimateIfNeeded();
117 return audio_.buffers();
118 }
119
120 const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() {
121 if (cluster_ended_)
122 video_.ApplyDurationDefaultOrEstimateIfNeeded();
123 return video_.buffers();
124 }
125
102 const WebMClusterParser::TextBufferQueueMap& 126 const WebMClusterParser::TextBufferQueueMap&
103 WebMClusterParser::GetTextBuffers() { 127 WebMClusterParser::GetTextBuffers() {
104 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in 128 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in
105 // the output only for non-empty text buffer queues in |text_track_map_|. 129 // the output only for non-empty text buffer queues in |text_track_map_|.
106 text_buffers_map_.clear(); 130 text_buffers_map_.clear();
107 for (TextTrackMap::const_iterator itr = text_track_map_.begin(); 131 for (TextTrackMap::const_iterator itr = text_track_map_.begin();
108 itr != text_track_map_.end(); 132 itr != text_track_map_.end();
109 ++itr) { 133 ++itr) {
134 // Per OnBlock(), all text buffers should already have valid durations, so
135 // there is no need to call
136 // itr->second.ApplyDurationDefaultOrEstimateIfNeeded() here.
110 const BufferQueue& text_buffers = itr->second.buffers(); 137 const BufferQueue& text_buffers = itr->second.buffers();
111 if (!text_buffers.empty()) 138 if (!text_buffers.empty())
112 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); 139 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers));
113 } 140 }
114 141
115 return text_buffers_map_; 142 return text_buffers_map_;
116 } 143 }
117 144
118 WebMParserClient* WebMClusterParser::OnListStart(int id) { 145 WebMParserClient* WebMClusterParser::OnListStart(int id) {
119 if (id == kWebMIdCluster) { 146 if (id == kWebMIdCluster) {
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 } 410 }
384 411
385 if (discard_padding != 0) { 412 if (discard_padding != 0) {
386 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds( 413 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
387 discard_padding / 1000)); 414 discard_padding / 1000));
388 } 415 }
389 416
390 return track->AddBuffer(buffer); 417 return track->AddBuffer(buffer);
391 } 418 }
392 419
393 WebMClusterParser::Track::Track(int track_num, bool is_video) 420 WebMClusterParser::Track::Track(int track_num, bool is_video,
421 base::TimeDelta default_duration)
394 : track_num_(track_num), 422 : track_num_(track_num),
395 is_video_(is_video) { 423 is_video_(is_video),
424 default_duration_(default_duration),
425 estimated_next_frame_duration_(kNoTimestamp()) {
426 DCHECK(default_duration_ == kNoTimestamp() ||
427 default_duration_ > base::TimeDelta());
396 } 428 }
397 429
398 WebMClusterParser::Track::~Track() {} 430 WebMClusterParser::Track::~Track() {}
399 431
400 bool WebMClusterParser::Track::AddBuffer( 432 bool WebMClusterParser::Track::AddBuffer(
401 const scoped_refptr<StreamParserBuffer>& buffer) { 433 const scoped_refptr<StreamParserBuffer>& buffer) {
402 DVLOG(2) << "AddBuffer() : " << track_num_ 434 DVLOG(2) << "AddBuffer() : " << track_num_
403 << " ts " << buffer->timestamp().InSecondsF() 435 << " ts " << buffer->timestamp().InSecondsF()
404 << " dur " << buffer->duration().InSecondsF() 436 << " dur " << buffer->duration().InSecondsF()
405 << " kf " << buffer->IsKeyframe() 437 << " kf " << buffer->IsKeyframe()
406 << " size " << buffer->data_size(); 438 << " size " << buffer->data_size();
407 439
408 buffers_.push_back(buffer); 440 if (last_added_buffer_missing_duration_) {
409 return true; 441 base::TimeDelta derived_duration =
442 buffer->timestamp() - last_added_buffer_missing_duration_->timestamp();
443 last_added_buffer_missing_duration_->set_duration(derived_duration);
444
445 DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : "
446 << " ts "
447 << last_added_buffer_missing_duration_->timestamp().InSecondsF()
448 << " dur "
449 << last_added_buffer_missing_duration_->duration().InSecondsF()
450 << " kf " << last_added_buffer_missing_duration_->IsKeyframe()
451 << " size " << last_added_buffer_missing_duration_->data_size();
452 scoped_refptr<StreamParserBuffer> updated_buffer =
453 last_added_buffer_missing_duration_;
454 last_added_buffer_missing_duration_ = NULL;
455 if (!QueueBuffer(updated_buffer))
456 return false;
457 }
458
459 if (buffer->duration() == kNoTimestamp()) {
460 last_added_buffer_missing_duration_ = buffer;
461 DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration";
462 return true;
463 }
464
465 return QueueBuffer(buffer);
466 }
467
468 void WebMClusterParser::Track::ApplyDurationDefaultOrEstimateIfNeeded() {
469 if (!last_added_buffer_missing_duration_)
470 return;
471
472 if (default_duration_ != kNoTimestamp()) {
acolwell GONE FROM CHROMIUM 2014/03/27 18:28:10 nit: Move if chain to a helper function so we only
wolenetz 2014/03/27 19:56:39 Done (with a helper function).
473 DVLOG(3) << __FUNCTION__ << " : using TrackEntry DefaultDuration";
474 last_added_buffer_missing_duration_->set_duration(default_duration_);
475 } else if (estimated_next_frame_duration_ != kNoTimestamp()) {
476 DVLOG(3) << __FUNCTION__ << " : using estimated duration";
477 last_added_buffer_missing_duration_->set_duration(
478 estimated_next_frame_duration_);
479 } else {
480 DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration";
481 if (is_video_) {
482 last_added_buffer_missing_duration_->set_duration(
483 base::TimeDelta::FromMilliseconds(kDefaultVideoBufferDurationInMs));
484 } else {
485 last_added_buffer_missing_duration_->set_duration(
486 base::TimeDelta::FromMilliseconds(kDefaultAudioBufferDurationInMs));
487 }
488 }
489
490 DCHECK(last_added_buffer_missing_duration_->duration() > base::TimeDelta());
491 DCHECK(last_added_buffer_missing_duration_->duration() != kNoTimestamp());
492
493 DVLOG(2) << "ApplyDurationDefaultOrEstimateIfNeeded() : new dur : "
494 << " ts "
495 << last_added_buffer_missing_duration_->timestamp().InSecondsF()
496 << " dur "
497 << last_added_buffer_missing_duration_->duration().InSecondsF()
498 << " kf " << last_added_buffer_missing_duration_->IsKeyframe()
499 << " size " << last_added_buffer_missing_duration_->data_size();
500 // Don't use the applied duration as a future estimation (don't use
501 // QueueBuffer() here.)
502 buffers_.push_back(last_added_buffer_missing_duration_);
503 last_added_buffer_missing_duration_ = NULL;
504 }
505
506 void WebMClusterParser::Track::ClearBuffersButKeepLastIfMissingDuration() {
507 // Note that |estimated_next_frame_duration_| is not reset, so it can be
508 // reused on subsequent buffers added to this instance.
509 buffers_.clear();
410 } 510 }
411 511
412 void WebMClusterParser::Track::Reset() { 512 void WebMClusterParser::Track::Reset() {
413 buffers_.clear(); 513 ClearBuffersButKeepLastIfMissingDuration();
514 last_added_buffer_missing_duration_ = NULL;
414 } 515 }
415 516
416 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { 517 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
417 // For now, assume that all blocks are keyframes for datatypes other than 518 // For now, assume that all blocks are keyframes for datatypes other than
418 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. 519 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
419 if (!is_video_) 520 if (!is_video_)
420 return true; 521 return true;
421 522
422 // Make sure the block is big enough for the minimal keyframe header size. 523 // Make sure the block is big enough for the minimal keyframe header size.
423 if (size < 7) 524 if (size < 7)
424 return false; 525 return false;
425 526
426 // The LSb of the first byte must be a 0 for a keyframe. 527 // The LSb of the first byte must be a 0 for a keyframe.
427 // http://tools.ietf.org/html/rfc6386 Section 19.1 528 // http://tools.ietf.org/html/rfc6386 Section 19.1
428 if ((data[0] & 0x01) != 0) 529 if ((data[0] & 0x01) != 0)
429 return false; 530 return false;
430 531
431 // Verify VP8 keyframe startcode. 532 // Verify VP8 keyframe startcode.
432 // http://tools.ietf.org/html/rfc6386 Section 19.1 533 // http://tools.ietf.org/html/rfc6386 Section 19.1
433 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) 534 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
434 return false; 535 return false;
435 536
436 return true; 537 return true;
437 } 538 }
438 539
540 bool WebMClusterParser::Track::QueueBuffer(
541 const scoped_refptr<StreamParserBuffer>& buffer) {
542 DCHECK(!last_added_buffer_missing_duration_);
543 base::TimeDelta duration = buffer->duration();
544 if (duration < base::TimeDelta() || duration == kNoTimestamp()) {
545 DVLOG(2) << "QueueBuffer() : Invalid buffer duration: "
546 << duration.InSecondsF();
547 return false;
548 }
549
550 estimated_next_frame_duration_ = std::max(duration,
551 estimated_next_frame_duration_);
552 buffers_.push_back(buffer);
553 return true;
554 }
555
439 void WebMClusterParser::ResetTextTracks() { 556 void WebMClusterParser::ResetTextTracks() {
440 text_buffers_map_.clear(); 557 text_buffers_map_.clear();
441 for (TextTrackMap::iterator it = text_track_map_.begin(); 558 for (TextTrackMap::iterator it = text_track_map_.begin();
442 it != text_track_map_.end(); 559 it != text_track_map_.end();
443 ++it) { 560 ++it) {
444 it->second.Reset(); 561 it->second.Reset();
445 } 562 }
446 } 563 }
447 564
448 WebMClusterParser::Track* 565 WebMClusterParser::Track*
449 WebMClusterParser::FindTextTrack(int track_num) { 566 WebMClusterParser::FindTextTrack(int track_num) {
450 const TextTrackMap::iterator it = text_track_map_.find(track_num); 567 const TextTrackMap::iterator it = text_track_map_.find(track_num);
451 568
452 if (it == text_track_map_.end()) 569 if (it == text_track_map_.end())
453 return NULL; 570 return NULL;
454 571
455 return &it->second; 572 return &it->second;
456 } 573 }
457 574
458 } // namespace media 575 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698