media-internals: Differentiate error, info, and debug log messages
[chromium-blink-merge.git] / media / formats / webm / webm_cluster_parser.cc
blob6f198ec66dc669d6b6876a12319a7a6e14b79f64
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_cluster_parser.h"
7 #include <vector>
9 #include "base/logging.h"
10 #include "base/sys_byteorder.h"
11 #include "media/base/buffers.h"
12 #include "media/base/decrypt_config.h"
13 #include "media/filters/webvtt_util.h"
14 #include "media/formats/webm/webm_constants.h"
15 #include "media/formats/webm/webm_crypto_helpers.h"
16 #include "media/formats/webm/webm_webvtt_parser.h"
18 namespace media {
20 const uint16_t WebMClusterParser::kOpusFrameDurationsMu[] = {
21 10000, 20000, 40000, 60000, 10000, 20000, 40000, 60000, 10000, 20000, 40000,
22 60000, 10000, 20000, 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000,
23 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, 10000, 20000};
25 enum {
26 // Limits the number of MEDIA_LOG() calls in the path of reading encoded
27 // duration to avoid spamming for corrupted data.
28 kMaxDurationLogs = 10,
31 WebMClusterParser::WebMClusterParser(
32 int64 timecode_scale,
33 int audio_track_num,
34 base::TimeDelta audio_default_duration,
35 int video_track_num,
36 base::TimeDelta video_default_duration,
37 const WebMTracksParser::TextTracks& text_tracks,
38 const std::set<int64>& ignored_tracks,
39 const std::string& audio_encryption_key_id,
40 const std::string& video_encryption_key_id,
41 const AudioCodec audio_codec,
42 const LogCB& log_cb)
43 : num_duration_errors_(0),
44 timecode_multiplier_(timecode_scale / 1000.0),
45 ignored_tracks_(ignored_tracks),
46 audio_encryption_key_id_(audio_encryption_key_id),
47 video_encryption_key_id_(video_encryption_key_id),
48 audio_codec_(audio_codec),
49 parser_(kWebMIdCluster, this),
50 last_block_timecode_(-1),
51 block_data_size_(-1),
52 block_duration_(-1),
53 block_add_id_(-1),
54 block_additional_data_size_(0),
55 discard_padding_(-1),
56 cluster_timecode_(-1),
57 cluster_start_time_(kNoTimestamp()),
58 cluster_ended_(false),
59 audio_(audio_track_num, false, audio_default_duration, log_cb),
60 video_(video_track_num, true, video_default_duration, log_cb),
61 ready_buffer_upper_bound_(kNoDecodeTimestamp()),
62 log_cb_(log_cb) {
63 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
64 it != text_tracks.end();
65 ++it) {
66 text_track_map_.insert(std::make_pair(
67 it->first, Track(it->first, false, kNoTimestamp(), log_cb_)));
71 WebMClusterParser::~WebMClusterParser() {}
73 void WebMClusterParser::Reset() {
74 last_block_timecode_ = -1;
75 cluster_timecode_ = -1;
76 cluster_start_time_ = kNoTimestamp();
77 cluster_ended_ = false;
78 parser_.Reset();
79 audio_.Reset();
80 video_.Reset();
81 ResetTextTracks();
82 ready_buffer_upper_bound_ = kNoDecodeTimestamp();
85 int WebMClusterParser::Parse(const uint8_t* buf, int size) {
86 audio_.ClearReadyBuffers();
87 video_.ClearReadyBuffers();
88 ClearTextTrackReadyBuffers();
89 ready_buffer_upper_bound_ = kNoDecodeTimestamp();
91 int result = parser_.Parse(buf, size);
93 if (result < 0) {
94 cluster_ended_ = false;
95 return result;
98 cluster_ended_ = parser_.IsParsingComplete();
99 if (cluster_ended_) {
100 // If there were no buffers in this cluster, set the cluster start time to
101 // be the |cluster_timecode_|.
102 if (cluster_start_time_ == kNoTimestamp()) {
103 // If the cluster did not even have a |cluster_timecode_|, signal parse
104 // error.
105 if (cluster_timecode_ < 0)
106 return -1;
108 cluster_start_time_ = base::TimeDelta::FromMicroseconds(
109 cluster_timecode_ * timecode_multiplier_);
112 // Reset the parser if we're done parsing so that
113 // it is ready to accept another cluster on the next
114 // call.
115 parser_.Reset();
117 last_block_timecode_ = -1;
118 cluster_timecode_ = -1;
121 return result;
124 const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() {
125 if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
126 UpdateReadyBuffers();
128 return audio_.ready_buffers();
131 const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() {
132 if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
133 UpdateReadyBuffers();
135 return video_.ready_buffers();
138 const WebMClusterParser::TextBufferQueueMap&
139 WebMClusterParser::GetTextBuffers() {
140 if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
141 UpdateReadyBuffers();
143 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in
144 // the output only for non-empty ready_buffer() queues in |text_track_map_|.
145 text_buffers_map_.clear();
146 for (TextTrackMap::const_iterator itr = text_track_map_.begin();
147 itr != text_track_map_.end();
148 ++itr) {
149 const BufferQueue& text_buffers = itr->second.ready_buffers();
150 if (!text_buffers.empty())
151 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers));
154 return text_buffers_map_;
157 base::TimeDelta WebMClusterParser::TryGetEncodedAudioDuration(
158 const uint8_t* data,
159 int size) {
161 // Duration is currently read assuming the *entire* stream is unencrypted.
162 // The special "Signal Byte" prepended to Blocks in encrypted streams is
163 // assumed to not be present.
164 // TODO(chcunningham): Consider parsing "Signal Byte" for encrypted streams
165 // to return duration for any unencrypted blocks.
167 if (audio_codec_ == kCodecOpus) {
168 return ReadOpusDuration(data, size);
171 // TODO(wolenetz/chcunningham): Implement duration reading for Vorbis. See
172 // motivations in http://crbug.com/396634.
174 return kNoTimestamp();
177 base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
178 int size) {
179 // Masks and constants for Opus packets. See
180 // https://tools.ietf.org/html/rfc6716#page-14
181 static const uint8_t kTocConfigMask = 0xf8;
182 static const uint8_t kTocFrameCountCodeMask = 0x03;
183 static const uint8_t kFrameCountMask = 0x3f;
184 static const base::TimeDelta kPacketDurationMax =
185 base::TimeDelta::FromMilliseconds(120);
187 if (size < 1) {
188 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_, kMaxDurationLogs)
189 << "Invalid zero-byte Opus packet; demuxed block duration may be "
190 "imprecise.";
191 return kNoTimestamp();
194 // Frame count type described by last 2 bits of Opus TOC byte.
195 int frame_count_type = data[0] & kTocFrameCountCodeMask;
197 int frame_count = 0;
198 switch (frame_count_type) {
199 case 0:
200 frame_count = 1;
201 break;
202 case 1:
203 case 2:
204 frame_count = 2;
205 break;
206 case 3:
207 // Type 3 indicates an arbitrary frame count described in the next byte.
208 if (size < 2) {
209 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_,
210 kMaxDurationLogs)
211 << "Second byte missing from 'Code 3' Opus packet; demuxed block "
212 "duration may be imprecise.";
213 return kNoTimestamp();
216 frame_count = data[1] & kFrameCountMask;
218 if (frame_count == 0) {
219 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_,
220 kMaxDurationLogs)
221 << "Illegal 'Code 3' Opus packet with frame count zero; demuxed "
222 "block duration may be imprecise.";
223 return kNoTimestamp();
226 break;
227 default:
228 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_, kMaxDurationLogs)
229 << "Unexpected Opus frame count type: " << frame_count_type << "; "
230 << "demuxed block duration may be imprecise.";
231 return kNoTimestamp();
234 int opusConfig = (data[0] & kTocConfigMask) >> 3;
235 CHECK_GE(opusConfig, 0);
236 CHECK_LT(opusConfig, static_cast<int>(arraysize(kOpusFrameDurationsMu)));
238 DCHECK_GT(frame_count, 0);
239 base::TimeDelta duration = base::TimeDelta::FromMicroseconds(
240 kOpusFrameDurationsMu[opusConfig] * frame_count);
242 if (duration > kPacketDurationMax) {
243 // Intentionally allowing packet to pass through for now. Decoder should
244 // either handle or fail gracefully. MEDIA_LOG as breadcrumbs in case
245 // things go sideways.
246 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_, kMaxDurationLogs)
247 << "Warning, demuxed Opus packet with encoded duration: " << duration
248 << ". Should be no greater than " << kPacketDurationMax;
251 return duration;
254 WebMParserClient* WebMClusterParser::OnListStart(int id) {
255 if (id == kWebMIdCluster) {
256 cluster_timecode_ = -1;
257 cluster_start_time_ = kNoTimestamp();
258 } else if (id == kWebMIdBlockGroup) {
259 block_data_.reset();
260 block_data_size_ = -1;
261 block_duration_ = -1;
262 discard_padding_ = -1;
263 discard_padding_set_ = false;
264 } else if (id == kWebMIdBlockAdditions) {
265 block_add_id_ = -1;
266 block_additional_data_.reset();
267 block_additional_data_size_ = 0;
270 return this;
273 bool WebMClusterParser::OnListEnd(int id) {
274 if (id != kWebMIdBlockGroup)
275 return true;
277 // Make sure the BlockGroup actually had a Block.
278 if (block_data_size_ == -1) {
279 MEDIA_LOG(ERROR, log_cb_) << "Block missing from BlockGroup.";
280 return false;
283 bool result = ParseBlock(false, block_data_.get(), block_data_size_,
284 block_additional_data_.get(),
285 block_additional_data_size_, block_duration_,
286 discard_padding_set_ ? discard_padding_ : 0);
287 block_data_.reset();
288 block_data_size_ = -1;
289 block_duration_ = -1;
290 block_add_id_ = -1;
291 block_additional_data_.reset();
292 block_additional_data_size_ = 0;
293 discard_padding_ = -1;
294 discard_padding_set_ = false;
295 return result;
298 bool WebMClusterParser::OnUInt(int id, int64 val) {
299 int64* dst;
300 switch (id) {
301 case kWebMIdTimecode:
302 dst = &cluster_timecode_;
303 break;
304 case kWebMIdBlockDuration:
305 dst = &block_duration_;
306 break;
307 case kWebMIdBlockAddID:
308 dst = &block_add_id_;
309 break;
310 default:
311 return true;
313 if (*dst != -1)
314 return false;
315 *dst = val;
316 return true;
319 bool WebMClusterParser::ParseBlock(bool is_simple_block,
320 const uint8_t* buf,
321 int size,
322 const uint8_t* additional,
323 int additional_size,
324 int duration,
325 int64 discard_padding) {
326 if (size < 4)
327 return false;
329 // Return an error if the trackNum > 127. We just aren't
330 // going to support large track numbers right now.
331 if (!(buf[0] & 0x80)) {
332 MEDIA_LOG(ERROR, log_cb_) << "TrackNumber over 127 not supported";
333 return false;
336 int track_num = buf[0] & 0x7f;
337 int timecode = buf[1] << 8 | buf[2];
338 int flags = buf[3] & 0xff;
339 int lacing = (flags >> 1) & 0x3;
341 if (lacing) {
342 MEDIA_LOG(ERROR, log_cb_) << "Lacing " << lacing
343 << " is not supported yet.";
344 return false;
347 // Sign extend negative timecode offsets.
348 if (timecode & 0x8000)
349 timecode |= ~0xffff;
351 const uint8_t* frame_data = buf + 4;
352 int frame_size = size - (frame_data - buf);
353 return OnBlock(is_simple_block, track_num, timecode, duration, flags,
354 frame_data, frame_size, additional, additional_size,
355 discard_padding);
358 bool WebMClusterParser::OnBinary(int id, const uint8_t* data, int size) {
359 switch (id) {
360 case kWebMIdSimpleBlock:
361 return ParseBlock(true, data, size, NULL, 0, -1, 0);
363 case kWebMIdBlock:
364 if (block_data_) {
365 MEDIA_LOG(ERROR, log_cb_) << "More than 1 Block in a BlockGroup is not "
366 "supported.";
367 return false;
369 block_data_.reset(new uint8_t[size]);
370 memcpy(block_data_.get(), data, size);
371 block_data_size_ = size;
372 return true;
374 case kWebMIdBlockAdditional: {
375 uint64 block_add_id = base::HostToNet64(block_add_id_);
376 if (block_additional_data_) {
377 // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
378 // as per matroska spec. But for now we don't have a use case to
379 // support parsing of such files. Take a look at this again when such a
380 // case arises.
381 MEDIA_LOG(ERROR, log_cb_) << "More than 1 BlockAdditional in a "
382 "BlockGroup is not supported.";
383 return false;
385 // First 8 bytes of side_data in DecoderBuffer is the BlockAddID
386 // element's value in Big Endian format. This is done to mimic ffmpeg
387 // demuxer's behavior.
388 block_additional_data_size_ = size + sizeof(block_add_id);
389 block_additional_data_.reset(new uint8_t[block_additional_data_size_]);
390 memcpy(block_additional_data_.get(), &block_add_id,
391 sizeof(block_add_id));
392 memcpy(block_additional_data_.get() + 8, data, size);
393 return true;
395 case kWebMIdDiscardPadding: {
396 if (discard_padding_set_ || size <= 0 || size > 8)
397 return false;
398 discard_padding_set_ = true;
400 // Read in the big-endian integer.
401 discard_padding_ = static_cast<int8>(data[0]);
402 for (int i = 1; i < size; ++i)
403 discard_padding_ = (discard_padding_ << 8) | data[i];
405 return true;
407 default:
408 return true;
412 bool WebMClusterParser::OnBlock(bool is_simple_block,
413 int track_num,
414 int timecode,
415 int block_duration,
416 int flags,
417 const uint8_t* data,
418 int size,
419 const uint8_t* additional,
420 int additional_size,
421 int64 discard_padding) {
422 DCHECK_GE(size, 0);
423 if (cluster_timecode_ == -1) {
424 MEDIA_LOG(ERROR, log_cb_) << "Got a block before cluster timecode.";
425 return false;
428 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or
429 // only when the absolute timecode is negative? See http://crbug.com/271794
430 if (timecode < 0) {
431 MEDIA_LOG(ERROR, log_cb_) << "Got a block with negative timecode offset "
432 << timecode;
433 return false;
436 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
437 MEDIA_LOG(ERROR, log_cb_)
438 << "Got a block with a timecode before the previous block.";
439 return false;
442 Track* track = NULL;
443 StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO;
444 std::string encryption_key_id;
445 base::TimeDelta encoded_duration = kNoTimestamp();
446 if (track_num == audio_.track_num()) {
447 track = &audio_;
448 encryption_key_id = audio_encryption_key_id_;
449 if (encryption_key_id.empty()) {
450 encoded_duration = TryGetEncodedAudioDuration(data, size);
452 } else if (track_num == video_.track_num()) {
453 track = &video_;
454 encryption_key_id = video_encryption_key_id_;
455 buffer_type = DemuxerStream::VIDEO;
456 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
457 return true;
458 } else if (Track* const text_track = FindTextTrack(track_num)) {
459 if (is_simple_block) // BlockGroup is required for WebVTT cues
460 return false;
461 if (block_duration < 0) // not specified
462 return false;
463 track = text_track;
464 buffer_type = DemuxerStream::TEXT;
465 } else {
466 MEDIA_LOG(ERROR, log_cb_) << "Unexpected track number " << track_num;
467 return false;
470 last_block_timecode_ = timecode;
472 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
473 (cluster_timecode_ + timecode) * timecode_multiplier_);
475 scoped_refptr<StreamParserBuffer> buffer;
476 if (buffer_type != DemuxerStream::TEXT) {
477 // The first bit of the flags is set when a SimpleBlock contains only
478 // keyframes. If this is a Block, then inspection of the payload is
479 // necessary to determine whether it contains a keyframe or not.
480 // http://www.matroska.org/technical/specs/index.html
481 bool is_keyframe =
482 is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
484 // Every encrypted Block has a signal byte and IV prepended to it. Current
485 // encrypted WebM request for comments specification is here
486 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
487 scoped_ptr<DecryptConfig> decrypt_config;
488 int data_offset = 0;
489 if (!encryption_key_id.empty() &&
490 !WebMCreateDecryptConfig(
491 data, size,
492 reinterpret_cast<const uint8_t*>(encryption_key_id.data()),
493 encryption_key_id.size(),
494 &decrypt_config, &data_offset)) {
495 return false;
498 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
499 // type with remapped bytestream track numbers and allow multiple tracks as
500 // applicable. See https://crbug.com/341581.
501 buffer = StreamParserBuffer::CopyFrom(
502 data + data_offset, size - data_offset,
503 additional, additional_size,
504 is_keyframe, buffer_type, track_num);
506 if (decrypt_config)
507 buffer->set_decrypt_config(decrypt_config.Pass());
508 } else {
509 std::string id, settings, content;
510 WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
512 std::vector<uint8_t> side_data;
513 MakeSideData(id.begin(), id.end(),
514 settings.begin(), settings.end(),
515 &side_data);
517 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
518 // type with remapped bytestream track numbers and allow multiple tracks as
519 // applicable. See https://crbug.com/341581.
520 buffer = StreamParserBuffer::CopyFrom(
521 reinterpret_cast<const uint8_t*>(content.data()),
522 content.length(),
523 &side_data[0],
524 side_data.size(),
525 true, buffer_type, track_num);
528 buffer->set_timestamp(timestamp);
529 if (cluster_start_time_ == kNoTimestamp())
530 cluster_start_time_ = timestamp;
532 base::TimeDelta block_duration_time_delta = kNoTimestamp();
533 if (block_duration >= 0) {
534 block_duration_time_delta = base::TimeDelta::FromMicroseconds(
535 block_duration * timecode_multiplier_);
538 // Prefer encoded duration over BlockGroup->BlockDuration or
539 // TrackEntry->DefaultDuration when available. This layering violation is a
540 // workaround for http://crbug.com/396634, decreasing the likelihood of
541 // fall-back to rough estimation techniques for Blocks that lack a
542 // BlockDuration at the end of a cluster. Cross cluster durations are not
543 // feasible given flexibility of cluster ordering and MSE APIs. Duration
544 // estimation may still apply in cases of encryption and codecs for which
545 // we do not extract encoded duration. Within a cluster, estimates are applied
546 // as Block Timecode deltas, or once the whole cluster is parsed in the case
547 // of the last Block in the cluster. See Track::AddBuffer and
548 // ApplyDurationEstimateIfNeeded().
549 if (encoded_duration != kNoTimestamp()) {
550 DCHECK(encoded_duration != kInfiniteDuration());
551 DCHECK(encoded_duration > base::TimeDelta());
552 buffer->set_duration(encoded_duration);
554 DVLOG(3) << __FUNCTION__ << " : "
555 << "Using encoded duration " << encoded_duration.InSecondsF();
557 if (block_duration_time_delta != kNoTimestamp()) {
558 base::TimeDelta duration_difference =
559 block_duration_time_delta - encoded_duration;
561 const auto kWarnDurationDiff =
562 base::TimeDelta::FromMicroseconds(timecode_multiplier_ * 2);
563 if (duration_difference.magnitude() > kWarnDurationDiff) {
564 LIMITED_MEDIA_LOG(DEBUG, log_cb_, num_duration_errors_,
565 kMaxDurationLogs)
566 << "BlockDuration "
567 << "(" << block_duration_time_delta << ") "
568 << "differs significantly from encoded duration "
569 << "(" << encoded_duration << ").";
572 } else if (block_duration_time_delta != kNoTimestamp()) {
573 buffer->set_duration(block_duration_time_delta);
574 } else {
575 DCHECK_NE(buffer_type, DemuxerStream::TEXT);
576 buffer->set_duration(track->default_duration());
579 if (discard_padding != 0) {
580 buffer->set_discard_padding(std::make_pair(
581 base::TimeDelta(),
582 base::TimeDelta::FromMicroseconds(discard_padding / 1000)));
585 return track->AddBuffer(buffer);
588 WebMClusterParser::Track::Track(int track_num,
589 bool is_video,
590 base::TimeDelta default_duration,
591 const LogCB& log_cb)
592 : track_num_(track_num),
593 is_video_(is_video),
594 default_duration_(default_duration),
595 estimated_next_frame_duration_(kNoTimestamp()),
596 log_cb_(log_cb) {
597 DCHECK(default_duration_ == kNoTimestamp() ||
598 default_duration_ > base::TimeDelta());
601 WebMClusterParser::Track::~Track() {}
603 DecodeTimestamp WebMClusterParser::Track::GetReadyUpperBound() {
604 DCHECK(ready_buffers_.empty());
605 if (last_added_buffer_missing_duration_.get())
606 return last_added_buffer_missing_duration_->GetDecodeTimestamp();
608 return DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max());
611 void WebMClusterParser::Track::ExtractReadyBuffers(
612 const DecodeTimestamp before_timestamp) {
613 DCHECK(ready_buffers_.empty());
614 DCHECK(DecodeTimestamp() <= before_timestamp);
615 DCHECK(kNoDecodeTimestamp() != before_timestamp);
617 if (buffers_.empty())
618 return;
620 if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) {
621 // All of |buffers_| are ready.
622 ready_buffers_.swap(buffers_);
623 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All "
624 << ready_buffers_.size() << " are ready: before upper bound ts "
625 << before_timestamp.InSecondsF();
626 return;
629 // Not all of |buffers_| are ready yet. Move any that are ready to
630 // |ready_buffers_|.
631 while (true) {
632 const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front();
633 if (buffer->GetDecodeTimestamp() >= before_timestamp)
634 break;
635 ready_buffers_.push_back(buffer);
636 buffers_.pop_front();
637 DCHECK(!buffers_.empty());
640 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only "
641 << ready_buffers_.size() << " ready, " << buffers_.size()
642 << " at or after upper bound ts " << before_timestamp.InSecondsF();
645 bool WebMClusterParser::Track::AddBuffer(
646 const scoped_refptr<StreamParserBuffer>& buffer) {
647 DVLOG(2) << "AddBuffer() : " << track_num_
648 << " ts " << buffer->timestamp().InSecondsF()
649 << " dur " << buffer->duration().InSecondsF()
650 << " kf " << buffer->is_key_frame()
651 << " size " << buffer->data_size();
653 if (last_added_buffer_missing_duration_.get()) {
654 base::TimeDelta derived_duration =
655 buffer->timestamp() - last_added_buffer_missing_duration_->timestamp();
656 last_added_buffer_missing_duration_->set_duration(derived_duration);
658 DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : "
659 << " ts "
660 << last_added_buffer_missing_duration_->timestamp().InSecondsF()
661 << " dur "
662 << last_added_buffer_missing_duration_->duration().InSecondsF()
663 << " kf " << last_added_buffer_missing_duration_->is_key_frame()
664 << " size " << last_added_buffer_missing_duration_->data_size();
665 scoped_refptr<StreamParserBuffer> updated_buffer =
666 last_added_buffer_missing_duration_;
667 last_added_buffer_missing_duration_ = NULL;
668 if (!QueueBuffer(updated_buffer))
669 return false;
672 if (buffer->duration() == kNoTimestamp()) {
673 last_added_buffer_missing_duration_ = buffer;
674 DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration";
675 return true;
678 return QueueBuffer(buffer);
681 void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() {
682 if (!last_added_buffer_missing_duration_.get())
683 return;
685 last_added_buffer_missing_duration_->set_duration(GetDurationEstimate());
687 DVLOG(2) << "ApplyDurationEstimateIfNeeded() : new dur : "
688 << " ts "
689 << last_added_buffer_missing_duration_->timestamp().InSecondsF()
690 << " dur "
691 << last_added_buffer_missing_duration_->duration().InSecondsF()
692 << " kf " << last_added_buffer_missing_duration_->is_key_frame()
693 << " size " << last_added_buffer_missing_duration_->data_size();
695 // Don't use the applied duration as a future estimation (don't use
696 // QueueBuffer() here.)
697 buffers_.push_back(last_added_buffer_missing_duration_);
698 last_added_buffer_missing_duration_ = NULL;
701 void WebMClusterParser::Track::ClearReadyBuffers() {
702 // Note that |buffers_| are kept and |estimated_next_frame_duration_| is not
703 // reset here.
704 ready_buffers_.clear();
707 void WebMClusterParser::Track::Reset() {
708 ClearReadyBuffers();
709 buffers_.clear();
710 last_added_buffer_missing_duration_ = NULL;
713 bool WebMClusterParser::Track::IsKeyframe(const uint8_t* data, int size) const {
714 // For now, assume that all blocks are keyframes for datatypes other than
715 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
716 if (!is_video_)
717 return true;
719 // Make sure the block is big enough for the minimal keyframe header size.
720 if (size < 7)
721 return false;
723 // The LSb of the first byte must be a 0 for a keyframe.
724 // http://tools.ietf.org/html/rfc6386 Section 19.1
725 if ((data[0] & 0x01) != 0)
726 return false;
728 // Verify VP8 keyframe startcode.
729 // http://tools.ietf.org/html/rfc6386 Section 19.1
730 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
731 return false;
733 return true;
736 bool WebMClusterParser::Track::QueueBuffer(
737 const scoped_refptr<StreamParserBuffer>& buffer) {
738 DCHECK(!last_added_buffer_missing_duration_.get());
740 // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing
741 // block timecode detection within a cluster. Therefore, we should not see
742 // those here.
743 DecodeTimestamp previous_buffers_timestamp = buffers_.empty() ?
744 DecodeTimestamp() : buffers_.back()->GetDecodeTimestamp();
745 CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp());
747 base::TimeDelta duration = buffer->duration();
748 if (duration < base::TimeDelta() || duration == kNoTimestamp()) {
749 MEDIA_LOG(ERROR, log_cb_)
750 << "Invalid buffer duration: " << duration.InSecondsF();
751 return false;
754 // The estimated frame duration is the minimum non-zero duration since the
755 // last initialization segment. The minimum is used to ensure frame durations
756 // aren't overestimated.
757 if (duration > base::TimeDelta()) {
758 if (estimated_next_frame_duration_ == kNoTimestamp()) {
759 estimated_next_frame_duration_ = duration;
760 } else {
761 estimated_next_frame_duration_ =
762 std::min(duration, estimated_next_frame_duration_);
766 buffers_.push_back(buffer);
767 return true;
770 base::TimeDelta WebMClusterParser::Track::GetDurationEstimate() {
771 base::TimeDelta duration = estimated_next_frame_duration_;
772 if (duration != kNoTimestamp()) {
773 DVLOG(3) << __FUNCTION__ << " : using estimated duration";
774 } else {
775 DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration";
776 if (is_video_) {
777 duration = base::TimeDelta::FromMilliseconds(
778 kDefaultVideoBufferDurationInMs);
779 } else {
780 duration = base::TimeDelta::FromMilliseconds(
781 kDefaultAudioBufferDurationInMs);
785 DCHECK(duration > base::TimeDelta());
786 DCHECK(duration != kNoTimestamp());
787 return duration;
790 void WebMClusterParser::ClearTextTrackReadyBuffers() {
791 text_buffers_map_.clear();
792 for (TextTrackMap::iterator it = text_track_map_.begin();
793 it != text_track_map_.end();
794 ++it) {
795 it->second.ClearReadyBuffers();
799 void WebMClusterParser::ResetTextTracks() {
800 ClearTextTrackReadyBuffers();
801 for (TextTrackMap::iterator it = text_track_map_.begin();
802 it != text_track_map_.end();
803 ++it) {
804 it->second.Reset();
808 void WebMClusterParser::UpdateReadyBuffers() {
809 DCHECK(ready_buffer_upper_bound_ == kNoDecodeTimestamp());
810 DCHECK(text_buffers_map_.empty());
812 if (cluster_ended_) {
813 audio_.ApplyDurationEstimateIfNeeded();
814 video_.ApplyDurationEstimateIfNeeded();
815 // Per OnBlock(), all text buffers should already have valid durations, so
816 // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks
817 // here.
818 ready_buffer_upper_bound_ =
819 DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max());
820 DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound());
821 DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound());
822 } else {
823 ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(),
824 video_.GetReadyUpperBound());
825 DCHECK(DecodeTimestamp() <= ready_buffer_upper_bound_);
826 DCHECK(kNoDecodeTimestamp() != ready_buffer_upper_bound_);
829 // Prepare each track's ready buffers for retrieval.
830 audio_.ExtractReadyBuffers(ready_buffer_upper_bound_);
831 video_.ExtractReadyBuffers(ready_buffer_upper_bound_);
832 for (TextTrackMap::iterator itr = text_track_map_.begin();
833 itr != text_track_map_.end();
834 ++itr) {
835 itr->second.ExtractReadyBuffers(ready_buffer_upper_bound_);
839 WebMClusterParser::Track*
840 WebMClusterParser::FindTextTrack(int track_num) {
841 const TextTrackMap::iterator it = text_track_map_.find(track_num);
843 if (it == text_track_map_.end())
844 return NULL;
846 return &it->second;
849 } // namespace media