#include "media/base/audio_discard_helper.h"
#include <stddef.h>
#include <memory>
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/decoder_buffer.h"
#include "media/base/test_helpers.h"
#include "media/base/timestamp_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
static const float kDataStep = 0.01f;
static const size_t kSampleRate = 48000;
static AudioDiscardHelper::TimeInfo CreateTimeInfo(base::TimeDelta timestamp,
base::TimeDelta duration) {
AudioDiscardHelper::TimeInfo time_info;
time_info.timestamp = timestamp;
time_info.duration = duration;
return time_info;
}
static scoped_refptr<AudioBuffer> CreateDecodedBuffer(int frames) {
return MakeAudioBuffer(kSampleFormatPlanarF32, CHANNEL_LAYOUT_MONO, 1,
kSampleRate, 0.0f, kDataStep, frames, kNoTimestamp);
}
static float ExtractDecodedData(const AudioBuffer& buffer, int index) {
std::unique_ptr<AudioBus> temp_bus =
AudioBus::Create(buffer.channel_count(), 1);
buffer.ReadFrames(1, index, 0, temp_bus.get());
return temp_bus->channel_span(0)[0] * std::numeric_limits<uint16_t>::max();
}
TEST(AudioDiscardHelperTest, TimeDeltaToFrames) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
EXPECT_EQ(0u, discard_helper.TimeDeltaToFrames(base::TimeDelta()));
EXPECT_EQ(kSampleRate / 100,
discard_helper.TimeDeltaToFrames(base::Milliseconds(10)));
const int small_remainder =
base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.9) / kSampleRate;
EXPECT_EQ(kSampleRate - 1, discard_helper.TimeDeltaToFrames(
base::Microseconds(small_remainder)));
const int large_remainder =
base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.4) / kSampleRate;
EXPECT_EQ(kSampleRate, discard_helper.TimeDeltaToFrames(
base::Microseconds(large_remainder)));
}
TEST(AudioDiscardHelperTest, BasicProcessBuffers) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kEstimatedDuration = base::Milliseconds(9);
const base::TimeDelta kActualDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kActualDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kEstimatedDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kActualDuration, decoded_buffer->duration());
EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
discard_helper.Reset(0);
ASSERT_FALSE(discard_helper.initialized());
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, nullptr));
}
TEST(AudioDiscardHelperTest, NegativeTimestampClampsToZero) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = -base::Seconds(1);
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(base::TimeDelta(), decoded_buffer->timestamp());
EXPECT_EQ(kDuration, decoded_buffer->duration());
EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, ProcessBuffersWithInitialDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
const int kDiscardFrames = kTestFrames / 2;
discard_helper.Reset(kDiscardFrames);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, ProcessBuffersWithLargeInitialDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
discard_helper.Reset(kTestFrames * 1.5);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
time_info = CreateTimeInfo(kTimestamp + kDuration, kDuration);
decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
const int kDiscardFrames = kTestFrames / 2;
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, AllowNonMonotonicTimestamps) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration, decoded_buffer->duration());
EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp + kDuration, decoded_buffer->timestamp());
EXPECT_EQ(kDuration, decoded_buffer->duration());
EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, DiscardEndPadding) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
time_info.discard_padding = std::make_pair(base::TimeDelta(), kDuration / 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, BadDiscardEndPadding) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
time_info.discard_padding = std::make_pair(base::TimeDelta(), kDuration * 2);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
}
TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardEndPadding) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
time_info.discard_padding = std::make_pair(base::TimeDelta(), kDuration / 4);
const int kDiscardFrames = kTestFrames / 4;
discard_helper.Reset(kDiscardFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
const int kDiscardFrames = kTestFrames / 4;
time_info.discard_padding = std::make_pair(kDuration / 8, kDuration / 16);
discard_helper.Reset(kDiscardFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
decoded_buffer->duration());
EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPaddingAndDecoderDelay) {
const int kDecoderDelay = kSampleRate / 100 / 2;
AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay, false);
ASSERT_FALSE(discard_helper.initialized());
discard_helper.Reset(kDecoderDelay);
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
time_info.discard_padding = std::make_pair(kDuration / 2, base::TimeDelta());
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
time_info.timestamp += kDuration;
time_info.discard_padding = std::make_pair(kDuration / 2, kDuration / 4);
decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
ASSERT_NEAR(kDecoderDelay * kDataStep,
ExtractDecodedData(*decoded_buffer, kDecoderDelay),
kDataStep / 1000);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
time_info.timestamp += kDuration;
time_info.discard_padding = std::make_pair(base::TimeDelta(), kDuration / 2);
decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp + kDuration / 2, decoded_buffer->timestamp());
EXPECT_EQ(3 * kDuration / 4, decoded_buffer->duration());
EXPECT_EQ(3 * kTestFrames / 4, decoded_buffer->frame_count());
const int kDiscardFrames = kTestFrames / 4;
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
ASSERT_FLOAT_EQ(
kDiscardFrames * 2 * kDataStep,
ExtractDecodedData(*decoded_buffer, kDecoderDelay - kDiscardFrames));
time_info.timestamp += kDuration;
time_info.discard_padding = DecoderBuffer::DiscardPadding();
decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp + kDuration / 2 + 3 * kDuration / 4,
decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kTestFrames / 2 * kDataStep,
ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, DelayedDiscardInitialDiscardAndDiscardPadding) {
AudioDiscardHelper discard_helper(kSampleRate, 0, true);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
const int kDiscardFrames = kTestFrames / 4;
time_info.discard_padding = std::make_pair(kDuration / 8, kDuration / 16);
discard_helper.Reset(kDiscardFrames);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, nullptr));
ASSERT_TRUE(discard_helper.initialized());
time_info = CreateTimeInfo(kTimestamp + kDuration, kDuration);
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
decoded_buffer->duration());
EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, CompleteDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0, false);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
discard_helper.Reset(0);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
time_info.discard_padding =
std::make_pair(kInfiniteDuration, base::TimeDelta());
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
time_info.timestamp = kTimestamp + kDuration;
time_info.discard_padding = DecoderBuffer::DiscardPadding();
decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, CompleteDiscardWithDelayedDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0, true);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
discard_helper.Reset(0);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
time_info.discard_padding =
std::make_pair(kInfiniteDuration, base::TimeDelta());
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, nullptr));
ASSERT_TRUE(discard_helper.initialized());
time_info.timestamp = kTimestamp + kDuration;
time_info.discard_padding = DecoderBuffer::DiscardPadding();
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
time_info.timestamp = kTimestamp + 2 * kDuration;
decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(*decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, CompleteDiscardWithInitialDiscardDecoderDelay) {
const int kDecoderDelay = kSampleRate / 100 / 2;
AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay, false);
ASSERT_FALSE(discard_helper.initialized());
discard_helper.Reset(kDecoderDelay);
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::Milliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
AudioDiscardHelper::TimeInfo time_info =
CreateTimeInfo(kTimestamp, kDuration);
time_info.discard_padding =
std::make_pair(kInfiniteDuration, base::TimeDelta());
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FALSE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
ASSERT_TRUE(discard_helper.initialized());
time_info.timestamp = kTimestamp + kDuration;
time_info.discard_padding = DecoderBuffer::DiscardPadding();
decoded_buffer = CreateDecodedBuffer(kTestFrames * 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(time_info, decoded_buffer.get()));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration * 2 - kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames * 2 - kDecoderDelay, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kDecoderDelay * kDataStep,
ExtractDecodedData(*decoded_buffer, 0));
}
}