| // Copyright 2017 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_range_by_pts.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "media/base/timestamp_constants.h" |
| |
| namespace media { |
| |
| SourceBufferRangeByPts::SourceBufferRangeByPts( |
| GapPolicy gap_policy, |
| const BufferQueue& new_buffers, |
| base::TimeDelta range_start_pts, |
| const InterbufferDistanceCB& interbuffer_distance_cb) |
| : SourceBufferRange(gap_policy, interbuffer_distance_cb), |
| range_start_pts_(range_start_pts), |
| keyframe_map_index_base_(0) { |
| DVLOG(3) << __func__; |
| CHECK(!new_buffers.empty()); |
| DCHECK(new_buffers.front()->is_key_frame()); |
| AppendBuffersToEnd(new_buffers, range_start_pts_); |
| } |
| |
| SourceBufferRangeByPts::~SourceBufferRangeByPts() = default; |
| |
| void SourceBufferRangeByPts::DeleteAll(BufferQueue* deleted_buffers) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| TruncateAt(0u, deleted_buffers); |
| } |
| |
| void SourceBufferRangeByPts::AppendRangeToEnd( |
| const SourceBufferRangeByPts& range, |
| bool transfer_current_position) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| 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_, |
| NextRangeStartTimeForAppendRangeToEnd(range)); |
| } |
| |
| bool SourceBufferRangeByPts::CanAppendRangeToEnd( |
| const SourceBufferRangeByPts& range) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| return CanAppendBuffersToEnd(range.buffers_, |
| NextRangeStartTimeForAppendRangeToEnd(range)); |
| } |
| |
| bool SourceBufferRangeByPts::AllowableAppendAfterEstimatedDuration( |
| const BufferQueue& buffers, |
| base::TimeDelta new_buffers_group_start_pts) const { |
| if (buffers_.empty() || !buffers_.back()->is_duration_estimated() || |
| buffers.empty() || !buffers.front()->is_key_frame()) { |
| return false; |
| } |
| |
| if (new_buffers_group_start_pts == kNoTimestamp) { |
| return GetBufferedEndTimestamp() == buffers.front()->timestamp(); |
| } |
| |
| return GetBufferedEndTimestamp() == new_buffers_group_start_pts; |
| } |
| |
| bool SourceBufferRangeByPts::CanAppendBuffersToEnd( |
| const BufferQueue& buffers, |
| base::TimeDelta new_buffers_group_start_pts) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| if (new_buffers_group_start_pts == kNoTimestamp) { |
| return buffers.front()->is_key_frame() |
| ? (IsNextInPresentationSequence(buffers.front()->timestamp()) || |
| AllowableAppendAfterEstimatedDuration( |
| buffers, new_buffers_group_start_pts)) |
| : IsNextInDecodeSequence(buffers.front()->GetDecodeTimestamp()); |
| } |
| CHECK(buffers.front()->is_key_frame()); |
| DCHECK(new_buffers_group_start_pts >= GetEndTimestamp()); |
| DCHECK(buffers.front()->timestamp() >= new_buffers_group_start_pts); |
| return IsNextInPresentationSequence(new_buffers_group_start_pts) || |
| AllowableAppendAfterEstimatedDuration(buffers, |
| new_buffers_group_start_pts); |
| } |
| |
| void SourceBufferRangeByPts::AppendBuffersToEnd( |
| const BufferQueue& new_buffers, |
| base::TimeDelta new_buffers_group_start_pts) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| CHECK(buffers_.empty() || |
| CanAppendBuffersToEnd(new_buffers, new_buffers_group_start_pts)); |
| |
| DCHECK(new_buffers_group_start_pts == kNoTimestamp || |
| new_buffers.front()->is_key_frame()) |
| << range_start_pts_ << ", " << new_buffers.front()->is_key_frame(); |
| |
| // TODO(wolenetz): Uncomment this DCHECK once SAP-Type-2 is more fully |
| // supported. It is hit by NewByPts versions of |
| // FrameProcessorTest.OOOKeyframePrecededByDependantNonKeyframeShouldWarn. See |
| // https://crbug.com/718641. |
| // DCHECK(range_start_pts_ == kNoTimestamp || |
| // range_start_pts_ <= new_buffers.front()->timestamp()); |
| |
| AdjustEstimatedDurationForNewAppend(new_buffers); |
| |
| for (BufferQueue::const_iterator itr = new_buffers.begin(); |
| itr != new_buffers.end(); ++itr) { |
| DCHECK((*itr)->timestamp() != kNoTimestamp); |
| DCHECK((*itr)->GetDecodeTimestamp() != kNoDecodeTimestamp()); |
| |
| buffers_.push_back(*itr); |
| UpdateEndTime(*itr); |
| size_in_bytes_ += (*itr)->data_size(); |
| |
| if ((*itr)->is_key_frame()) { |
| keyframe_map_.insert(std::make_pair( |
| (*itr)->timestamp(), buffers_.size() - 1 + keyframe_map_index_base_)); |
| } |
| } |
| |
| DVLOG(4) << __func__ << " Result: " << ToStringForDebugging(); |
| } |
| |
| void SourceBufferRangeByPts::Seek(base::TimeDelta timestamp) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(CanSeekTo(timestamp)); |
| DCHECK(!keyframe_map_.empty()); |
| |
| KeyframeMap::const_iterator result = GetFirstKeyframeAtOrBefore(timestamp); |
| next_buffer_index_ = result->second - keyframe_map_index_base_; |
| CHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size())) |
| << next_buffer_index_ << ", size = " << buffers_.size(); |
| } |
| |
| bool SourceBufferRangeByPts::CanSeekTo(base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| base::TimeDelta start_timestamp = |
| std::max(base::TimeDelta(), GetStartTimestamp() - GetFudgeRoom()); |
| return !keyframe_map_.empty() && start_timestamp <= timestamp && |
| timestamp < GetBufferedEndTimestamp(); |
| } |
| |
| int SourceBufferRangeByPts::GetConfigIdAtTime(base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(CanSeekTo(timestamp)); |
| DCHECK(!keyframe_map_.empty()); |
| |
| KeyframeMap::const_iterator result = GetFirstKeyframeAtOrBefore(timestamp); |
| CHECK(result != keyframe_map_.end()); |
| size_t buffer_index = result->second - keyframe_map_index_base_; |
| CHECK_LT(buffer_index, buffers_.size()) |
| << buffer_index << ", size = " << buffers_.size(); |
| |
| return buffers_[buffer_index]->GetConfigId(); |
| } |
| |
| bool SourceBufferRangeByPts::SameConfigThruRange(base::TimeDelta start, |
| base::TimeDelta end) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(CanSeekTo(start)); |
| DCHECK(CanSeekTo(end)); |
| DCHECK(start <= end); |
| DCHECK(!keyframe_map_.empty()); |
| |
| if (start == end) |
| return true; |
| |
| KeyframeMap::const_iterator result = GetFirstKeyframeAtOrBefore(start); |
| CHECK(result != keyframe_map_.end()); |
| size_t buffer_index = result->second - keyframe_map_index_base_; |
| CHECK_LT(buffer_index, buffers_.size()) |
| << buffer_index << ", size = " << buffers_.size(); |
| |
| int start_config = buffers_[buffer_index]->GetConfigId(); |
| buffer_index++; |
| while (buffer_index < buffers_.size() && |
| buffers_[buffer_index]->timestamp() <= end) { |
| if (buffers_[buffer_index]->GetConfigId() != start_config) |
| return false; |
| buffer_index++; |
| } |
| |
| return true; |
| } |
| |
| std::unique_ptr<SourceBufferRangeByPts> SourceBufferRangeByPts::SplitRange( |
| base::TimeDelta timestamp) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| CHECK(!buffers_.empty()); |
| |
| // Find the first keyframe at or after |timestamp|. |
| KeyframeMap::const_iterator new_beginning_keyframe = |
| GetFirstKeyframeAt(timestamp, false); |
| |
| // If there is no keyframe at or after |timestamp|, we can't split the range. |
| if (new_beginning_keyframe == keyframe_map_.end()) |
| return nullptr; |
| |
| // 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_; |
| CHECK_LT(keyframe_index, static_cast<int>(buffers_.size())); |
| BufferQueue::iterator starting_point = buffers_.begin() + keyframe_index; |
| BufferQueue removed_buffers(starting_point, buffers_.end()); |
| |
| base::TimeDelta new_range_start_pts = |
| std::max(timestamp, GetStartTimestamp()); |
| DCHECK(new_range_start_pts <= removed_buffers.front()->timestamp()); |
| |
| keyframe_map_.erase(new_beginning_keyframe, keyframe_map_.end()); |
| FreeBufferRange(starting_point, buffers_.end()); |
| UpdateEndTimeUsingLastGOP(); |
| |
| // Create a new range with |removed_buffers|. |
| std::unique_ptr<SourceBufferRangeByPts> split_range = |
| std::make_unique<SourceBufferRangeByPts>(gap_policy_, removed_buffers, |
| new_range_start_pts, |
| 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; |
| |
| int split_range_next_buffer_index = split_range->next_buffer_index_; |
| CHECK_GE(split_range_next_buffer_index, 0); |
| // Note that a SourceBufferRange's |next_buffer_index_| can be the index |
| // of a buffer one beyond what is currently in |buffers_|. |
| CHECK_LE(split_range_next_buffer_index, |
| static_cast<int>(split_range->buffers_.size())); |
| |
| ResetNextBufferPosition(); |
| } |
| |
| return split_range; |
| } |
| |
| bool SourceBufferRangeByPts::TruncateAt(base::TimeDelta timestamp, |
| BufferQueue* deleted_buffers, |
| bool is_exclusive) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| // Find the place in |buffers_| where we will begin deleting data, then |
| // truncate from there. |
| return TruncateAt(GetBufferIndexAt(timestamp, is_exclusive), deleted_buffers); |
| } |
| |
| size_t SourceBufferRangeByPts::DeleteGOPFromFront( |
| BufferQueue* deleted_buffers) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| DCHECK(!FirstGOPContainsNextBufferPosition()); |
| DCHECK(deleted_buffers); |
| |
| int buffers_deleted = 0; |
| size_t total_bytes_deleted = 0; |
| |
| KeyframeMap::const_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++) { |
| size_t bytes_deleted = buffers_.front()->data_size(); |
| DCHECK_GE(size_in_bytes_, bytes_deleted); |
| 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; |
| CHECK_GE(next_buffer_index_, 0) |
| << next_buffer_index_ << ", deleted " << buffers_deleted; |
| } |
| |
| // Invalidate range start time if we've deleted the first buffer of the range. |
| if (buffers_deleted > 0) { |
| range_start_pts_ = kNoTimestamp; |
| // Reset the range end time tracking if there are no more buffers in the |
| // range. |
| if (buffers_.empty()) |
| highest_frame_ = nullptr; |
| } |
| |
| return total_bytes_deleted; |
| } |
| |
| size_t SourceBufferRangeByPts::DeleteGOPFromBack(BufferQueue* deleted_buffers) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| DCHECK(!LastGOPContainsNextBufferPosition()); |
| DCHECK(deleted_buffers); |
| |
| // Remove the last GOP's keyframe from the |keyframe_map_|. |
| KeyframeMap::const_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); |
| |
| size_t total_bytes_deleted = 0; |
| while (buffers_.size() != goal_size) { |
| size_t bytes_deleted = buffers_.back()->data_size(); |
| DCHECK_GE(size_in_bytes_, bytes_deleted); |
| 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(); |
| } |
| |
| UpdateEndTimeUsingLastGOP(); |
| |
| return total_bytes_deleted; |
| } |
| |
| size_t SourceBufferRangeByPts::GetRemovalGOP( |
| base::TimeDelta start_timestamp, |
| base::TimeDelta end_timestamp, |
| size_t total_bytes_to_free, |
| base::TimeDelta* removal_end_timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| size_t bytes_removed = 0; |
| |
| KeyframeMap::const_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::const_iterator buffer_itr = buffers_.begin() + keyframe_index; |
| KeyframeMap::const_iterator gop_end = keyframe_map_.end(); |
| if (end_timestamp < GetBufferedEndTimestamp()) |
| gop_end = GetFirstKeyframeAtOrBefore(end_timestamp); |
| |
| // Check if the removal range is within a GOP and skip the loop if so. |
| // [keyframe]...[start_timestamp]...[end_timestamp]...[keyframe] |
| KeyframeMap::const_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_removed < total_bytes_to_free) { |
| ++gop_itr; |
| |
| size_t gop_size = 0; |
| int next_gop_index = gop_itr == keyframe_map_.end() |
| ? buffers_.size() |
| : gop_itr->second - keyframe_map_index_base_; |
| BufferQueue::const_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; |
| } |
| if (bytes_removed > 0) { |
| *removal_end_timestamp = gop_itr == keyframe_map_.end() |
| ? GetBufferedEndTimestamp() |
| : gop_itr->first; |
| } |
| return bytes_removed; |
| } |
| |
| bool SourceBufferRangeByPts::FirstGOPEarlierThanMediaTime( |
| base::TimeDelta media_time) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| if (keyframe_map_.size() == 1u) |
| return (GetBufferedEndTimestamp() <= media_time); |
| |
| KeyframeMap::const_iterator second_gop = keyframe_map_.begin(); |
| ++second_gop; |
| return second_gop->first <= media_time; |
| } |
| |
| bool SourceBufferRangeByPts::FirstGOPContainsNextBufferPosition() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| 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 SourceBufferRangeByPts::LastGOPContainsNextBufferPosition() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| 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_; |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::GetNextTimestamp() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| CHECK(!buffers_.empty()) << next_buffer_index_; |
| CHECK(HasNextBufferPosition()) |
| << next_buffer_index_ << ", size=" << buffers_.size(); |
| |
| if (next_buffer_index_ >= static_cast<int>(buffers_.size())) { |
| return kNoTimestamp; |
| } |
| |
| return buffers_[next_buffer_index_]->timestamp(); |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::GetStartTimestamp() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| base::TimeDelta start_timestamp = range_start_pts_; |
| if (start_timestamp == kNoTimestamp) |
| start_timestamp = buffers_.front()->timestamp(); |
| return start_timestamp; |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::GetEndTimestamp() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| return highest_frame_->timestamp(); |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::GetBufferedEndTimestamp() const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| base::TimeDelta duration = highest_frame_->duration(); |
| |
| // FrameProcessor should protect against unknown buffer durations. |
| DCHECK_NE(duration, kNoTimestamp); |
| |
| // Because media::Ranges<base::TimeDelta>::Add() ignores 0 duration ranges, |
| // report 1 microsecond for the last buffer's duration if it is a 0 duration |
| // buffer. |
| if (duration.is_zero()) |
| duration = base::TimeDelta::FromMicroseconds(1); |
| |
| return GetEndTimestamp() + duration; |
| } |
| |
| bool SourceBufferRangeByPts::BelongsToRange(base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| |
| return (IsNextInPresentationSequence(timestamp) || |
| (GetStartTimestamp() <= timestamp && timestamp <= GetEndTimestamp())); |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::FindHighestBufferedTimestampAtOrBefore( |
| base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!buffers_.empty()); |
| DCHECK(BelongsToRange(timestamp)); |
| |
| if (keyframe_map_.begin()->first > timestamp) { |
| // If the first keyframe in the range starts after |timestamp|, then |
| // return the range start time (which could be earlier due to coded frame |
| // group signalling.) |
| base::TimeDelta range_start = GetStartTimestamp(); |
| DCHECK(timestamp >= range_start) << "BelongsToRange() semantics failed."; |
| return range_start; |
| } |
| |
| if (keyframe_map_.begin()->first == timestamp) { |
| return timestamp; |
| } |
| |
| KeyframeMap::const_iterator key_iter = GetFirstKeyframeAtOrBefore(timestamp); |
| DCHECK(key_iter != keyframe_map_.end()) |
| << "BelongsToRange() semantics failed."; |
| DCHECK(key_iter->first <= timestamp); |
| |
| // Scan forward in |buffers_| to find the highest frame with timestamp <= |
| // |timestamp|. Stop once a frame with timestamp > |timestamp| is encountered. |
| size_t key_index = key_iter->second - keyframe_map_index_base_; |
| SourceBufferRange::BufferQueue::const_iterator search_iter = |
| buffers_.begin() + key_index; |
| CHECK(search_iter != buffers_.end()); |
| base::TimeDelta cur_frame_time = (*search_iter)->timestamp(); |
| base::TimeDelta result = cur_frame_time; |
| while (true) { |
| result = std::max(result, cur_frame_time); |
| search_iter++; |
| if (search_iter == buffers_.end()) |
| return result; |
| cur_frame_time = (*search_iter)->timestamp(); |
| if (cur_frame_time > timestamp) |
| return result; |
| } |
| |
| NOTREACHED(); |
| return base::TimeDelta(); |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::NextKeyframeTimestamp( |
| base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!keyframe_map_.empty()); |
| |
| if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) |
| return kNoTimestamp; |
| |
| KeyframeMap::const_iterator itr = GetFirstKeyframeAt(timestamp, false); |
| if (itr == keyframe_map_.end()) |
| return kNoTimestamp; |
| |
| // If the timestamp is inside the gap between the start of the coded frame |
| // group and the first buffer, then just pretend there is a keyframe at the |
| // specified timestamp. |
| if (itr == keyframe_map_.begin() && timestamp > range_start_pts_ && |
| timestamp < itr->first) { |
| return timestamp; |
| } |
| |
| return itr->first; |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::KeyframeBeforeTimestamp( |
| base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| DCHECK(!keyframe_map_.empty()); |
| |
| if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) |
| return kNoTimestamp; |
| |
| return GetFirstKeyframeAtOrBefore(timestamp)->first; |
| } |
| |
| bool SourceBufferRangeByPts::GetBuffersInRange(base::TimeDelta start, |
| base::TimeDelta end, |
| BufferQueue* buffers) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| // Find the nearest buffer with a timestamp <= start. |
| const base::TimeDelta first_timestamp = KeyframeBeforeTimestamp(start); |
| if (first_timestamp == kNoTimestamp) |
| return false; |
| |
| // Find all buffers involved in the range. |
| const size_t previous_size = buffers->size(); |
| for (BufferQueue::const_iterator it = GetBufferItrAt(first_timestamp, false); |
| it != buffers_.end(); ++it) { |
| const scoped_refptr<StreamParserBuffer>& buffer = *it; |
| // Buffers without duration are not supported, so bail if we encounter any. |
| if (buffer->duration() == kNoTimestamp || |
| buffer->duration() <= base::TimeDelta()) { |
| return false; |
| } |
| if (buffer->timestamp() >= end) |
| break; |
| |
| if (buffer->timestamp() + buffer->duration() <= start) |
| continue; |
| |
| DCHECK(buffer->is_key_frame()); |
| buffers->push_back(buffer); |
| } |
| return previous_size < buffers->size(); |
| } |
| |
| base::TimeDelta SourceBufferRangeByPts::NextRangeStartTimeForAppendRangeToEnd( |
| const SourceBufferRangeByPts& range) const { |
| DCHECK(!buffers_.empty()); |
| DCHECK(!range.buffers_.empty()); |
| |
| base::TimeDelta next_range_first_buffer_time = |
| range.buffers_.front()->timestamp(); |
| base::TimeDelta this_range_end_time = GetEndTimestamp(); |
| if (next_range_first_buffer_time < this_range_end_time) |
| return kNoTimestamp; |
| |
| base::TimeDelta next_range_start_time = range.GetStartTimestamp(); |
| DCHECK(next_range_start_time <= next_range_first_buffer_time); |
| |
| if (next_range_start_time >= this_range_end_time) |
| return next_range_start_time; |
| |
| return this_range_end_time; |
| } |
| |
| size_t SourceBufferRangeByPts::GetBufferIndexAt( |
| base::TimeDelta timestamp, |
| bool skip_given_timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| // Find the GOP containing |timestamp| (or trivial buffers_.size() if none |
| // contain |timestamp|). |
| KeyframeMap::const_iterator gop_iter = GetFirstKeyframeAtOrBefore(timestamp); |
| if (gop_iter == keyframe_map_.end()) |
| return buffers_.size(); |
| |
| // Then scan forward in this GOP in decode sequence for the first frame with |
| // PTS >= |timestamp| (or strictly > if |skip_given_timestamp| is true). If |
| // this GOP doesn't contain such a frame, returns the index of the keyframe of |
| // the next GOP (which could be the index of end() of |buffers_| if this was |
| // the last GOP in |buffers_|). We do linear scan of the GOP here because we |
| // don't know the DTS for the searched-for frame, and the PTS sequence within |
| // a GOP may not match the DTS-sorted sequence of frames within the GOP. |
| DCHECK_GT(buffers_.size(), 0u); |
| size_t search_index = gop_iter->second - keyframe_map_index_base_; |
| SourceBufferRange::BufferQueue::const_iterator search_iter = |
| buffers_.begin() + search_index; |
| gop_iter++; |
| |
| SourceBufferRange::BufferQueue::const_iterator next_gop_start = |
| gop_iter == keyframe_map_.end() |
| ? buffers_.end() |
| : buffers_.begin() + (gop_iter->second - keyframe_map_index_base_); |
| |
| while (search_iter != next_gop_start) { |
| if (((*search_iter)->timestamp() > timestamp) || |
| (!skip_given_timestamp && (*search_iter)->timestamp() == timestamp)) { |
| break; |
| } |
| search_index++; |
| search_iter++; |
| } |
| |
| return search_index; |
| } |
| |
| SourceBufferRange::BufferQueue::const_iterator |
| SourceBufferRangeByPts::GetBufferItrAt(base::TimeDelta timestamp, |
| bool skip_given_timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| return buffers_.begin() + GetBufferIndexAt(timestamp, skip_given_timestamp); |
| } |
| |
| SourceBufferRangeByPts::KeyframeMap::const_iterator |
| SourceBufferRangeByPts::GetFirstKeyframeAt(base::TimeDelta timestamp, |
| bool skip_given_timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| return skip_given_timestamp ? keyframe_map_.upper_bound(timestamp) |
| : keyframe_map_.lower_bound(timestamp); |
| } |
| |
| SourceBufferRangeByPts::KeyframeMap::const_iterator |
| SourceBufferRangeByPts::GetFirstKeyframeAtOrBefore( |
| base::TimeDelta timestamp) const { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| KeyframeMap::const_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; |
| } |
| |
| bool SourceBufferRangeByPts::TruncateAt(const size_t starting_point, |
| BufferQueue* deleted_buffers) { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| CHECK_LE(starting_point, buffers_.size()); |
| DCHECK(!deleted_buffers || deleted_buffers->empty()); |
| |
| // Return if we're not deleting anything. |
| if (starting_point == buffers_.size()) |
| return buffers_.empty(); |
| |
| // Reset the next buffer index if we will be deleting the buffer that's next |
| // in sequence. |
| if (HasNextBufferPosition()) { |
| if (static_cast<size_t>(next_buffer_index_) >= starting_point) { |
| if (HasNextBuffer() && deleted_buffers) { |
| BufferQueue saved(buffers_.begin() + next_buffer_index_, |
| buffers_.end()); |
| deleted_buffers->swap(saved); |
| } |
| ResetNextBufferPosition(); |
| } |
| } |
| |
| const BufferQueue::const_iterator starting_point_iter = |
| buffers_.begin() + starting_point; |
| |
| // Remove keyframes from |starting_point| onward. |
| KeyframeMap::const_iterator starting_point_keyframe = |
| keyframe_map_.lower_bound((*starting_point_iter)->timestamp()); |
| keyframe_map_.erase(starting_point_keyframe, keyframe_map_.end()); |
| |
| // Remove everything from |starting_point| onward. |
| FreeBufferRange(starting_point_iter, buffers_.end()); |
| |
| UpdateEndTimeUsingLastGOP(); |
| return buffers_.empty(); |
| } |
| |
| void SourceBufferRangeByPts::UpdateEndTimeUsingLastGOP() { |
| DVLOG(1) << __func__; |
| DVLOG(4) << ToStringForDebugging(); |
| |
| if (buffers_.empty()) { |
| DVLOG(1) << __func__ << " Empty range, resetting range end"; |
| highest_frame_ = nullptr; |
| return; |
| } |
| |
| highest_frame_ = nullptr; |
| |
| KeyframeMap::const_iterator last_gop = keyframe_map_.end(); |
| CHECK_GT(keyframe_map_.size(), 0u); |
| --last_gop; |
| |
| // Iterate through the frames of the last GOP in this range, finding the |
| // frame with the highest PTS. |
| for (BufferQueue::const_iterator buffer_itr = |
| buffers_.begin() + (last_gop->second - keyframe_map_index_base_); |
| buffer_itr != buffers_.end(); ++buffer_itr) { |
| UpdateEndTime(*buffer_itr); |
| } |
| |
| DVLOG(1) << __func__ << " Updated range end time to " |
| << highest_frame_->timestamp() << ", " |
| << highest_frame_->timestamp() + highest_frame_->duration(); |
| } |
| |
| std::string SourceBufferRangeByPts::ToStringForDebugging() const { |
| std::stringstream result; |
| |
| #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) |
| result << "keyframe_map_index_base_=" << keyframe_map_index_base_ |
| << ", buffers.size()=" << buffers_.size() |
| << ", keyframe_map_.size()=" << keyframe_map_.size() |
| << ", keyframe_map_:\n"; |
| for (const auto& entry : keyframe_map_) { |
| result << "\t pts " << entry.first.InMicroseconds() |
| << ", unadjusted idx = " << entry.second << "\n"; |
| } |
| #endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) |
| |
| return result.str(); |
| } |
| |
| } // namespace media |