| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/filters/source_buffer_stream.h" |
| |
| #include <algorithm> |
| #include <map> |
| |
| #include "base/bind.h" |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| |
| namespace media { |
| // Helper class representing a range of buffered data. All buffers in a |
| // SourceBufferRange are ordered sequentially in presentation order with no |
| // gaps. |
| class SourceBufferRange { |
| public: |
| typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
| |
| // Returns the maximum distance in time between any buffer seen in this |
| // stream. Used to estimate the duration of a buffer if its duration is not |
| // known. |
| typedef base::Callback<base::TimeDelta()> InterbufferDistanceCB; |
| |
| // Creates a source buffer range with |new_buffers|. |new_buffers| cannot be |
| // empty and the front of |new_buffers| must be a keyframe. |
| // |media_segment_start_time| refers to the starting timestamp for the media |
| // segment to which these buffers belong. |
| SourceBufferRange(const BufferQueue& new_buffers, |
| base::TimeDelta media_segment_start_time, |
| const InterbufferDistanceCB& interbuffer_distance_cb); |
| |
| // Appends |buffers| to the end of the range and updates |keyframe_map_| as |
| // it encounters new keyframes. Assumes |buffers| belongs at the end of the |
| // range. |
| void AppendBuffersToEnd(const BufferQueue& buffers); |
| bool CanAppendBuffersToEnd(const BufferQueue& buffers) const; |
| |
| // Appends the buffers from |range| into this range. |
| // The first buffer in |range| must come directly after the last buffer |
| // in this range. |
| // If |transfer_current_position| is true, |range|'s |next_buffer_index_| |
| // is transfered to this SourceBufferRange. |
| void AppendRangeToEnd(const SourceBufferRange& range, |
| bool transfer_current_position); |
| bool CanAppendRangeToEnd(const SourceBufferRange& range) const; |
| |
| // Updates |next_buffer_index_| to point to the Buffer containing |timestamp|. |
| // Assumes |timestamp| is valid and in this range. |
| void Seek(base::TimeDelta timestamp); |
| |
| // Updates |next_buffer_index_| to point to next keyframe after or equal to |
| // |timestamp|. |
| void SeekAheadTo(base::TimeDelta timestamp); |
| |
| // Updates |next_buffer_index_| to point to next keyframe strictly after |
| // |timestamp|. |
| void SeekAheadPast(base::TimeDelta timestamp); |
| |
| // Seeks to the beginning of the range. |
| void SeekToStart(); |
| |
| // Finds the next keyframe from |buffers_| after |timestamp| (or at |
| // |timestamp| if |is_exclusive| is false) and creates and returns a new |
| // SourceBufferRange with the buffers from that keyframe onward. |
| // The buffers in the new SourceBufferRange are moved out of this range. If |
| // there is no keyframe after |timestamp|, SplitRange() returns null and this |
| // range is unmodified. |
| SourceBufferRange* SplitRange(base::TimeDelta timestamp, bool is_exclusive); |
| |
| // Deletes the buffers from this range starting at |timestamp|, exclusive if |
| // |is_exclusive| is true, inclusive otherwise. |
| // Resets |next_buffer_index_| if the buffer at |next_buffer_index_| was |
| // deleted, and deletes the |keyframe_map_| entries for the buffers that |
| // were removed. |
| // |deleted_buffers| contains the buffers that were deleted from this range, |
| // starting at the buffer that had been at |next_buffer_index_|. |
| void TruncateAt(base::TimeDelta timestamp, |
| BufferQueue* deleted_buffers, bool is_exclusive); |
| // Deletes all buffers in range. |
| void DeleteAll(BufferQueue* deleted_buffers); |
| |
| // Deletes a GOP from the front or back of the range and moves these |
| // buffers into |deleted_buffers|. Returns the number of bytes deleted from |
| // the range (i.e. the size in bytes of |deleted_buffers|). |
| int DeleteGOPFromFront(BufferQueue* deleted_buffers); |
| int DeleteGOPFromBack(BufferQueue* deleted_buffers); |
| |
| // Gets the range of GOP to secure at least |bytes_to_free| from |
| // [|start_timestamp|, |end_timestamp|). |
| // Returns the size of the buffers to secure if the buffers of |
| // [|start_timestamp|, |end_removal_timestamp|) is removed. |
| // Will not update |end_removal_timestamp| if the returned size is 0. |
| int GetRemovalGOP( |
| base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, |
| int bytes_to_free, base::TimeDelta* end_removal_timestamp); |
| |
| // Indicates whether the GOP at the beginning or end of the range contains the |
| // next buffer position. |
| bool FirstGOPContainsNextBufferPosition() const; |
| bool LastGOPContainsNextBufferPosition() const; |
| |
| // Updates |out_buffer| with the next buffer in presentation order. Seek() |
| // must be called before calls to GetNextBuffer(), and buffers are returned |
| // in order from the last call to Seek(). Returns true if |out_buffer| is |
| // filled with a valid buffer, false if there is not enough data to fulfill |
| // the request. |
| bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer); |
| bool HasNextBuffer() const; |
| |
| // Returns the config ID for the buffer that will be returned by |
| // GetNextBuffer(). |
| int GetNextConfigId() const; |
| |
| // Returns true if the range knows the position of the next buffer it should |
| // return, i.e. it has been Seek()ed. This does not necessarily mean that it |
| // has the next buffer yet. |
| bool HasNextBufferPosition() const; |
| |
| // Resets this range to an "unseeked" state. |
| void ResetNextBufferPosition(); |
| |
| // Returns the timestamp of the next buffer that will be returned from |
| // GetNextBuffer(), or kNoTimestamp() if the timestamp is unknown. |
| base::TimeDelta GetNextTimestamp() const; |
| |
| // Returns the start timestamp of the range. |
| base::TimeDelta GetStartTimestamp() const; |
| |
| // Returns the timestamp of the last buffer in the range. |
| base::TimeDelta GetEndTimestamp() const; |
| |
| // Returns the timestamp for the end of the buffered region in this range. |
| // This is an approximation if the duration for the last buffer in the range |
| // is unset. |
| base::TimeDelta GetBufferedEndTimestamp() const; |
| |
| // Gets the timestamp for the keyframe that is after |timestamp|. If |
| // there isn't a keyframe in the range after |timestamp| then kNoTimestamp() |
| // is returned. |
| base::TimeDelta NextKeyframeTimestamp(base::TimeDelta timestamp); |
| |
| // Gets the timestamp for the closest keyframe that is <= |timestamp|. If |
| // there isn't a keyframe before |timestamp| or |timestamp| is outside |
| // this range, then kNoTimestamp() is returned. |
| base::TimeDelta KeyframeBeforeTimestamp(base::TimeDelta timestamp); |
| |
| // Returns whether a buffer with a starting timestamp of |timestamp| would |
| // belong in this range. This includes a buffer that would be appended to |
| // the end of the range. |
| bool BelongsToRange(base::TimeDelta timestamp) const; |
| |
| // Returns true if the range has enough data to seek to the specified |
| // |timestamp|, false otherwise. |
| bool CanSeekTo(base::TimeDelta timestamp) const; |
| |
| // Returns true if this range's buffered timespan completely overlaps the |
| // buffered timespan of |range|. |
| bool CompletelyOverlaps(const SourceBufferRange& range) const; |
| |
| // Returns true if the end of this range contains buffers that overlaps with |
| // the beginning of |range|. |
| bool EndOverlaps(const SourceBufferRange& range) const; |
| |
| // Returns true if |timestamp| is the timestamp of the next buffer in |
| // sequence after |buffer|, false otherwise. |
| bool IsNextInSequence( |
| const scoped_refptr<media::StreamParserBuffer>& buffer, |
| base::TimeDelta timestamp) const; |
| |
| int size_in_bytes() const { return size_in_bytes_; } |
| |
| private: |
| typedef std::map<base::TimeDelta, int> KeyframeMap; |
| |
| // Seeks the range to the next keyframe after |timestamp|. If |
| // |skip_given_timestamp| is true, the seek will go to a keyframe with a |
| // timestamp strictly greater than |timestamp|. |
| void SeekAhead(base::TimeDelta timestamp, bool skip_given_timestamp); |
| |
| // Returns an iterator in |buffers_| pointing to the buffer at |timestamp|. |
| // If |skip_given_timestamp| is true, this returns the first buffer with |
| // timestamp greater than |timestamp|. |
| BufferQueue::iterator GetBufferItrAt( |
| base::TimeDelta timestamp, bool skip_given_timestamp); |
| |
| // Returns an iterator in |keyframe_map_| pointing to the next keyframe after |
| // |timestamp|. If |skip_given_timestamp| is true, this returns the first |
| // keyframe with a timestamp strictly greater than |timestamp|. |
| KeyframeMap::iterator GetFirstKeyframeAt( |
| base::TimeDelta timestamp, bool skip_given_timestamp); |
| |
| // Returns an iterator in |keyframe_map_| pointing to the first keyframe |
| // before or at |timestamp|. |
| KeyframeMap::iterator GetFirstKeyframeBefore(base::TimeDelta timestamp); |
| |
| // Helper method to delete buffers in |buffers_| starting at |
| // |starting_point|, an iterator in |buffers_|. |
| void TruncateAt(const BufferQueue::iterator& starting_point, |
| BufferQueue* deleted_buffers); |
| |
| // Frees the buffers in |buffers_| from [|start_point|,|ending_point|) and |
| // updates the |size_in_bytes_| accordingly. Does not update |keyframe_map_|. |
| void FreeBufferRange(const BufferQueue::iterator& starting_point, |
| const BufferQueue::iterator& ending_point); |
| |
| // Returns the distance in time estimating how far from the beginning or end |
| // of this range a buffer can be to considered in the range. |
| base::TimeDelta GetFudgeRoom() const; |
| |
| // Returns the approximate duration of a buffer in this range. |
| base::TimeDelta GetApproximateDuration() const; |
| |
| // An ordered list of buffers in this range. |
| BufferQueue buffers_; |
| |
| // Maps keyframe timestamps to its index position in |buffers_|. |
| KeyframeMap keyframe_map_; |
| |
| // Index base of all positions in |keyframe_map_|. In other words, the |
| // real position of entry |k| of |keyframe_map_| in the range is: |
| // keyframe_map_[k] - keyframe_map_index_base_ |
| int keyframe_map_index_base_; |
| |
| // Index into |buffers_| for the next buffer to be returned by |
| // GetNextBuffer(), set to -1 before Seek(). |
| int next_buffer_index_; |
| |
| // If the first buffer in this range is the beginning of a media segment, |
| // |media_segment_start_time_| is the time when the media segment begins. |
| // |media_segment_start_time_| may be <= the timestamp of the first buffer in |
| // |buffers_|. |media_segment_start_time_| is kNoTimestamp() if this range |
| // does not start at the beginning of a media segment, which can only happen |
| // garbage collection or after an end overlap that results in a split range |
| // (we don't have a way of knowing the media segment timestamp for the new |
| // range). |
| base::TimeDelta media_segment_start_time_; |
| |
| // Called to get the largest interbuffer distance seen so far in the stream. |
| InterbufferDistanceCB interbuffer_distance_cb_; |
| |
| // Stores the amount of memory taken up by the data in |buffers_|. |
| int size_in_bytes_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SourceBufferRange); |
| }; |
| |
| } // namespace media |
| |
| // Helper method that returns true if |ranges| is sorted in increasing order, |
| // false otherwise. |
| static bool IsRangeListSorted( |
| const std::list<media::SourceBufferRange*>& ranges) { |
| base::TimeDelta prev = media::kNoTimestamp(); |
| for (std::list<media::SourceBufferRange*>::const_iterator itr = |
| ranges.begin(); itr != ranges.end(); ++itr) { |
| if (prev != media::kNoTimestamp() && prev >= (*itr)->GetStartTimestamp()) |
| return false; |
| prev = (*itr)->GetEndTimestamp(); |
| } |
| return true; |
| } |
| |
| // Comparison function for two Buffers based on timestamp. |
| static bool BufferComparator( |
| const scoped_refptr<media::StreamParserBuffer>& first, |
| const scoped_refptr<media::StreamParserBuffer>& second) { |
| return first->GetDecodeTimestamp() < second->GetDecodeTimestamp(); |
| } |
| |
| // Returns an estimate of how far from the beginning or end of a range a buffer |
| // can be to still be considered in the range, given the |approximate_duration| |
| // of a buffer in the stream. |
| static base::TimeDelta ComputeFudgeRoom(base::TimeDelta approximate_duration) { |
| // Because we do not know exactly when is the next timestamp, any buffer |
| // that starts within 2x the approximate duration of a buffer is considered |
| // within this range. |
| return 2 * approximate_duration; |
| } |
| |
| // An arbitrarily-chosen number to estimate the duration of a buffer if none |
| // is set and there's not enough information to get a better estimate. |
| static int kDefaultBufferDurationInMs = 125; |
| |
| // The amount of time the beginning of the buffered data can differ from the |
| // start time in order to still be considered the start of stream. |
| static base::TimeDelta kSeekToStartFudgeRoom() { |
| return base::TimeDelta::FromMilliseconds(1000); |
| } |
| // The maximum amount of data in bytes the stream will keep in memory. |
| #if defined(GOOGLE_TV) |
| // In Google TV, set the size of the buffer to 1 min because of |
| // the limited memory of the embedded system. |
| // 2MB: approximately 1 minutes of 256Kbps content. |
| // 30MB: approximately 1 minutes of 4Mbps content. |
| static int kDefaultAudioMemoryLimit = 2 * 1024 * 1024; |
| static int kDefaultVideoMemoryLimit = 30 * 1024 * 1024; |
| #else |
| // 12MB: approximately 5 minutes of 320Kbps content. |
| // 150MB: approximately 5 minutes of 4Mbps content. |
| static int kDefaultAudioMemoryLimit = 12 * 1024 * 1024; |
| static int kDefaultVideoMemoryLimit = 150 * 1024 * 1024; |
| #endif |
| |
| namespace media { |
| |
| SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config, |
| const LogCB& log_cb) |
| : log_cb_(log_cb), |
| current_config_index_(0), |
| append_config_index_(0), |
| seek_pending_(false), |
| end_of_stream_(false), |
| seek_buffer_timestamp_(kNoTimestamp()), |
| selected_range_(NULL), |
| media_segment_start_time_(kNoTimestamp()), |
| range_for_next_append_(ranges_.end()), |
| new_media_segment_(false), |
| last_appended_buffer_timestamp_(kNoTimestamp()), |
| last_appended_buffer_is_keyframe_(false), |
| last_output_buffer_timestamp_(kNoTimestamp()), |
| max_interbuffer_distance_(kNoTimestamp()), |
| memory_limit_(kDefaultAudioMemoryLimit), |
| config_change_pending_(false) { |
| DCHECK(audio_config.IsValidConfig()); |
| audio_configs_.push_back(audio_config); |
| } |
| |
| SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config, |
| const LogCB& log_cb) |
| : log_cb_(log_cb), |
| current_config_index_(0), |
| append_config_index_(0), |
| seek_pending_(false), |
| end_of_stream_(false), |
| seek_buffer_timestamp_(kNoTimestamp()), |
| selected_range_(NULL), |
| media_segment_start_time_(kNoTimestamp()), |
| range_for_next_append_(ranges_.end()), |
| new_media_segment_(false), |
| last_appended_buffer_timestamp_(kNoTimestamp()), |
| last_appended_buffer_is_keyframe_(false), |
| last_output_buffer_timestamp_(kNoTimestamp()), |
| max_interbuffer_distance_(kNoTimestamp()), |
| memory_limit_(kDefaultVideoMemoryLimit), |
| config_change_pending_(false) { |
| DCHECK(video_config.IsValidConfig()); |
| video_configs_.push_back(video_config); |
| } |
| |
| SourceBufferStream::~SourceBufferStream() { |
| while (!ranges_.empty()) { |
| delete ranges_.front(); |
| ranges_.pop_front(); |
| } |
| } |
| |
| void SourceBufferStream::OnNewMediaSegment( |
| base::TimeDelta media_segment_start_time) { |
| DCHECK(!end_of_stream_); |
| media_segment_start_time_ = media_segment_start_time; |
| new_media_segment_ = true; |
| |
| RangeList::iterator last_range = range_for_next_append_; |
| range_for_next_append_ = FindExistingRangeFor(media_segment_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. |
| if (range_for_next_append_ == ranges_.end() || |
| !AreAdjacentInSequence(last_appended_buffer_timestamp_, |
| media_segment_start_time)) { |
| last_appended_buffer_timestamp_ = kNoTimestamp(); |
| last_appended_buffer_is_keyframe_ = false; |
| } else { |
| DCHECK(last_range == range_for_next_append_); |
| } |
| } |
| |
| bool SourceBufferStream::Append( |
| const SourceBufferStream::BufferQueue& buffers) { |
| TRACE_EVENT2("mse", "SourceBufferStream::Append", |
| "stream type", GetStreamTypeName(), |
| "buffers to append", buffers.size()); |
| |
| DCHECK(!buffers.empty()); |
| DCHECK(media_segment_start_time_ != kNoTimestamp()); |
| DCHECK(!end_of_stream_); |
| |
| // New media segments must begin with a keyframe. |
| if (new_media_segment_ && !buffers.front()->IsKeyframe()) { |
| MEDIA_LOG(log_cb_) << "Media segment did not begin with keyframe."; |
| return false; |
| } |
| |
| // Buffers within a media segment should be monotonically increasing. |
| if (!IsMonotonicallyIncreasing(buffers)) |
| return false; |
| |
| if (media_segment_start_time_ < base::TimeDelta() || |
| buffers.front()->GetDecodeTimestamp() < base::TimeDelta()) { |
| MEDIA_LOG(log_cb_) |
| << "Cannot append a media segment with negative timestamps."; |
| return false; |
| } |
| |
| UpdateMaxInterbufferDistance(buffers); |
| SetConfigIds(buffers); |
| |
| // Save a snapshot of stream state before range modifications are made. |
| base::TimeDelta next_buffer_timestamp = GetNextBufferTimestamp(); |
| BufferQueue deleted_buffers; |
| |
| RangeList::iterator range_for_new_buffers = range_for_next_append_; |
| // If there's a range for |buffers|, insert |buffers| accordingly. Otherwise, |
| // create a new range with |buffers|. |
| if (range_for_new_buffers != ranges_.end()) { |
| if (!InsertIntoExistingRange(range_for_new_buffers, buffers, |
| &deleted_buffers)) { |
| return false; |
| } |
| } else { |
| DCHECK(new_media_segment_); |
| range_for_new_buffers = |
| AddToRanges(new SourceBufferRange( |
| buffers, media_segment_start_time_, |
| base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, |
| base::Unretained(this)))); |
| } |
| |
| range_for_next_append_ = range_for_new_buffers; |
| new_media_segment_ = false; |
| last_appended_buffer_timestamp_ = buffers.back()->GetDecodeTimestamp(); |
| last_appended_buffer_is_keyframe_ = buffers.back()->IsKeyframe(); |
| |
| // Resolve overlaps. |
| ResolveCompleteOverlaps(range_for_new_buffers, &deleted_buffers); |
| ResolveEndOverlap(range_for_new_buffers, &deleted_buffers); |
| MergeWithAdjacentRangeIfNecessary(range_for_new_buffers); |
| |
| // Seek to try to fulfill a previous call to Seek(). |
| if (seek_pending_) { |
| DCHECK(!selected_range_); |
| DCHECK(deleted_buffers.empty()); |
| Seek(seek_buffer_timestamp_); |
| } |
| |
| if (!deleted_buffers.empty()) { |
| base::TimeDelta start_of_deleted = |
| deleted_buffers.front()->GetDecodeTimestamp(); |
| |
| DCHECK(track_buffer_.empty() || |
| track_buffer_.back()->GetDecodeTimestamp() < start_of_deleted) |
| << "decode timestamp " |
| << track_buffer_.back()->GetDecodeTimestamp().InSecondsF() << " sec" |
| << ", start_of_deleted " << start_of_deleted.InSecondsF()<< " sec"; |
| |
| track_buffer_.insert(track_buffer_.end(), deleted_buffers.begin(), |
| deleted_buffers.end()); |
| } |
| |
| // Prune any extra buffers in |track_buffer_| if new keyframes |
| // are appended to the range covered by |track_buffer_|. |
| if (!track_buffer_.empty()) { |
| base::TimeDelta keyframe_timestamp = |
| FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp()); |
| if (keyframe_timestamp != kNoTimestamp()) |
| PruneTrackBuffer(keyframe_timestamp); |
| } |
| |
| SetSelectedRangeIfNeeded(next_buffer_timestamp); |
| |
| GarbageCollectIfNeeded(); |
| |
| DCHECK(IsRangeListSorted(ranges_)); |
| DCHECK(OnlySelectedRangeIsSeeked()); |
| return true; |
| } |
| |
| void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, |
| base::TimeDelta duration) { |
| DVLOG(1) << __FUNCTION__ << "(" << start.InSecondsF() |
| << ", " << end.InSecondsF() |
| << ", " << duration.InSecondsF() << ")"; |
| DCHECK(start >= base::TimeDelta()) << start.InSecondsF(); |
| DCHECK(start < end) << "start " << start.InSecondsF() |
| << " end " << end.InSecondsF(); |
| DCHECK(duration != kNoTimestamp()); |
| |
| base::TimeDelta remove_end_timestamp = duration; |
| base::TimeDelta keyframe_timestamp = FindKeyframeAfterTimestamp(end); |
| if (keyframe_timestamp != kNoTimestamp()) { |
| remove_end_timestamp = keyframe_timestamp; |
| } else if (end < remove_end_timestamp) { |
| remove_end_timestamp = end; |
| } |
| |
| RangeList::iterator itr = ranges_.begin(); |
| |
| while (itr != ranges_.end()) { |
| SourceBufferRange* range = *itr; |
| if (range->GetStartTimestamp() >= remove_end_timestamp) |
| break; |
| |
| // Split off any remaining end piece and add it to |ranges_|. |
| SourceBufferRange* new_range = |
| range->SplitRange(remove_end_timestamp, false); |
| if (new_range) { |
| itr = ranges_.insert(++itr, new_range); |
| --itr; |
| |
| // Update the selected range if the next buffer position was transferred |
| // to |new_range|. |
| if (new_range->HasNextBufferPosition()) |
| SetSelectedRange(new_range); |
| } |
| |
| // If the current range now is completely covered by the removal |
| // range then delete it and move on. |
| if (start <= range->GetStartTimestamp()) { |
| DeleteAndRemoveRange(&itr); |
| continue; |
| } |
| |
| // Truncate the current range so that it only contains data before |
| // the removal range. |
| BufferQueue saved_buffers; |
| range->TruncateAt(start, &saved_buffers, false); |
| |
| // Check to see if the current playback position was removed and |
| // update the selected range appropriately. |
| if (!saved_buffers.empty()) { |
| DCHECK(!range->HasNextBufferPosition()); |
| SetSelectedRange(NULL); |
| SetSelectedRangeIfNeeded(saved_buffers.front()->GetDecodeTimestamp()); |
| } |
| |
| // Move on to the next range. |
| ++itr; |
| } |
| |
| DCHECK(IsRangeListSorted(ranges_)); |
| DCHECK(OnlySelectedRangeIsSeeked()); |
| } |
| |
| void SourceBufferStream::ResetSeekState() { |
| SetSelectedRange(NULL); |
| track_buffer_.clear(); |
| config_change_pending_ = false; |
| last_output_buffer_timestamp_ = kNoTimestamp(); |
| } |
| |
| bool SourceBufferStream::ShouldSeekToStartOfBuffered( |
| base::TimeDelta seek_timestamp) const { |
| if (ranges_.empty()) |
| return false; |
| base::TimeDelta beginning_of_buffered = |
| ranges_.front()->GetStartTimestamp(); |
| return (seek_timestamp <= beginning_of_buffered && |
| beginning_of_buffered < kSeekToStartFudgeRoom()); |
| } |
| |
| // Buffers with the same timestamp are only allowed under certain conditions. |
| // Video: Allowed when the previous frame and current frame are NOT keyframes. |
| // This is the situation for VP8 Alt-Ref frames. |
| // Otherwise: Allowed in all situations except where a non-keyframe is followed |
| // by a keyframe. |
| // Returns true if |prev_is_keyframe| and |current_is_keyframe| indicate a |
| // same timestamp situation that is allowed. False is returned otherwise. |
| bool SourceBufferStream::AllowSameTimestamp( |
| bool prev_is_keyframe, bool current_is_keyframe) const { |
| if (video_configs_.size() > 0) |
| return !prev_is_keyframe && !current_is_keyframe; |
| |
| return prev_is_keyframe || !current_is_keyframe; |
| } |
| |
| bool SourceBufferStream::IsMonotonicallyIncreasing( |
| const BufferQueue& buffers) const { |
| DCHECK(!buffers.empty()); |
| base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; |
| bool prev_is_keyframe = last_appended_buffer_is_keyframe_; |
| for (BufferQueue::const_iterator itr = buffers.begin(); |
| itr != buffers.end(); ++itr) { |
| base::TimeDelta current_timestamp = (*itr)->GetDecodeTimestamp(); |
| bool current_is_keyframe = (*itr)->IsKeyframe(); |
| DCHECK(current_timestamp != kNoTimestamp()); |
| |
| if (prev_timestamp != kNoTimestamp()) { |
| if (current_timestamp < prev_timestamp) { |
| MEDIA_LOG(log_cb_) << "Buffers were not monotonically increasing."; |
| return false; |
| } |
| |
| if (current_timestamp == prev_timestamp && |
| !AllowSameTimestamp(prev_is_keyframe, current_is_keyframe)) { |
| MEDIA_LOG(log_cb_) << "Unexpected combination of buffers with the" |
| << " same timestamp detected at " |
| << current_timestamp.InSecondsF(); |
| return false; |
| } |
| } |
| |
| prev_timestamp = current_timestamp; |
| prev_is_keyframe = current_is_keyframe; |
| } |
| return true; |
| } |
| |
| bool SourceBufferStream::OnlySelectedRangeIsSeeked() const { |
| for (RangeList::const_iterator itr = ranges_.begin(); |
| itr != ranges_.end(); ++itr) { |
| if ((*itr)->HasNextBufferPosition() && (*itr) != selected_range_) |
| return false; |
| } |
| return !selected_range_ || selected_range_->HasNextBufferPosition(); |
| } |
| |
| void SourceBufferStream::UpdateMaxInterbufferDistance( |
| const BufferQueue& buffers) { |
| DCHECK(!buffers.empty()); |
| base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; |
| for (BufferQueue::const_iterator itr = buffers.begin(); |
| itr != buffers.end(); ++itr) { |
| base::TimeDelta current_timestamp = (*itr)->GetDecodeTimestamp(); |
| DCHECK(current_timestamp != kNoTimestamp()); |
| |
| if (prev_timestamp != kNoTimestamp()) { |
| base::TimeDelta interbuffer_distance = current_timestamp - prev_timestamp; |
| if (max_interbuffer_distance_ == kNoTimestamp()) { |
| max_interbuffer_distance_ = interbuffer_distance; |
| } else { |
| max_interbuffer_distance_ = |
| std::max(max_interbuffer_distance_, interbuffer_distance); |
| } |
| } |
| prev_timestamp = current_timestamp; |
| } |
| } |
| |
| void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) { |
| for (BufferQueue::const_iterator itr = buffers.begin(); |
| itr != buffers.end(); ++itr) { |
| (*itr)->SetConfigId(append_config_index_); |
| } |
| } |
| |
| void SourceBufferStream::GarbageCollectIfNeeded() { |
| // Compute size of |ranges_|. |
| int ranges_size = 0; |
| for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) |
| ranges_size += (*itr)->size_in_bytes(); |
| |
| // Return if we're under or at the memory limit. |
| if (ranges_size <= memory_limit_) |
| return; |
| |
| int bytes_to_free = ranges_size - memory_limit_; |
| |
| // Begin deleting after the last appended buffer. |
| int bytes_freed = FreeBuffersAfterLastAppended(bytes_to_free); |
| |
| // Begin deleting from the front. |
| if (bytes_to_free - bytes_freed > 0) |
| bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, false); |
| |
| // Begin deleting from the back. |
| if (bytes_to_free - bytes_freed > 0) |
| FreeBuffers(bytes_to_free - bytes_freed, true); |
| } |
| |
| int SourceBufferStream::FreeBuffersAfterLastAppended(int total_bytes_to_free) { |
| base::TimeDelta next_buffer_timestamp = GetNextBufferTimestamp(); |
| if (last_appended_buffer_timestamp_ == kNoTimestamp() || |
| next_buffer_timestamp == kNoTimestamp() || |
| last_appended_buffer_timestamp_ >= next_buffer_timestamp) { |
| return 0; |
| } |
| |
| base::TimeDelta remove_range_start = last_appended_buffer_timestamp_; |
| if (last_appended_buffer_is_keyframe_) |
| remove_range_start += GetMaxInterbufferDistance(); |
| |
| base::TimeDelta remove_range_start_keyframe = FindKeyframeAfterTimestamp( |
| remove_range_start); |
| if (remove_range_start_keyframe != kNoTimestamp()) |
| remove_range_start = remove_range_start_keyframe; |
| if (remove_range_start >= next_buffer_timestamp) |
| return 0; |
| |
| base::TimeDelta remove_range_end; |
| int bytes_freed = GetRemovalRange( |
| remove_range_start, next_buffer_timestamp, total_bytes_to_free, |
| &remove_range_end); |
| if (bytes_freed > 0) |
| Remove(remove_range_start, remove_range_end, next_buffer_timestamp); |
| return bytes_freed; |
| } |
| |
| int SourceBufferStream::GetRemovalRange( |
| base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, |
| int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { |
| DCHECK(start_timestamp >= base::TimeDelta()) << start_timestamp.InSecondsF(); |
| DCHECK(start_timestamp < end_timestamp) |
| << "start " << start_timestamp.InSecondsF() |
| << ", end " << end_timestamp.InSecondsF(); |
| |
| int bytes_to_free = total_bytes_to_free; |
| int bytes_freed = 0; |
| |
| for (RangeList::iterator itr = ranges_.begin(); |
| itr != ranges_.end() && bytes_to_free > 0; ++itr) { |
| SourceBufferRange* range = *itr; |
| if (range->GetStartTimestamp() >= end_timestamp) |
| break; |
| if (range->GetEndTimestamp() < start_timestamp) |
| continue; |
| |
| int bytes_removed = range->GetRemovalGOP( |
| start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp); |
| bytes_to_free -= bytes_removed; |
| bytes_freed += bytes_removed; |
| } |
| return bytes_freed; |
| } |
| |
| int SourceBufferStream::FreeBuffers(int total_bytes_to_free, |
| bool reverse_direction) { |
| TRACE_EVENT2("mse", "SourceBufferStream::FreeBuffers", |
| "total bytes to free", total_bytes_to_free, |
| "reverse direction", reverse_direction); |
| |
| DCHECK_GT(total_bytes_to_free, 0); |
| int bytes_to_free = total_bytes_to_free; |
| int bytes_freed = 0; |
| |
| // This range will save the last GOP appended to |range_for_next_append_| |
| // if the buffers surrounding it get deleted during garbage collection. |
| SourceBufferRange* new_range_for_append = NULL; |
| |
| while (!ranges_.empty() && bytes_to_free > 0) { |
| SourceBufferRange* current_range = NULL; |
| BufferQueue buffers; |
| int bytes_deleted = 0; |
| |
| if (reverse_direction) { |
| current_range = ranges_.back(); |
| if (current_range->LastGOPContainsNextBufferPosition()) { |
| DCHECK_EQ(current_range, selected_range_); |
| break; |
| } |
| bytes_deleted = current_range->DeleteGOPFromBack(&buffers); |
| } else { |
| current_range = ranges_.front(); |
| if (current_range->FirstGOPContainsNextBufferPosition()) { |
| DCHECK_EQ(current_range, selected_range_); |
| break; |
| } |
| bytes_deleted = current_range->DeleteGOPFromFront(&buffers); |
| } |
| |
| // Check to see if we've just deleted the GOP that was last appended. |
| base::TimeDelta end_timestamp = buffers.back()->GetDecodeTimestamp(); |
| if (end_timestamp == last_appended_buffer_timestamp_) { |
| DCHECK(last_appended_buffer_timestamp_ != kNoTimestamp()); |
| DCHECK(!new_range_for_append); |
| // Create a new range containing these buffers. |
| new_range_for_append = new SourceBufferRange( |
| buffers, kNoTimestamp(), |
| base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, |
| base::Unretained(this))); |
| range_for_next_append_ = ranges_.end(); |
| } else { |
| bytes_to_free -= bytes_deleted; |
| bytes_freed += bytes_deleted; |
| } |
| |
| if (current_range->size_in_bytes() == 0) { |
| DCHECK_NE(current_range, selected_range_); |
| DCHECK(range_for_next_append_ == ranges_.end() || |
| *range_for_next_append_ != current_range); |
| delete current_range; |
| reverse_direction ? ranges_.pop_back() : ranges_.pop_front(); |
| } |
| } |
| |
| // Insert |new_range_for_append| into |ranges_|, if applicable. |
| if (new_range_for_append) { |
| range_for_next_append_ = AddToRanges(new_range_for_append); |
| DCHECK(range_for_next_append_ != ranges_.end()); |
| |
| // Check to see if we need to merge |new_range_for_append| with the range |
| // before or after it. |new_range_for_append| is created whenever the last |
| // GOP appended is encountered, regardless of whether any buffers after it |
| // are ultimately deleted. Merging is necessary if there were no buffers |
| // (or very few buffers) deleted after creating |new_range_for_append|. |
| if (range_for_next_append_ != ranges_.begin()) { |
| RangeList::iterator range_before_next = range_for_next_append_; |
| --range_before_next; |
| MergeWithAdjacentRangeIfNecessary(range_before_next); |
| } |
| MergeWithAdjacentRangeIfNecessary(range_for_next_append_); |
| } |
| return bytes_freed; |
| } |
| |
| bool SourceBufferStream::InsertIntoExistingRange( |
| const RangeList::iterator& range_for_new_buffers_itr, |
| const BufferQueue& new_buffers, BufferQueue* deleted_buffers) { |
| DCHECK(deleted_buffers); |
| |
| SourceBufferRange* range_for_new_buffers = *range_for_new_buffers_itr; |
| |
| bool temporarily_select_range = false; |
| if (!track_buffer_.empty()) { |
| base::TimeDelta tb_timestamp = track_buffer_.back()->GetDecodeTimestamp(); |
| base::TimeDelta seek_timestamp = FindKeyframeAfterTimestamp(tb_timestamp); |
| if (seek_timestamp != kNoTimestamp() && |
| seek_timestamp < new_buffers.front()->GetDecodeTimestamp() && |
| range_for_new_buffers->BelongsToRange(seek_timestamp)) { |
| DCHECK(tb_timestamp < seek_timestamp); |
| DCHECK(!selected_range_); |
| DCHECK(!range_for_new_buffers->HasNextBufferPosition()); |
| |
| // If there are GOPs between the end of the track buffer and the |
| // beginning of the new buffers, then temporarily seek the range |
| // so that the buffers between these two times will be deposited in |
| // |deleted_buffers| as if they were part of the current playback |
| // position. |
| // TODO(acolwell): Figure out a more elegant way to do this. |
| SeekAndSetSelectedRange(range_for_new_buffers, seek_timestamp); |
| temporarily_select_range = true; |
| } |
| } |
| |
| base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; |
| bool prev_is_keyframe = last_appended_buffer_is_keyframe_; |
| base::TimeDelta next_timestamp = new_buffers.front()->GetDecodeTimestamp(); |
| bool next_is_keyframe = new_buffers.front()->IsKeyframe(); |
| |
| if (prev_timestamp != kNoTimestamp() && prev_timestamp != next_timestamp) { |
| // Clean up the old buffers between the last appended buffer and the |
| // beginning of |new_buffers|. |
| DeleteBetween( |
| range_for_new_buffers_itr, prev_timestamp, next_timestamp, true, |
| deleted_buffers); |
| } |
| |
| bool is_exclusive = false; |
| if (prev_timestamp == next_timestamp) { |
| if (!new_media_segment_ && |
| !AllowSameTimestamp(prev_is_keyframe, next_is_keyframe)) { |
| MEDIA_LOG(log_cb_) << "Invalid same timestamp construct detected at time " |
| << prev_timestamp.InSecondsF(); |
| return false; |
| } |
| |
| // Make the delete range exclusive if we are dealing with an allowed same |
| // timestamp situation so that the buffer with the same timestamp that is |
| // already stored in |*range_for_new_buffers_itr| doesn't get deleted. |
| is_exclusive = AllowSameTimestamp(prev_is_keyframe, next_is_keyframe); |
| } |
| |
| // If we cannot append the |new_buffers| to the end of the existing range, |
| // this is either a start overlap or a middle overlap. Delete the buffers |
| // that |new_buffers| overlaps. |
| if (!range_for_new_buffers->CanAppendBuffersToEnd(new_buffers)) { |
| DeleteBetween( |
| range_for_new_buffers_itr, new_buffers.front()->GetDecodeTimestamp(), |
| new_buffers.back()->GetDecodeTimestamp(), is_exclusive, |
| deleted_buffers); |
| } |
| |
| // Restore the range seek state if necessary. |
| if (temporarily_select_range) |
| SetSelectedRange(NULL); |
| |
| range_for_new_buffers->AppendBuffersToEnd(new_buffers); |
| return true; |
| } |
| |
| void SourceBufferStream::DeleteBetween( |
| const RangeList::iterator& range_itr, base::TimeDelta start_timestamp, |
| base::TimeDelta end_timestamp, bool is_range_exclusive, |
| BufferQueue* deleted_buffers) { |
| SourceBufferRange* new_next_range = |
| (*range_itr)->SplitRange(end_timestamp, is_range_exclusive); |
| |
| // Insert the |new_next_range| into |ranges_| after |range|. |
| if (new_next_range) { |
| RangeList::iterator next_range_itr = range_itr; |
| ranges_.insert(++next_range_itr, new_next_range); |
| } |
| |
| BufferQueue saved_buffers; |
| (*range_itr)->TruncateAt(start_timestamp, &saved_buffers, is_range_exclusive); |
| |
| if (selected_range_ != *range_itr) |
| return; |
| |
| DCHECK(deleted_buffers->empty()); |
| *deleted_buffers = saved_buffers; |
| |
| // If the next buffer position has transferred to the split range, set the |
| // selected range accordingly. |
| if (new_next_range && new_next_range->HasNextBufferPosition()) { |
| DCHECK(!(*range_itr)->HasNextBufferPosition()); |
| SetSelectedRange(new_next_range); |
| } else if (!selected_range_->HasNextBufferPosition()) { |
| SetSelectedRange(NULL); |
| } |
| } |
| |
| bool SourceBufferStream::AreAdjacentInSequence( |
| base::TimeDelta first_timestamp, base::TimeDelta second_timestamp) const { |
| return first_timestamp < second_timestamp && |
| second_timestamp <= |
| first_timestamp + ComputeFudgeRoom(GetMaxInterbufferDistance()); |
| } |
| |
| void SourceBufferStream::ResolveCompleteOverlaps( |
| const RangeList::iterator& range_with_new_buffers_itr, |
| BufferQueue* deleted_buffers) { |
| DCHECK(deleted_buffers); |
| |
| SourceBufferRange* range_with_new_buffers = *range_with_new_buffers_itr; |
| RangeList::iterator next_range_itr = range_with_new_buffers_itr; |
| ++next_range_itr; |
| |
| while (next_range_itr != ranges_.end() && |
| range_with_new_buffers->CompletelyOverlaps(**next_range_itr)) { |
| if (*next_range_itr == selected_range_) { |
| DCHECK(deleted_buffers->empty()); |
| selected_range_->DeleteAll(deleted_buffers); |
| } |
| DeleteAndRemoveRange(&next_range_itr); |
| } |
| } |
| |
| void SourceBufferStream::ResolveEndOverlap( |
| const RangeList::iterator& range_with_new_buffers_itr, |
| BufferQueue* deleted_buffers) { |
| DCHECK(deleted_buffers); |
| |
| SourceBufferRange* range_with_new_buffers = *range_with_new_buffers_itr; |
| RangeList::iterator overlapped_range_itr = range_with_new_buffers_itr; |
| ++overlapped_range_itr; |
| |
| if (overlapped_range_itr == ranges_.end() || |
| !range_with_new_buffers->EndOverlaps(**overlapped_range_itr)) { |
| return; |
| } |
| |
| // Split the overlapped range after |range_with_new_buffers|'s last buffer |
| // overlaps. Now |*overlapped_range_itr| contains only the buffers that do not |
| // belong in |ranges_| anymore, and |new_next_range| contains buffers that |
| // go after |range_with_new_buffers| (without overlap). |
| SourceBufferRange* new_next_range = |
| (*overlapped_range_itr)->SplitRange( |
| range_with_new_buffers->GetEndTimestamp(), true); |
| |
| if (selected_range_ == *overlapped_range_itr) { |
| SourceBufferRange* overlapped_range = *overlapped_range_itr; |
| // If the |overlapped_range| transfers its next buffer position to |
| // |new_next_range|, make |new_next_range| the |selected_range_|. |
| if (new_next_range && new_next_range->HasNextBufferPosition()) { |
| DCHECK(!overlapped_range->HasNextBufferPosition()); |
| SetSelectedRange(new_next_range); |
| } else { |
| // The |overlapped_range| still has the current playback position. |
| // Move the buffers for the current playback position in |
| // |overlapped_range| into |deleted_buffers|. |
| DCHECK(overlapped_range->HasNextBufferPosition()); |
| DCHECK(deleted_buffers->empty()); |
| overlapped_range->DeleteAll(deleted_buffers); |
| } |
| } |
| DeleteAndRemoveRange(&overlapped_range_itr); |
| |
| // If there were non-overlapped buffers, add the new range to |ranges_|. |
| if (new_next_range) |
| AddToRanges(new_next_range); |
| } |
| |
| void SourceBufferStream::PruneTrackBuffer(const base::TimeDelta timestamp) { |
| // If we don't have the next timestamp, we don't have anything to delete. |
| if (timestamp == kNoTimestamp()) |
| return; |
| |
| while (!track_buffer_.empty() && |
| track_buffer_.back()->GetDecodeTimestamp() >= timestamp) { |
| track_buffer_.pop_back(); |
| } |
| } |
| |
| void SourceBufferStream::MergeWithAdjacentRangeIfNecessary( |
| const RangeList::iterator& range_with_new_buffers_itr) { |
| DCHECK(range_with_new_buffers_itr != ranges_.end()); |
| |
| SourceBufferRange* range_with_new_buffers = *range_with_new_buffers_itr; |
| RangeList::iterator next_range_itr = range_with_new_buffers_itr; |
| ++next_range_itr; |
| |
| if (next_range_itr == ranges_.end() || |
| !range_with_new_buffers->CanAppendRangeToEnd(**next_range_itr)) { |
| return; |
| } |
| |
| bool transfer_current_position = selected_range_ == *next_range_itr; |
| range_with_new_buffers->AppendRangeToEnd(**next_range_itr, |
| transfer_current_position); |
| // Update |selected_range_| pointer if |range| has become selected after |
| // merges. |
| if (transfer_current_position) |
| SetSelectedRange(range_with_new_buffers); |
| |
| if (next_range_itr == range_for_next_append_) |
| range_for_next_append_ = range_with_new_buffers_itr; |
| |
| DeleteAndRemoveRange(&next_range_itr); |
| } |
| |
| void SourceBufferStream::Seek(base::TimeDelta timestamp) { |
| DCHECK(timestamp >= base::TimeDelta()); |
| ResetSeekState(); |
| |
| if (ShouldSeekToStartOfBuffered(timestamp)) { |
| ranges_.front()->SeekToStart(); |
| SetSelectedRange(ranges_.front()); |
| seek_pending_ = false; |
| return; |
| } |
| |
| seek_buffer_timestamp_ = timestamp; |
| seek_pending_ = true; |
| |
| RangeList::iterator itr = ranges_.end(); |
| for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { |
| if ((*itr)->CanSeekTo(timestamp)) |
| break; |
| } |
| |
| if (itr == ranges_.end()) |
| return; |
| |
| SeekAndSetSelectedRange(*itr, timestamp); |
| seek_pending_ = false; |
| } |
| |
| bool SourceBufferStream::IsSeekPending() const { |
| return !(end_of_stream_ && IsEndSelected()) && seek_pending_; |
| } |
| |
| void SourceBufferStream::OnSetDuration(base::TimeDelta duration) { |
| RangeList::iterator itr = ranges_.end(); |
| for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { |
| if ((*itr)->GetEndTimestamp() > duration) |
| break; |
| } |
| if (itr == ranges_.end()) |
| return; |
| |
| // Need to partially truncate this range. |
| if ((*itr)->GetStartTimestamp() < duration) { |
| (*itr)->TruncateAt(duration, NULL, false); |
| ++itr; |
| } |
| |
| // Delete all ranges that begin after |duration|. |
| while (itr != ranges_.end()) { |
| // If we're about to delete the selected range, also reset the seek state. |
| DCHECK((*itr)->GetStartTimestamp() >= duration); |
| if (*itr == selected_range_) |
| ResetSeekState(); |
| DeleteAndRemoveRange(&itr); |
| } |
| } |
| |
| SourceBufferStream::Status SourceBufferStream::GetNextBuffer( |
| scoped_refptr<StreamParserBuffer>* out_buffer) { |
| CHECK(!config_change_pending_); |
| |
| if (!track_buffer_.empty()) { |
| DCHECK(!selected_range_); |
| if (track_buffer_.front()->GetConfigId() != current_config_index_) { |
| config_change_pending_ = true; |
| DVLOG(1) << "Config change (track buffer config ID does not match)."; |
| return kConfigChange; |
| } |
| |
| *out_buffer = track_buffer_.front(); |
| track_buffer_.pop_front(); |
| last_output_buffer_timestamp_ = (*out_buffer)->GetDecodeTimestamp(); |
| |
| // If the track buffer becomes empty, then try to set the selected range |
| // based on the timestamp of this buffer being returned. |
| if (track_buffer_.empty()) |
| SetSelectedRangeIfNeeded(last_output_buffer_timestamp_); |
| |
| return kSuccess; |
| } |
| |
| if (!selected_range_ || !selected_range_->HasNextBuffer()) { |
| if (end_of_stream_ && IsEndSelected()) |
| return kEndOfStream; |
| return kNeedBuffer; |
| } |
| |
| if (selected_range_->GetNextConfigId() != current_config_index_) { |
| config_change_pending_ = true; |
| DVLOG(1) << "Config change (selected range config ID does not match)."; |
| return kConfigChange; |
| } |
| |
| CHECK(selected_range_->GetNextBuffer(out_buffer)); |
| last_output_buffer_timestamp_ = (*out_buffer)->GetDecodeTimestamp(); |
| return kSuccess; |
| } |
| |
| base::TimeDelta SourceBufferStream::GetNextBufferTimestamp() { |
| if (!track_buffer_.empty()) |
| return track_buffer_.front()->GetDecodeTimestamp(); |
| |
| if (!selected_range_) |
| return kNoTimestamp(); |
| |
| DCHECK(selected_range_->HasNextBufferPosition()); |
| return selected_range_->GetNextTimestamp(); |
| } |
| |
| base::TimeDelta SourceBufferStream::GetEndBufferTimestamp() { |
| if (!selected_range_) |
| return kNoTimestamp(); |
| return selected_range_->GetEndTimestamp(); |
| } |
| |
| SourceBufferStream::RangeList::iterator |
| SourceBufferStream::FindExistingRangeFor(base::TimeDelta start_timestamp) { |
| for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) { |
| if ((*itr)->BelongsToRange(start_timestamp)) |
| return itr; |
| } |
| return ranges_.end(); |
| } |
| |
| SourceBufferStream::RangeList::iterator |
| SourceBufferStream::AddToRanges(SourceBufferRange* new_range) { |
| base::TimeDelta start_timestamp = new_range->GetStartTimestamp(); |
| RangeList::iterator itr = ranges_.end(); |
| for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { |
| if ((*itr)->GetStartTimestamp() > start_timestamp) |
| break; |
| } |
| return ranges_.insert(itr, new_range); |
| } |
| |
| SourceBufferStream::RangeList::iterator |
| SourceBufferStream::GetSelectedRangeItr() { |
| DCHECK(selected_range_); |
| RangeList::iterator itr = ranges_.end(); |
| for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { |
| if (*itr == selected_range_) |
| break; |
| } |
| DCHECK(itr != ranges_.end()); |
| return itr; |
| } |
| |
| void SourceBufferStream::SeekAndSetSelectedRange( |
| SourceBufferRange* range, base::TimeDelta seek_timestamp) { |
| if (range) |
| range->Seek(seek_timestamp); |
| SetSelectedRange(range); |
| } |
| |
| void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) { |
| if (selected_range_) |
| selected_range_->ResetNextBufferPosition(); |
| DCHECK(!range || range->HasNextBufferPosition()); |
| selected_range_ = range; |
| } |
| |
| Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const { |
| Ranges<base::TimeDelta> ranges; |
| for (RangeList::const_iterator itr = ranges_.begin(); |
| itr != ranges_.end(); ++itr) { |
| ranges.Add((*itr)->GetStartTimestamp(), (*itr)->GetBufferedEndTimestamp()); |
| } |
| return ranges; |
| } |
| |
| void SourceBufferStream::MarkEndOfStream() { |
| DCHECK(!end_of_stream_); |
| end_of_stream_ = true; |
| } |
| |
| void SourceBufferStream::UnmarkEndOfStream() { |
| DCHECK(end_of_stream_); |
| end_of_stream_ = false; |
| } |
| |
| bool SourceBufferStream::IsEndSelected() const { |
| if (ranges_.empty()) |
| return true; |
| |
| if (seek_pending_) |
| return seek_buffer_timestamp_ >= ranges_.back()->GetBufferedEndTimestamp(); |
| |
| return selected_range_ == ranges_.back(); |
| } |
| |
| const AudioDecoderConfig& SourceBufferStream::GetCurrentAudioDecoderConfig() { |
| if (config_change_pending_) |
| CompleteConfigChange(); |
| return audio_configs_[current_config_index_]; |
| } |
| |
| const VideoDecoderConfig& SourceBufferStream::GetCurrentVideoDecoderConfig() { |
| if (config_change_pending_) |
| CompleteConfigChange(); |
| return video_configs_[current_config_index_]; |
| } |
| |
| base::TimeDelta SourceBufferStream::GetMaxInterbufferDistance() const { |
| if (max_interbuffer_distance_ == kNoTimestamp()) |
| return base::TimeDelta::FromMilliseconds(kDefaultBufferDurationInMs); |
| return max_interbuffer_distance_; |
| } |
| |
| bool SourceBufferStream::UpdateAudioConfig(const AudioDecoderConfig& config) { |
| DCHECK(!audio_configs_.empty()); |
| DCHECK(video_configs_.empty()); |
| DVLOG(3) << "UpdateAudioConfig."; |
| |
| if (audio_configs_[0].codec() != config.codec()) { |
| MEDIA_LOG(log_cb_) << "Audio codec changes not allowed."; |
| return false; |
| } |
| |
| if (audio_configs_[0].samples_per_second() != config.samples_per_second()) { |
| MEDIA_LOG(log_cb_) << "Audio sample rate changes not allowed."; |
| return false; |
| } |
| |
| if (audio_configs_[0].channel_layout() != config.channel_layout()) { |
| MEDIA_LOG(log_cb_) << "Audio channel layout changes not allowed."; |
| return false; |
| } |
| |
| if (audio_configs_[0].bits_per_channel() != config.bits_per_channel()) { |
| MEDIA_LOG(log_cb_) << "Audio bits per channel changes not allowed."; |
| return false; |
| } |
| |
| if (audio_configs_[0].is_encrypted() != config.is_encrypted()) { |
| MEDIA_LOG(log_cb_) << "Audio encryption changes not allowed."; |
| return false; |
| } |
| |
| // Check to see if the new config matches an existing one. |
| for (size_t i = 0; i < audio_configs_.size(); ++i) { |
| if (config.Matches(audio_configs_[i])) { |
| append_config_index_ = i; |
| return true; |
| } |
| } |
| |
| // No matches found so let's add this one to the list. |
| append_config_index_ = audio_configs_.size(); |
| DVLOG(2) << "New audio config - index: " << append_config_index_; |
| audio_configs_.resize(audio_configs_.size() + 1); |
| audio_configs_[append_config_index_] = config; |
| return true; |
| } |
| |
| bool SourceBufferStream::UpdateVideoConfig(const VideoDecoderConfig& config) { |
| DCHECK(!video_configs_.empty()); |
| DCHECK(audio_configs_.empty()); |
| DVLOG(3) << "UpdateVideoConfig."; |
| |
| if (video_configs_[0].is_encrypted() != config.is_encrypted()) { |
| MEDIA_LOG(log_cb_) << "Video Encryption changes not allowed."; |
| return false; |
| } |
| |
| if (video_configs_[0].codec() != config.codec()) { |
| MEDIA_LOG(log_cb_) << "Video codec changes not allowed."; |
| return false; |
| } |
| |
| if (video_configs_[0].is_encrypted() != config.is_encrypted()) { |
| MEDIA_LOG(log_cb_) << "Video encryption changes not allowed."; |
| return false; |
| } |
| |
| // Check to see if the new config matches an existing one. |
| for (size_t i = 0; i < video_configs_.size(); ++i) { |
| if (config.Matches(video_configs_[i])) { |
| append_config_index_ = i; |
| return true; |
| } |
| } |
| |
| // No matches found so let's add this one to the list. |
| append_config_index_ = video_configs_.size(); |
| DVLOG(2) << "New video config - index: " << append_config_index_; |
| video_configs_.resize(video_configs_.size() + 1); |
| video_configs_[append_config_index_] = config; |
| return true; |
| } |
| |
| void SourceBufferStream::CompleteConfigChange() { |
| config_change_pending_ = false; |
| |
| if (!track_buffer_.empty()) { |
| current_config_index_ = track_buffer_.front()->GetConfigId(); |
| return; |
| } |
| |
| if (selected_range_ && selected_range_->HasNextBuffer()) |
| current_config_index_ = selected_range_->GetNextConfigId(); |
| } |
| |
| void SourceBufferStream::SetSelectedRangeIfNeeded( |
| const base::TimeDelta timestamp) { |
| DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")"; |
| |
| if (selected_range_) { |
| DCHECK(track_buffer_.empty()); |
| return; |
| } |
| |
| if (!track_buffer_.empty()) { |
| DCHECK(!selected_range_); |
| return; |
| } |
| |
| base::TimeDelta start_timestamp = timestamp; |
| |
| // If the next buffer timestamp is not known then use a timestamp just after |
| // the timestamp on the last buffer returned by GetNextBuffer(). |
| if (start_timestamp == kNoTimestamp()) { |
| if (last_output_buffer_timestamp_ == kNoTimestamp()) |
| return; |
| |
| start_timestamp = last_output_buffer_timestamp_ + |
| base::TimeDelta::FromInternalValue(1); |
| } |
| |
| base::TimeDelta seek_timestamp = |
| FindNewSelectedRangeSeekTimestamp(start_timestamp); |
| |
| // If we don't have buffered data to seek to, then return. |
| if (seek_timestamp == kNoTimestamp()) |
| return; |
| |
| DCHECK(track_buffer_.empty()); |
| SeekAndSetSelectedRange(*FindExistingRangeFor(seek_timestamp), |
| seek_timestamp); |
| } |
| |
| base::TimeDelta SourceBufferStream::FindNewSelectedRangeSeekTimestamp( |
| const base::TimeDelta start_timestamp) { |
| DCHECK(start_timestamp != kNoTimestamp()); |
| DCHECK(start_timestamp >= base::TimeDelta()); |
| |
| RangeList::iterator itr = ranges_.begin(); |
| |
| for (; itr != ranges_.end(); ++itr) { |
| if ((*itr)->GetEndTimestamp() >= start_timestamp) { |
| break; |
| } |
| } |
| |
| if (itr == ranges_.end()) |
| return kNoTimestamp(); |
| |
| // First check for a keyframe timestamp >= |start_timestamp| |
| // in the current range. |
| base::TimeDelta keyframe_timestamp = |
| (*itr)->NextKeyframeTimestamp(start_timestamp); |
| |
| if (keyframe_timestamp != kNoTimestamp()) |
| return keyframe_timestamp; |
| |
| // If a keyframe was not found then look for a keyframe that is |
| // "close enough" in the current or next range. |
| base::TimeDelta end_timestamp = |
| start_timestamp + ComputeFudgeRoom(GetMaxInterbufferDistance()); |
| DCHECK(start_timestamp < end_timestamp); |
| |
| // Make sure the current range doesn't start beyond |end_timestamp|. |
| if ((*itr)->GetStartTimestamp() >= end_timestamp) |
| return kNoTimestamp(); |
| |
| keyframe_timestamp = (*itr)->KeyframeBeforeTimestamp(end_timestamp); |
| |
| // Check to see if the keyframe is within the acceptable range |
| // (|start_timestamp|, |end_timestamp|]. |
| if (keyframe_timestamp != kNoTimestamp() && |
| start_timestamp < keyframe_timestamp && |
| keyframe_timestamp <= end_timestamp) { |
| return keyframe_timestamp; |
| } |
| |
| // If |end_timestamp| is within this range, then no other checks are |
| // necessary. |
| if (end_timestamp <= (*itr)->GetEndTimestamp()) |
| return kNoTimestamp(); |
| |
| // Move on to the next range. |
| ++itr; |
| |
| // Return early if the next range does not contain |end_timestamp|. |
| if (itr == ranges_.end() || (*itr)->GetStartTimestamp() >= end_timestamp) |
| return kNoTimestamp(); |
| |
| keyframe_timestamp = (*itr)->KeyframeBeforeTimestamp(end_timestamp); |
| |
| // Check to see if the keyframe is within the acceptable range |
| // (|start_timestamp|, |end_timestamp|]. |
| if (keyframe_timestamp != kNoTimestamp() && |
| start_timestamp < keyframe_timestamp && |
| keyframe_timestamp <= end_timestamp) { |
| return keyframe_timestamp; |
| } |
| |
| return kNoTimestamp(); |
| } |
| |
| base::TimeDelta SourceBufferStream::FindKeyframeAfterTimestamp( |
| const base::TimeDelta timestamp) { |
| DCHECK(timestamp != kNoTimestamp()); |
| |
| RangeList::iterator itr = FindExistingRangeFor(timestamp); |
| |
| if (itr == ranges_.end()) |
| return kNoTimestamp(); |
| |
| // First check for a keyframe timestamp >= |timestamp| |
| // in the current range. |
| return (*itr)->NextKeyframeTimestamp(timestamp); |
| } |
| |
| std::string SourceBufferStream::GetStreamTypeName() const { |
| if (!video_configs_.empty()) { |
| DCHECK(audio_configs_.empty()); |
| return "VIDEO"; |
| } |
| |
| DCHECK(!audio_configs_.empty()); |
| return "AUDIO"; |
| } |
| |
| void SourceBufferStream::DeleteAndRemoveRange(RangeList::iterator* itr) { |
| DCHECK(*itr != ranges_.end()); |
| if (**itr == selected_range_) |
| SetSelectedRange(NULL); |
| |
| delete **itr; |
| *itr = ranges_.erase(*itr); |
| } |
| |
| SourceBufferRange::SourceBufferRange( |
| const BufferQueue& new_buffers, base::TimeDelta media_segment_start_time, |
| const InterbufferDistanceCB& interbuffer_distance_cb) |
| : keyframe_map_index_base_(0), |
| next_buffer_index_(-1), |
| media_segment_start_time_(media_segment_start_time), |
| interbuffer_distance_cb_(interbuffer_distance_cb), |
| size_in_bytes_(0) { |
| DCHECK(!new_buffers.empty()); |
| DCHECK(new_buffers.front()->IsKeyframe()); |
| DCHECK(!interbuffer_distance_cb.is_null()); |
| AppendBuffersToEnd(new_buffers); |
| } |
| |
| void SourceBufferRange::AppendBuffersToEnd(const BufferQueue& new_buffers) { |
| DCHECK(buffers_.empty() || |
| buffers_.back()->GetDecodeTimestamp() <= |
| new_buffers.front()->GetDecodeTimestamp()); |
| |
| for (BufferQueue::const_iterator itr = new_buffers.begin(); |
| itr != new_buffers.end(); ++itr) { |
| DCHECK((*itr)->GetDecodeTimestamp() != kNoTimestamp()); |
| buffers_.push_back(*itr); |
| size_in_bytes_ += (*itr)->data_size(); |
| |
| if ((*itr)->IsKeyframe()) { |
| keyframe_map_.insert( |
| std::make_pair((*itr)->GetDecodeTimestamp(), |
| buffers_.size() - 1 + keyframe_map_index_base_)); |
| } |
| } |
| } |
| |
| void SourceBufferRange::Seek(base::TimeDelta timestamp) { |
| DCHECK(CanSeekTo(timestamp)); |
| DCHECK(!keyframe_map_.empty()); |
| |
| KeyframeMap::iterator result = GetFirstKeyframeBefore(timestamp); |
| next_buffer_index_ = result->second - keyframe_map_index_base_; |
| DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size())); |
| } |
| |
| void SourceBufferRange::SeekAheadTo(base::TimeDelta timestamp) { |
| SeekAhead(timestamp, false); |
| } |
| |
| void SourceBufferRange::SeekAheadPast(base::TimeDelta timestamp) { |
| SeekAhead(timestamp, true); |
| } |
| |
| void SourceBufferRange::SeekAhead(base::TimeDelta timestamp, |
| bool skip_given_timestamp) { |
| DCHECK(!keyframe_map_.empty()); |
| |
| KeyframeMap::iterator result = |
| GetFirstKeyframeAt(timestamp, skip_given_timestamp); |
| |
| // If there isn't a keyframe after |timestamp|, then seek to end and return |
| // kNoTimestamp to signal such. |
| if (result == keyframe_map_.end()) { |
| next_buffer_index_ = -1; |
| return; |
| } |
| next_buffer_index_ = result->second - keyframe_map_index_base_; |
| DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size())); |
| } |
| |
| void SourceBufferRange::SeekToStart() { |
| DCHECK(!buffers_.empty()); |
| next_buffer_index_ = 0; |
| } |
| |
| SourceBufferRange* SourceBufferRange::SplitRange( |
| base::TimeDelta timestamp, bool is_exclusive) { |
| // Find the first keyframe after |timestamp|. If |is_exclusive|, do not |
| // include keyframes at |timestamp|. |
| KeyframeMap::iterator new_beginning_keyframe = |
| GetFirstKeyframeAt(timestamp, is_exclusive); |
| |
| // If there is no keyframe after |timestamp|, we can't split the range. |
| if (new_beginning_keyframe == keyframe_map_.end()) |
| return NULL; |
| |
| // Remove the data beginning at |keyframe_index| from |buffers_| and save it |
| // into |removed_buffers|. |
| int keyframe_index = |
| new_beginning_keyframe->second - keyframe_map_index_base_; |
| DCHECK_LT(keyframe_index, static_cast<int>(buffers_.size())); |
| BufferQueue::iterator starting_point = buffers_.begin() + keyframe_index; |
| BufferQueue removed_buffers(starting_point, buffers_.end()); |
| keyframe_map_.erase(new_beginning_keyframe, keyframe_map_.end()); |
| FreeBufferRange(starting_point, buffers_.end()); |
| |
| // Create a new range with |removed_buffers|. |
| SourceBufferRange* split_range = |
| new SourceBufferRange( |
| removed_buffers, kNoTimestamp(), interbuffer_distance_cb_); |
| |
| // If the next buffer position is now in |split_range|, update the state of |
| // this range and |split_range| accordingly. |
| if (next_buffer_index_ >= static_cast<int>(buffers_.size())) { |
| split_range->next_buffer_index_ = next_buffer_index_ - keyframe_index; |
| ResetNextBufferPosition(); |
| } |
| |
| return split_range; |
| } |
| |
| SourceBufferRange::BufferQueue::iterator SourceBufferRange::GetBufferItrAt( |
| base::TimeDelta timestamp, bool skip_given_timestamp) { |
| // Need to make a dummy buffer with timestamp |timestamp| in order to search |
| // the |buffers_| container. |
| scoped_refptr<StreamParserBuffer> dummy_buffer = |
| StreamParserBuffer::CopyFrom(NULL, 0, false); |
| dummy_buffer->SetDecodeTimestamp(timestamp); |
| |
| if (skip_given_timestamp) { |
| return std::upper_bound( |
| buffers_.begin(), buffers_.end(), dummy_buffer, BufferComparator); |
| } |
| return std::lower_bound( |
| buffers_.begin(), buffers_.end(), dummy_buffer, BufferComparator); |
| } |
| |
| SourceBufferRange::KeyframeMap::iterator |
| SourceBufferRange::GetFirstKeyframeAt(base::TimeDelta timestamp, |
| bool skip_given_timestamp) { |
| return skip_given_timestamp ? |
| keyframe_map_.upper_bound(timestamp) : |
| keyframe_map_.lower_bound(timestamp); |
| } |
| |
| SourceBufferRange::KeyframeMap::iterator |
| SourceBufferRange::GetFirstKeyframeBefore(base::TimeDelta timestamp) { |
| KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp); |
| // lower_bound() returns the first element >= |timestamp|, so we want the |
| // previous element if it did not return the element exactly equal to |
| // |timestamp|. |
| if (result != keyframe_map_.begin() && |
| (result == keyframe_map_.end() || result->first != timestamp)) { |
| --result; |
| } |
| return result; |
| } |
| |
| void SourceBufferRange::DeleteAll(BufferQueue* removed_buffers) { |
| TruncateAt(buffers_.begin(), removed_buffers); |
| } |
| |
| void SourceBufferRange::TruncateAt( |
| base::TimeDelta timestamp, BufferQueue* removed_buffers, |
| bool is_exclusive) { |
| // Find the place in |buffers_| where we will begin deleting data. |
| BufferQueue::iterator starting_point = |
| GetBufferItrAt(timestamp, is_exclusive); |
| TruncateAt(starting_point, removed_buffers); |
| } |
| |
| int SourceBufferRange::DeleteGOPFromFront(BufferQueue* deleted_buffers) { |
| DCHECK(!FirstGOPContainsNextBufferPosition()); |
| DCHECK(deleted_buffers); |
| |
| int buffers_deleted = 0; |
| int total_bytes_deleted = 0; |
| |
| KeyframeMap::iterator front = keyframe_map_.begin(); |
| DCHECK(front != keyframe_map_.end()); |
| |
| // Delete the keyframe at the start of |keyframe_map_|. |
| keyframe_map_.erase(front); |
| |
| // Now we need to delete all the buffers that depend on the keyframe we've |
| // just deleted. |
| int end_index = keyframe_map_.size() > 0 ? |
| keyframe_map_.begin()->second - keyframe_map_index_base_ : |
| buffers_.size(); |
| |
| // Delete buffers from the beginning of the buffered range up until (but not |
| // including) the next keyframe. |
| for (int i = 0; i < end_index; i++) { |
| int bytes_deleted = buffers_.front()->data_size(); |
| size_in_bytes_ -= bytes_deleted; |
| total_bytes_deleted += bytes_deleted; |
| deleted_buffers->push_back(buffers_.front()); |
| buffers_.pop_front(); |
| ++buffers_deleted; |
| } |
| |
| // Update |keyframe_map_index_base_| to account for the deleted buffers. |
| keyframe_map_index_base_ += buffers_deleted; |
| |
| if (next_buffer_index_ > -1) { |
| next_buffer_index_ -= buffers_deleted; |
| DCHECK_GE(next_buffer_index_, 0); |
| } |
| |
| // Invalidate media segment start time if we've deleted the first buffer of |
| // the range. |
| if (buffers_deleted > 0) |
| media_segment_start_time_ = kNoTimestamp(); |
| |
| return total_bytes_deleted; |
| } |
| |
| int SourceBufferRange::DeleteGOPFromBack(BufferQueue* deleted_buffers) { |
| DCHECK(!LastGOPContainsNextBufferPosition()); |
| DCHECK(deleted_buffers); |
| |
| // Remove the last GOP's keyframe from the |keyframe_map_|. |
| KeyframeMap::iterator back = keyframe_map_.end(); |
| DCHECK_GT(keyframe_map_.size(), 0u); |
| --back; |
| |
| // The index of the first buffer in the last GOP is equal to the new size of |
| // |buffers_| after that GOP is deleted. |
| size_t goal_size = back->second - keyframe_map_index_base_; |
| keyframe_map_.erase(back); |
| |
| int total_bytes_deleted = 0; |
| while (buffers_.size() != goal_size) { |
| int bytes_deleted = buffers_.back()->data_size(); |
| size_in_bytes_ -= bytes_deleted; |
| total_bytes_deleted += bytes_deleted; |
| // We're removing buffers from the back, so push each removed buffer to the |
| // front of |deleted_buffers| so that |deleted_buffers| are in nondecreasing |
| // order. |
| deleted_buffers->push_front(buffers_.back()); |
| buffers_.pop_back(); |
| } |
| |
| return total_bytes_deleted; |
| } |
| |
| int SourceBufferRange::GetRemovalGOP( |
| base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, |
| int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { |
| int bytes_to_free = total_bytes_to_free; |
| int bytes_removed = 0; |
| |
| KeyframeMap::iterator gop_itr = GetFirstKeyframeAt(start_timestamp, false); |
| if (gop_itr == keyframe_map_.end()) |
| return 0; |
| int keyframe_index = gop_itr->second - keyframe_map_index_base_; |
| BufferQueue::iterator buffer_itr = buffers_.begin() + keyframe_index; |
| KeyframeMap::iterator gop_end = keyframe_map_.end(); |
| if (end_timestamp < GetBufferedEndTimestamp()) |
| gop_end = GetFirstKeyframeBefore(end_timestamp); |
| |
| // Check if the removal range is within a GOP and skip the loop if so. |
| // [keyframe]...[start_timestamp]...[end_timestamp]...[keyframe] |
| KeyframeMap::iterator gop_itr_prev = gop_itr; |
| if (gop_itr_prev != keyframe_map_.begin() && --gop_itr_prev == gop_end) |
| gop_end = gop_itr; |
| |
| while (gop_itr != gop_end && bytes_to_free > 0) { |
| ++gop_itr; |
| |
| int gop_size = 0; |
| int next_gop_index = gop_itr == keyframe_map_.end() ? |
| buffers_.size() : gop_itr->second - keyframe_map_index_base_; |
| BufferQueue::iterator next_gop_start = buffers_.begin() + next_gop_index; |
| for (; buffer_itr != next_gop_start; ++buffer_itr) |
| gop_size += (*buffer_itr)->data_size(); |
| |
| bytes_removed += gop_size; |
| bytes_to_free -= gop_size; |
| } |
| if (bytes_removed > 0) { |
| *removal_end_timestamp = gop_itr == keyframe_map_.end() ? |
| GetBufferedEndTimestamp() : gop_itr->first; |
| } |
| return bytes_removed; |
| } |
| |
| bool SourceBufferRange::FirstGOPContainsNextBufferPosition() const { |
| if (!HasNextBufferPosition()) |
| return false; |
| |
| // If there is only one GOP, it must contain the next buffer position. |
| if (keyframe_map_.size() == 1u) |
| return true; |
| |
| KeyframeMap::const_iterator second_gop = keyframe_map_.begin(); |
| ++second_gop; |
| return next_buffer_index_ < second_gop->second - keyframe_map_index_base_; |
| } |
| |
| bool SourceBufferRange::LastGOPContainsNextBufferPosition() const { |
| if (!HasNextBufferPosition()) |
| return false; |
| |
| // If there is only one GOP, it must contain the next buffer position. |
| if (keyframe_map_.size() == 1u) |
| return true; |
| |
| KeyframeMap::const_iterator last_gop = keyframe_map_.end(); |
| --last_gop; |
| return last_gop->second - keyframe_map_index_base_ <= next_buffer_index_; |
| } |
| |
| void SourceBufferRange::FreeBufferRange( |
| const BufferQueue::iterator& starting_point, |
| const BufferQueue::iterator& ending_point) { |
| for (BufferQueue::iterator itr = starting_point; |
| itr != ending_point; ++itr) { |
| size_in_bytes_ -= (*itr)->data_size(); |
| DCHECK_GE(size_in_bytes_, 0); |
| } |
| buffers_.erase(starting_point, ending_point); |
| } |
| |
| void SourceBufferRange::TruncateAt( |
| const BufferQueue::iterator& starting_point, BufferQueue* removed_buffers) { |
| DCHECK(!removed_buffers || removed_buffers->empty()); |
| |
| // Return if we're not deleting anything. |
| if (starting_point == buffers_.end()) |
| return; |
| |
| // Reset the next buffer index if we will be deleting the buffer that's next |
| // in sequence. |
| if (HasNextBufferPosition()) { |
| base::TimeDelta next_buffer_timestamp = GetNextTimestamp(); |
| if (next_buffer_timestamp == kNoTimestamp() || |
| next_buffer_timestamp >= (*starting_point)->GetDecodeTimestamp()) { |
| if (HasNextBuffer() && removed_buffers) { |
| int starting_offset = starting_point - buffers_.begin(); |
| int next_buffer_offset = next_buffer_index_ - starting_offset; |
| DCHECK_GE(next_buffer_offset, 0); |
| BufferQueue saved(starting_point + next_buffer_offset, buffers_.end()); |
| removed_buffers->swap(saved); |
| } |
| ResetNextBufferPosition(); |
| } |
| } |
| |
| // Remove keyframes from |starting_point| onward. |
| KeyframeMap::iterator starting_point_keyframe = |
| keyframe_map_.lower_bound((*starting_point)->GetDecodeTimestamp()); |
| keyframe_map_.erase(starting_point_keyframe, keyframe_map_.end()); |
| |
| // Remove everything from |starting_point| onward. |
| FreeBufferRange(starting_point, buffers_.end()); |
| } |
| |
| bool SourceBufferRange::GetNextBuffer( |
| scoped_refptr<StreamParserBuffer>* out_buffer) { |
| if (!HasNextBuffer()) |
| return false; |
| |
| *out_buffer = buffers_.at(next_buffer_index_); |
| next_buffer_index_++; |
| return true; |
| } |
| |
| bool SourceBufferRange::HasNextBuffer() const { |
| return next_buffer_index_ >= 0 && |
| next_buffer_index_ < static_cast<int>(buffers_.size()); |
| } |
| |
| int SourceBufferRange::GetNextConfigId() const { |
| DCHECK(HasNextBuffer()); |
| return buffers_.at(next_buffer_index_)->GetConfigId(); |
| } |
| |
| base::TimeDelta SourceBufferRange::GetNextTimestamp() const { |
| DCHECK(!buffers_.empty()); |
| DCHECK(HasNextBufferPosition()); |
| |
| if (next_buffer_index_ >= static_cast<int>(buffers_.size())) { |
| return kNoTimestamp(); |
| } |
| |
| return buffers_.at(next_buffer_index_)->GetDecodeTimestamp(); |
| } |
| |
| bool SourceBufferRange::HasNextBufferPosition() const { |
| return next_buffer_index_ >= 0; |
| } |
| |
| void SourceBufferRange::ResetNextBufferPosition() { |
| next_buffer_index_ = -1; |
| } |
| |
| void SourceBufferRange::AppendRangeToEnd(const SourceBufferRange& range, |
| bool transfer_current_position) { |
| DCHECK(CanAppendRangeToEnd(range)); |
| DCHECK(!buffers_.empty()); |
| |
| if (transfer_current_position && range.next_buffer_index_ >= 0) |
| next_buffer_index_ = range.next_buffer_index_ + buffers_.size(); |
| |
| AppendBuffersToEnd(range.buffers_); |
| } |
| |
| bool SourceBufferRange::CanAppendRangeToEnd( |
| const SourceBufferRange& range) const { |
| return CanAppendBuffersToEnd(range.buffers_); |
| } |
| |
| bool SourceBufferRange::CanAppendBuffersToEnd( |
| const BufferQueue& buffers) const { |
| DCHECK(!buffers_.empty()); |
| return IsNextInSequence(buffers_.back(), |
| buffers.front()->GetDecodeTimestamp()); |
| } |
| |
| bool SourceBufferRange::BelongsToRange(base::TimeDelta timestamp) const { |
| DCHECK(!buffers_.empty()); |
| |
| return (IsNextInSequence(buffers_.back(), timestamp) || |
| (GetStartTimestamp() <= timestamp && timestamp <= GetEndTimestamp())); |
| } |
| |
| bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const { |
| base::TimeDelta start_timestamp = |
| std::max(base::TimeDelta(), GetStartTimestamp() - GetFudgeRoom()); |
| return !keyframe_map_.empty() && start_timestamp <= timestamp && |
| timestamp < GetBufferedEndTimestamp(); |
| } |
| |
| bool SourceBufferRange::CompletelyOverlaps( |
| const SourceBufferRange& range) const { |
| return GetStartTimestamp() <= range.GetStartTimestamp() && |
| GetEndTimestamp() >= range.GetEndTimestamp(); |
| } |
| |
| bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const { |
| return range.GetStartTimestamp() <= GetEndTimestamp() && |
| GetEndTimestamp() < range.GetEndTimestamp(); |
| } |
| |
| base::TimeDelta SourceBufferRange::GetStartTimestamp() const { |
| DCHECK(!buffers_.empty()); |
| base::TimeDelta start_timestamp = media_segment_start_time_; |
| if (start_timestamp == kNoTimestamp()) |
| start_timestamp = buffers_.front()->GetDecodeTimestamp(); |
| return start_timestamp; |
| } |
| |
| base::TimeDelta SourceBufferRange::GetEndTimestamp() const { |
| DCHECK(!buffers_.empty()); |
| return buffers_.back()->GetDecodeTimestamp(); |
| } |
| |
| base::TimeDelta SourceBufferRange::GetBufferedEndTimestamp() const { |
| DCHECK(!buffers_.empty()); |
| base::TimeDelta duration = buffers_.back()->duration(); |
| if (duration == kNoTimestamp() || duration == base::TimeDelta()) |
| duration = GetApproximateDuration(); |
| return GetEndTimestamp() + duration; |
| } |
| |
| base::TimeDelta SourceBufferRange::NextKeyframeTimestamp( |
| base::TimeDelta timestamp) { |
| DCHECK(!keyframe_map_.empty()); |
| |
| if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) |
| return kNoTimestamp(); |
| |
| KeyframeMap::iterator itr = GetFirstKeyframeAt(timestamp, false); |
| if (itr == keyframe_map_.end()) |
| return kNoTimestamp(); |
| return itr->first; |
| } |
| |
| base::TimeDelta SourceBufferRange::KeyframeBeforeTimestamp( |
| base::TimeDelta timestamp) { |
| DCHECK(!keyframe_map_.empty()); |
| |
| if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) |
| return kNoTimestamp(); |
| |
| return GetFirstKeyframeBefore(timestamp)->first; |
| } |
| |
| bool SourceBufferRange::IsNextInSequence( |
| const scoped_refptr<media::StreamParserBuffer>& buffer, |
| base::TimeDelta timestamp) const { |
| return buffer->GetDecodeTimestamp() < timestamp && |
| timestamp <= buffer->GetDecodeTimestamp() + GetFudgeRoom(); |
| } |
| |
| base::TimeDelta SourceBufferRange::GetFudgeRoom() const { |
| return ComputeFudgeRoom(GetApproximateDuration()); |
| } |
| |
| base::TimeDelta SourceBufferRange::GetApproximateDuration() const { |
| base::TimeDelta max_interbuffer_distance = interbuffer_distance_cb_.Run(); |
| DCHECK(max_interbuffer_distance != kNoTimestamp()); |
| return max_interbuffer_distance; |
| } |
| |
| } // namespace media |