Index: media/filters/source_buffer_stream.cc |
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc |
index d2b4fae6e5fc56a902d344cf03e69ac027355fc9..e03a8c38ab8e92d920fc73ac501b2ca38d67af38 100644 |
--- a/media/filters/source_buffer_stream.cc |
+++ b/media/filters/source_buffer_stream.cc |
@@ -37,6 +37,15 @@ const int kMaxTrackBufferGapWarningLogs = 20; |
// Limit the number of MEDIA_LOG() logs for MSE GC algorithm warnings. |
const int kMaxGarbageCollectAlgorithmWarningLogs = 20; |
+// Limit the number of MEDIA_LOG() logs for same DTS for non-keyframe followed |
+// by keyframe. Prior to relaxing the "media segments must begin with a |
+// keyframe" requirement, we issued decode error for this situation. That was |
+// likely too strict, and now that the keyframe requirement is relaxed, we have |
+// no knowledge of media segment boundaries here. Now, we log but don't trigger |
+// decode error, since we allow these sequences which may cause extra decoder |
+// work or other side-effects. |
+const int kMaxStrangeSameTimestampsLogs = 20; |
+ |
// Helper method that returns true if |ranges| is sorted in increasing order, |
// false otherwise. |
bool IsRangeListSorted(const std::list<media::SourceBufferRange*>& ranges) { |
@@ -137,7 +146,7 @@ SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config, |
bool splice_frames_enabled) |
: media_log_(media_log), |
seek_buffer_timestamp_(kNoTimestamp()), |
- media_segment_start_time_(kNoDecodeTimestamp()), |
+ coded_frame_group_start_time_(kNoDecodeTimestamp()), |
range_for_next_append_(ranges_.end()), |
last_output_buffer_timestamp_(kNoDecodeTimestamp()), |
max_interbuffer_distance_(kNoTimestamp()), |
@@ -152,7 +161,7 @@ SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config, |
bool splice_frames_enabled) |
: media_log_(media_log), |
seek_buffer_timestamp_(kNoTimestamp()), |
- media_segment_start_time_(kNoDecodeTimestamp()), |
+ coded_frame_group_start_time_(kNoDecodeTimestamp()), |
range_for_next_append_(ranges_.end()), |
last_output_buffer_timestamp_(kNoDecodeTimestamp()), |
max_interbuffer_distance_(kNoTimestamp()), |
@@ -168,7 +177,7 @@ SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config, |
: media_log_(media_log), |
text_track_config_(text_config), |
seek_buffer_timestamp_(kNoTimestamp()), |
- media_segment_start_time_(kNoDecodeTimestamp()), |
+ coded_frame_group_start_time_(kNoDecodeTimestamp()), |
range_for_next_append_(ranges_.end()), |
last_output_buffer_timestamp_(kNoDecodeTimestamp()), |
max_interbuffer_distance_(kNoTimestamp()), |
@@ -182,26 +191,29 @@ SourceBufferStream::~SourceBufferStream() { |
} |
} |
-void SourceBufferStream::OnNewMediaSegment( |
- DecodeTimestamp media_segment_start_time) { |
- DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
- << " (" << media_segment_start_time.InSecondsF() << ")"; |
+void SourceBufferStream::OnStartOfCodedFrameGroup( |
+ DecodeTimestamp coded_frame_group_start_time) { |
+ DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() << " (" |
+ << coded_frame_group_start_time.InSecondsF() << ")"; |
DCHECK(!end_of_stream_); |
- media_segment_start_time_ = media_segment_start_time; |
- new_media_segment_ = true; |
+ coded_frame_group_start_time_ = coded_frame_group_start_time; |
+ new_coded_frame_group_ = true; |
RangeList::iterator last_range = range_for_next_append_; |
- range_for_next_append_ = FindExistingRangeFor(media_segment_start_time); |
+ range_for_next_append_ = FindExistingRangeFor(coded_frame_group_start_time); |
- // Only reset |last_appended_buffer_timestamp_| if this new media segment is |
- // not adjacent to the previous media segment appended to the stream. |
+ // Only reset |last_appended_buffer_timestamp_| if this new coded frame group |
+ // is not adjacent to the previous coded frame group appended to the stream. |
if (range_for_next_append_ == ranges_.end() || |
!AreAdjacentInSequence(last_appended_buffer_timestamp_, |
- media_segment_start_time)) { |
+ coded_frame_group_start_time)) { |
last_appended_buffer_timestamp_ = kNoDecodeTimestamp(); |
last_appended_buffer_duration_ = kNoTimestamp(); |
last_appended_buffer_is_keyframe_ = false; |
- DVLOG(3) << __FUNCTION__ << " next appended buffers will be in a new range"; |
+ DVLOG(3) << __FUNCTION__ << " next appended buffers will " |
+ << (range_for_next_append_ == ranges_.end() |
+ ? "be in a new range" |
+ : "overlap an existing range"); |
} else if (last_range != ranges_.end()) { |
DCHECK(last_range == range_for_next_append_); |
DVLOG(3) << __FUNCTION__ << " next appended buffers will continue range " |
@@ -215,41 +227,29 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { |
"buffers to append", buffers.size()); |
DCHECK(!buffers.empty()); |
- DCHECK(media_segment_start_time_ != kNoDecodeTimestamp()); |
- DCHECK(media_segment_start_time_ <= buffers.front()->GetDecodeTimestamp()); |
+ DCHECK(coded_frame_group_start_time_ != kNoDecodeTimestamp()); |
+ DCHECK(coded_frame_group_start_time_ <= |
+ buffers.front()->GetDecodeTimestamp()); |
DCHECK(!end_of_stream_); |
DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
<< ": buffers " << BufferQueueToLogString(buffers); |
- // New media segments must begin with a keyframe. |
- // TODO(wolenetz): Relax this requirement. See http://crbug.com/229412. |
- if (new_media_segment_ && !buffers.front()->is_key_frame()) { |
- MEDIA_LOG(ERROR, media_log_) << "Media segment did not begin with key " |
- "frame. Support for such segments will be " |
- "available in a future version. Please see " |
- "https://crbug.com/229412."; |
- return false; |
- } |
- |
- // Buffers within a media segment should be monotonically increasing. |
- if (!IsMonotonicallyIncreasing(buffers)) |
- return false; |
+ // New coded frame groups emitted by the coded frame processor must begin with |
+ // a keyframe. TODO(wolenetz): Change this to [DCHECK + MEDIA_LOG(ERROR...) + |
+ // return false] once the CHECK has baked in a stable release. See |
+ // https://crbug.com/580621. |
+ CHECK(!new_coded_frame_group_ || buffers.front()->is_key_frame()); |
- if (media_segment_start_time_ < DecodeTimestamp() || |
- buffers.front()->GetDecodeTimestamp() < DecodeTimestamp()) { |
- MEDIA_LOG(ERROR, media_log_) |
- << "Cannot append a media segment with negative timestamps."; |
+ // Buffers within a coded frame group should be monotonically increasing. |
+ if (!IsMonotonicallyIncreasing(buffers)) { |
return false; |
} |
- if (!IsNextTimestampValid(buffers.front()->GetDecodeTimestamp(), |
- buffers.front()->is_key_frame())) { |
- const DecodeTimestamp& dts = buffers.front()->GetDecodeTimestamp(); |
+ if (coded_frame_group_start_time_ < DecodeTimestamp() || |
+ buffers.front()->GetDecodeTimestamp() < DecodeTimestamp()) { |
MEDIA_LOG(ERROR, media_log_) |
- << "Invalid same timestamp construct detected at" |
- << " time " << dts.InSecondsF(); |
- |
+ << "Cannot append a coded frame group with negative timestamps."; |
return false; |
} |
@@ -265,21 +265,40 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { |
// If there's a range for |buffers|, insert |buffers| accordingly. Otherwise, |
// create a new range with |buffers|. |
if (range_for_next_append_ != ranges_.end()) { |
- (*range_for_next_append_)->AppendBuffersToEnd(buffers); |
+ if (new_coded_frame_group_ && (!splice_frames_enabled_ || |
+ buffers.front()->splice_buffers().empty())) { |
+ // If the first append to this stream in a new coded frame group continues |
+ // a previous range, use the new group's start time instead of the first |
+ // new buffer's timestamp as the proof of adjacency to the existing range. |
+ // A large gap (larger than our normal buffer adjacency test) can occur in |
+ // a muxed set of streams (which share a common coded frame group start |
+ // time) with a significantly jagged start across the streams. |
+ // Don't do this logic if there was a splice frame generated for the first |
+ // new buffer, since splices are guaranteed to be in the same range and |
+ // adjacent, and since the splice frame's timestamp can be less than |
+ // |coded_frame_group_start_time_| due to the splicing. |
+ (*range_for_next_append_) |
+ ->AppendBuffersToEnd(buffers, coded_frame_group_start_time_); |
+ } else { |
+ // Otherwise, use the first new buffer's timestamp as the proof of |
+ // adjacency. |
+ (*range_for_next_append_) |
+ ->AppendBuffersToEnd(buffers, kNoDecodeTimestamp()); |
+ } |
+ |
last_appended_buffer_timestamp_ = buffers.back()->GetDecodeTimestamp(); |
last_appended_buffer_duration_ = buffers.back()->duration(); |
last_appended_buffer_is_keyframe_ = buffers.back()->is_key_frame(); |
} else { |
DecodeTimestamp new_range_start_time = std::min( |
- media_segment_start_time_, buffers.front()->GetDecodeTimestamp()); |
+ coded_frame_group_start_time_, buffers.front()->GetDecodeTimestamp()); |
const BufferQueue* buffers_for_new_range = &buffers; |
BufferQueue trimmed_buffers; |
- // If the new range is not being created because of a new media |
- // segment, then we must make sure that we start with a key frame. |
- // This can happen if the GOP in the previous append gets destroyed |
- // by a Remove() call. |
- if (!new_media_segment_) { |
+ // If the new range is not being created because of a new coded frame group, |
+ // then we must make sure that we start with a key frame. This can happen |
+ // if the GOP in the previous append gets destroyed by a Remove() call. |
+ if (!new_coded_frame_group_) { |
BufferQueue::const_iterator itr = buffers.begin(); |
// Scan past all the non-key-frames. |
@@ -294,7 +313,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { |
last_appended_buffer_duration_ = buffers.back()->duration(); |
last_appended_buffer_is_keyframe_ = buffers.back()->is_key_frame(); |
DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
- << ": new buffers in the middle of media segment depend on" |
+ << ": new buffers in the middle of coded frame group depend on" |
"keyframe that has been removed, and contain no keyframes." |
"Skipping further processing."; |
DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
@@ -324,7 +343,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { |
buffers_for_new_range->back()->is_key_frame(); |
} |
- new_media_segment_ = false; |
+ new_coded_frame_group_ = false; |
MergeWithAdjacentRangeIfNecessary(range_for_next_append_); |
@@ -413,6 +432,28 @@ void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, |
} |
} |
+DecodeTimestamp SourceBufferStream::PotentialNextAppendTimestamp() const { |
+ // The next potential append will either be just at or after |
+ // |last_appended_buffer_timestamp_| (if known), or if unknown and we are |
+ // still at the beginning of a new coded frame group, then will be into the |
+ // range (if any) to which |coded_frame_group_start_time_| belongs. |
+ if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp()) { |
+ // TODO(wolenetz): Determine if this +1us is still necessary. See |
+ // https://crbug.com/589295. |
+ return last_appended_buffer_timestamp_ + |
+ base::TimeDelta::FromInternalValue(1); |
+ } |
+ |
+ if (new_coded_frame_group_) |
+ return coded_frame_group_start_time_; |
+ |
+ // If we still don't know a potential next append timestamp, then we have |
+ // removed the ranged to which it previously belonged and have not completed a |
+ // subsequent append or received a subsequent OnStartOfCodedFrameGroup() |
+ // signal. |
+ return kNoDecodeTimestamp(); |
+} |
+ |
void SourceBufferStream::RemoveInternal(DecodeTimestamp start, |
DecodeTimestamp end, |
bool exclude_start, |
@@ -440,6 +481,19 @@ void SourceBufferStream::RemoveInternal(DecodeTimestamp start, |
SourceBufferRange* new_range = range->SplitRange(end); |
if (new_range) { |
itr = ranges_.insert(++itr, new_range); |
+ |
+ // Update |range_for_next_append_| if it was previously |range| and should |
+ // be |new_range| now. |
+ if (range_for_next_append_ != ranges_.end() && |
+ *range_for_next_append_ == range) { |
+ DecodeTimestamp potential_next_append_timestamp = |
+ PotentialNextAppendTimestamp(); |
+ if (potential_next_append_timestamp != kNoDecodeTimestamp() && |
+ new_range->BelongsToRange(potential_next_append_timestamp)) { |
+ range_for_next_append_ = itr; |
+ } |
+ } |
+ |
--itr; |
// Update the selected range if the next buffer position was transferred |
@@ -476,11 +530,9 @@ void SourceBufferStream::RemoveInternal(DecodeTimestamp start, |
// operation makes it impossible for the next append to be added |
// to the current range. |
if (range_for_next_append_ != ranges_.end() && |
- *range_for_next_append_ == range && |
- last_appended_buffer_timestamp_ != kNoDecodeTimestamp()) { |
+ *range_for_next_append_ == range) { |
DecodeTimestamp potential_next_append_timestamp = |
- last_appended_buffer_timestamp_ + |
- base::TimeDelta::FromInternalValue(1); |
+ PotentialNextAppendTimestamp(); |
if (!range->BelongsToRange(potential_next_append_timestamp)) { |
DVLOG(1) << "Resetting range_for_next_append_ since the next append" |
@@ -522,8 +574,7 @@ bool SourceBufferStream::ShouldSeekToStartOfBuffered( |
beginning_of_buffered < kSeekToStartFudgeRoom()); |
} |
-bool SourceBufferStream::IsMonotonicallyIncreasing( |
- const BufferQueue& buffers) const { |
+bool SourceBufferStream::IsMonotonicallyIncreasing(const BufferQueue& buffers) { |
DCHECK(!buffers.empty()); |
DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_; |
bool prev_is_keyframe = last_appended_buffer_is_keyframe_; |
@@ -546,12 +597,13 @@ bool SourceBufferStream::IsMonotonicallyIncreasing( |
} |
if (current_timestamp == prev_timestamp && |
- !SourceBufferRange::AllowSameTimestamp(prev_is_keyframe, |
- current_is_keyframe)) { |
- MEDIA_LOG(ERROR, media_log_) << "Unexpected combination of buffers with" |
- << " the same timestamp detected at " |
- << current_timestamp.InSecondsF(); |
- return false; |
+ SourceBufferRange::IsUncommonSameTimestampSequence( |
+ prev_is_keyframe, current_is_keyframe)) { |
+ LIMITED_MEDIA_LOG(DEBUG, media_log_, num_strange_same_timestamps_logs_, |
+ kMaxStrangeSameTimestampsLogs) |
+ << "Detected an append sequence with keyframe following a " |
+ "non-keyframe, both with the same decode timestamp of " |
+ << current_timestamp.InSecondsF(); |
} |
} |
@@ -561,15 +613,6 @@ bool SourceBufferStream::IsMonotonicallyIncreasing( |
return true; |
} |
-bool SourceBufferStream::IsNextTimestampValid( |
- DecodeTimestamp next_timestamp, bool next_is_keyframe) const { |
- return (last_appended_buffer_timestamp_ != next_timestamp) || |
- new_media_segment_ || |
- SourceBufferRange::AllowSameTimestamp(last_appended_buffer_is_keyframe_, |
- next_is_keyframe); |
-} |
- |
- |
bool SourceBufferStream::OnlySelectedRangeIsSeeked() const { |
for (RangeList::const_iterator itr = ranges_.begin(); |
itr != ranges_.end(); ++itr) { |
@@ -938,9 +981,7 @@ void SourceBufferStream::PrepareRangesForNextAppend( |
GenerateSpliceFrame(new_buffers); |
DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_; |
- bool prev_is_keyframe = last_appended_buffer_is_keyframe_; |
DecodeTimestamp next_timestamp = new_buffers.front()->GetDecodeTimestamp(); |
- bool next_is_keyframe = new_buffers.front()->is_key_frame(); |
if (prev_timestamp != kNoDecodeTimestamp() && |
prev_timestamp != next_timestamp) { |
@@ -949,22 +990,25 @@ void SourceBufferStream::PrepareRangesForNextAppend( |
RemoveInternal(prev_timestamp, next_timestamp, true, deleted_buffers); |
} |
- // Make the delete range exclusive if we are dealing with an allowed same |
- // timestamp situation. This prevents the first buffer in the current append |
- // from deleting the last buffer in the previous append if both buffers |
- // have the same timestamp. |
- // |
- // The delete range should never be exclusive if a splice frame was generated |
- // because we don't generate splice frames for same timestamp situations. |
+ // Always make the start of the delete range exclusive for same timestamp |
+ // across the last buffer in the previous append and the first buffer in the |
+ // current append. Never be exclusive if a splice frame was generated because |
+ // we don't generate splice frames for same timestamp situations. |
DCHECK(new_buffers.front()->splice_timestamp() != |
new_buffers.front()->timestamp()); |
- const bool exclude_start = |
- new_buffers.front()->splice_buffers().empty() && |
- prev_timestamp == next_timestamp && |
- SourceBufferRange::AllowSameTimestamp(prev_is_keyframe, next_is_keyframe); |
+ const bool exclude_start = new_buffers.front()->splice_buffers().empty() && |
+ prev_timestamp == next_timestamp; |
// Delete the buffers that |new_buffers| overlaps. |
DecodeTimestamp start = new_buffers.front()->GetDecodeTimestamp(); |
+ if (new_coded_frame_group_) { |
+ // Extend the deletion range earlier to the coded frame group start time if |
+ // this is the first append in a new coded frame group. Note that |start| |
+ // could already be less than |coded_frame_group_start_time_| if a splice |
+ // was generated. |
+ DCHECK(coded_frame_group_start_time_ != kNoDecodeTimestamp()); |
+ start = std::min(coded_frame_group_start_time_, start); |
+ } |
DecodeTimestamp end = new_buffers.back()->GetDecodeTimestamp(); |
base::TimeDelta duration = new_buffers.back()->duration(); |