#include "media/base/audio_discard_helper.h"
#include <algorithm>
#include "base/logging.h"
#include "media/base/audio_buffer.h"
namespace media {
static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,
base::TimeDelta current_timestamp) {
if (last_timestamp == kNoTimestamp || last_timestamp <= current_timestamp)
return;
const base::TimeDelta diff = current_timestamp - last_timestamp;
DLOG(WARNING) << "Input timestamps are not monotonically increasing! "
<< " ts " << current_timestamp.InMicroseconds() << " us"
<< " diff " << diff.InMicroseconds() << " us";
}
AudioDiscardHelper::AudioDiscardHelper(int sample_rate,
size_t decoder_delay,
bool delayed_discard)
: sample_rate_(sample_rate),
decoder_delay_(decoder_delay),
timestamp_helper_(sample_rate_),
discard_frames_(0),
last_input_timestamp_(kNoTimestamp),
delayed_discard_(delayed_discard),
delayed_end_discard_(0) {
DCHECK_GT(sample_rate_, 0);
}
AudioDiscardHelper::~AudioDiscardHelper() = default;
size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const {
DCHECK(duration >= base::TimeDelta());
return duration.InSecondsF() * sample_rate_ + 0.5;
}
void AudioDiscardHelper::Reset(size_t initial_discard) {
discard_frames_ = initial_discard;
last_input_timestamp_ = kNoTimestamp;
timestamp_helper_.Reset();
delayed_discard_padding_ = DecoderBuffer::DiscardPadding();
}
bool AudioDiscardHelper::ProcessBuffers(const TimeInfo& time_info,
AudioBuffer* decoded_buffer) {
DCHECK(time_info.timestamp != kNoTimestamp);
WarnOnNonMonotonicTimestamps(last_input_timestamp_, time_info.timestamp);
last_input_timestamp_ = time_info.timestamp;
if (!initialized()) {
timestamp_helper_.SetBaseTimestamp(
std::max(base::TimeDelta(), time_info.timestamp));
}
DCHECK(initialized());
if (!decoded_buffer) {
if (delayed_discard_) {
delayed_discard_padding_ =
time_info.discard_padding.value_or(DecoderBuffer::DiscardPadding());
}
return false;
}
const size_t original_frame_count = decoded_buffer->frame_count();
DecoderBuffer::DiscardPadding current_discard_padding =
time_info.discard_padding.value_or(DecoderBuffer::DiscardPadding());
if (delayed_discard_) {
DCHECK_EQ(decoder_delay_, 0u);
std::swap(current_discard_padding, delayed_discard_padding_);
}
if (discard_frames_ > 0) {
const size_t decoded_frames = decoded_buffer->frame_count();
const size_t frames_to_discard = std::min(discard_frames_, decoded_frames);
discard_frames_ -= frames_to_discard;
DVLOG(1) << "Initial discard of " << frames_to_discard << " out of "
<< decoded_frames << " frames.";
if (frames_to_discard == decoded_frames) {
return false;
}
decoded_buffer->TrimStart(frames_to_discard);
}
if (delayed_end_discard_ > 0) {
DCHECK_GT(decoder_delay_, 0u);
const size_t discard_index = decoder_delay_ - delayed_end_discard_;
DCHECK_LT(discard_index, decoder_delay_);
const size_t decoded_frames = decoded_buffer->frame_count();
DCHECK_LT(delayed_end_discard_, decoded_frames);
DVLOG(1) << "Delayed end discard of " << delayed_end_discard_ << " out of "
<< decoded_frames << " frames starting at " << discard_index;
decoded_buffer->TrimRange(discard_index,
discard_index + delayed_end_discard_);
delayed_end_discard_ = 0;
}
if (current_discard_padding.first.is_positive()) {
const size_t decoded_frames = decoded_buffer->frame_count();
const size_t start_frames_to_discard =
current_discard_padding.first == kInfiniteDuration
? (decoder_delay_ > 0 ? TimeDeltaToFrames(time_info.duration)
: decoded_frames)
: TimeDeltaToFrames(current_discard_padding.first);
size_t discard_start = decoder_delay_;
if (decoder_delay_ > 0) {
const size_t frames_discarded_so_far =
original_frame_count - decoded_buffer->frame_count();
CHECK_LE(frames_discarded_so_far, decoder_delay_);
discard_start -= frames_discarded_so_far;
}
if (discard_start >= decoded_frames) {
DLOG(ERROR)
<< "Unsupported discard padding and decoder delay mix. Due to "
"decoder delay, discard padding indicates data beyond the current "
"buffer should be discarded. This is not supported.";
return false;
}
const size_t frames_to_discard =
std::min(start_frames_to_discard, decoded_frames - discard_start);
DCHECK(!discard_frames_);
discard_frames_ = start_frames_to_discard - frames_to_discard;
DVLOG(1) << "Front discard of " << frames_to_discard << " out of "
<< decoded_frames << " frames starting at " << discard_start;
if (frames_to_discard == decoded_frames) {
return false;
}
decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard);
} else {
DCHECK(current_discard_padding.first.is_zero());
}
if (current_discard_padding.second.is_positive()) {
const size_t decoded_frames = decoded_buffer->frame_count();
size_t end_frames_to_discard =
TimeDeltaToFrames(current_discard_padding.second);
if (decoder_delay_) {
if (decoder_delay_ >= original_frame_count) {
DLOG(ERROR) << "Encountered invalid discard padding value.";
return false;
}
if (end_frames_to_discard >= decoder_delay_) {
DCHECK(!discard_frames_);
discard_frames_ = decoder_delay_;
end_frames_to_discard -= decoder_delay_;
} else {
DCHECK(!delayed_end_discard_);
std::swap(delayed_end_discard_, end_frames_to_discard);
}
}
if (end_frames_to_discard > decoded_frames) {
DLOG(ERROR) << "Encountered invalid discard padding value.";
return false;
}
if (end_frames_to_discard > 0) {
DVLOG(1) << "End discard of " << end_frames_to_discard << " out of "
<< decoded_frames;
if (end_frames_to_discard == decoded_frames)
return false;
decoded_buffer->TrimEnd(end_frames_to_discard);
}
} else {
DCHECK(current_discard_padding.second.is_zero());
}
DVLOG(3) << __func__ << " ts: " << timestamp_helper_.GetTimestamp()
<< " frames: " << decoded_buffer->frame_count();
decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
timestamp_helper_.AddFrames(decoded_buffer->frame_count());
return true;
}
}