blob: fb4bb210278489b99177cc4a81c0540e19bf4a95 [file] [log] [blame]
// Copyright 2014 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.h"
#include "media/base/timestamp_constants.h"
namespace media {
// static
bool SourceBufferRange::IsUncommonSameTimestampSequence(
bool prev_is_keyframe,
bool current_is_keyframe) {
return current_is_keyframe && !prev_is_keyframe;
}
SourceBufferRange::SourceBufferRange(
GapPolicy gap_policy,
const InterbufferDistanceCB& interbuffer_distance_cb)
: gap_policy_(gap_policy),
next_buffer_index_(-1),
interbuffer_distance_cb_(interbuffer_distance_cb),
size_in_bytes_(0) {
DCHECK(!interbuffer_distance_cb.is_null());
}
SourceBufferRange::~SourceBufferRange() = default;
void SourceBufferRange::SeekToStart() {
CHECK(!buffers_.empty());
next_buffer_index_ = 0;
}
bool SourceBufferRange::GetNextBuffer(
scoped_refptr<StreamParserBuffer>* out_buffer) {
if (!HasNextBuffer())
return false;
*out_buffer = buffers_[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 {
CHECK(HasNextBuffer()) << next_buffer_index_;
// If the next buffer is an audio splice frame, the next effective config id
// comes from the first fade out preroll buffer.
return buffers_[next_buffer_index_]->GetConfigId();
}
bool SourceBufferRange::HasNextBufferPosition() const {
return next_buffer_index_ >= 0;
}
void SourceBufferRange::ResetNextBufferPosition() {
next_buffer_index_ = -1;
}
void SourceBufferRange::GetRangeEndTimesForTesting(
base::TimeDelta* highest_pts,
base::TimeDelta* end_time) const {
if (highest_frame_) {
*highest_pts = highest_frame_->timestamp();
*end_time = *highest_pts + highest_frame_->duration();
DCHECK_NE(*highest_pts, kNoTimestamp);
DCHECK_NE(*end_time, kNoTimestamp);
return;
}
*highest_pts = *end_time = kNoTimestamp;
}
void SourceBufferRange::AdjustEstimatedDurationForNewAppend(
const BufferQueue& new_buffers) {
if (buffers_.empty() || new_buffers.empty()) {
return;
}
// If the last of the previously appended buffers contains estimated duration,
// we now refine that estimate by taking the PTS delta from the first new
// buffer being appended.
const auto& last_appended_buffer = buffers_.back();
if (last_appended_buffer->is_duration_estimated()) {
base::TimeDelta timestamp_delta =
new_buffers.front()->timestamp() - last_appended_buffer->timestamp();
DCHECK_GE(timestamp_delta, base::TimeDelta());
if (last_appended_buffer->duration() != timestamp_delta) {
DVLOG(1) << "Replacing estimated duration ("
<< last_appended_buffer->duration()
<< ") from previous range-end with derived duration ("
<< timestamp_delta << ").";
last_appended_buffer->set_duration(timestamp_delta);
}
}
}
void SourceBufferRange::FreeBufferRange(
const BufferQueue::const_iterator& starting_point,
const BufferQueue::const_iterator& ending_point) {
for (BufferQueue::const_iterator itr = starting_point; itr != ending_point;
++itr) {
size_t itr_data_size = static_cast<size_t>((*itr)->data_size());
DCHECK_GE(size_in_bytes_, itr_data_size);
size_in_bytes_ -= itr_data_size;
}
buffers_.erase(starting_point, ending_point);
}
base::TimeDelta SourceBufferRange::GetFudgeRoom() const {
// 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 * GetApproximateDuration();
}
base::TimeDelta SourceBufferRange::GetApproximateDuration() const {
base::TimeDelta max_interbuffer_distance = interbuffer_distance_cb_.Run();
DCHECK(max_interbuffer_distance != kNoTimestamp);
return max_interbuffer_distance;
}
void SourceBufferRange::UpdateEndTime(
const scoped_refptr<StreamParserBuffer>& new_buffer) {
base::TimeDelta timestamp = new_buffer->timestamp();
base::TimeDelta duration = new_buffer->duration();
DVLOG(1) << __func__ << " timestamp=" << timestamp
<< ", duration=" << duration;
DCHECK_NE(timestamp, kNoTimestamp);
DCHECK_GE(timestamp, base::TimeDelta());
DCHECK_GE(duration, base::TimeDelta());
if (!highest_frame_) {
DVLOG(1) << "Updating range end time from <empty> to "
<< timestamp.InMicroseconds() << "us, "
<< (timestamp + duration).InMicroseconds() << "us";
highest_frame_ = new_buffer;
return;
}
if (highest_frame_->timestamp() < timestamp ||
(highest_frame_->timestamp() == timestamp &&
highest_frame_->duration() <= duration)) {
DVLOG(1) << "Updating range end time from "
<< highest_frame_->timestamp().InMicroseconds() << "us, "
<< (highest_frame_->timestamp() + highest_frame_->duration())
.InMicroseconds()
<< "us to " << timestamp.InMicroseconds() << "us, "
<< (timestamp + duration).InMicroseconds();
highest_frame_ = new_buffer;
}
}
bool SourceBufferRange::IsNextInPresentationSequence(
base::TimeDelta timestamp) const {
DCHECK_NE(timestamp, kNoTimestamp);
CHECK(!buffers_.empty());
base::TimeDelta highest_timestamp = highest_frame_->timestamp();
DCHECK_NE(highest_timestamp, kNoTimestamp);
return (highest_timestamp == timestamp ||
(highest_timestamp < timestamp &&
(gap_policy_ == ALLOW_GAPS ||
timestamp <= highest_timestamp + GetFudgeRoom())));
}
bool SourceBufferRange::IsNextInDecodeSequence(
DecodeTimestamp decode_timestamp) const {
CHECK(!buffers_.empty());
DecodeTimestamp end = buffers_.back()->GetDecodeTimestamp();
return (
end == decode_timestamp ||
(end < decode_timestamp && (gap_policy_ == ALLOW_GAPS ||
decode_timestamp <= end + GetFudgeRoom())));
}
} // namespace media