blob: 29220c66fa6eddfd2f20efd10c4ab894d297340e [file] [log] [blame]
// 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 "cobalt/media/base/audio_splicer.h"
#include <memory>
#include "base/basictypes.h"
#include "cobalt/media/base/audio_buffer.h"
#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media/base/audio_timestamp_helper.h"
#include "cobalt/media/base/test_helpers.h"
#include "cobalt/media/base/timestamp_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace media {
// Do not change this format. AddInput() and GetValue() only work with float.
static const SampleFormat kSampleFormat = kSampleFormatF32;
static_assert(kSampleFormat == kSampleFormatF32, "invalid splice format");
static const int kChannels = 1;
static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_MONO;
static const int kDefaultSampleRate = 44100;
static const int kDefaultBufferSize = 100;
class AudioSplicerTest : public ::testing::Test {
public:
AudioSplicerTest()
: splicer_(kDefaultSampleRate, new MediaLog()),
input_timestamp_helper_(kDefaultSampleRate) {
input_timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
}
scoped_refptr<AudioBuffer> GetNextInputBuffer(float value) {
return GetNextInputBuffer(value, kDefaultBufferSize);
}
scoped_refptr<AudioBuffer> GetNextInputBuffer(float value, int frame_size) {
scoped_refptr<AudioBuffer> buffer = MakeAudioBuffer<float>(
kSampleFormat, kChannelLayout, kChannels, kDefaultSampleRate, value,
0.0f, frame_size, input_timestamp_helper_.GetTimestamp());
input_timestamp_helper_.AddFrames(frame_size);
return buffer;
}
float GetValue(const scoped_refptr<AudioBuffer>& buffer) {
return reinterpret_cast<const float*>(buffer->channel_data()[0])[0];
}
bool VerifyData(const scoped_refptr<AudioBuffer>& buffer, float value) {
int frames = buffer->frame_count();
std::unique_ptr<AudioBus> bus = AudioBus::Create(kChannels, frames);
buffer->ReadFrames(frames, 0, 0, bus.get());
for (int ch = 0; ch < buffer->channel_count(); ++ch) {
for (int i = 0; i < frames; ++i) {
if (bus->channel(ch)[i] != value) return false;
}
}
return true;
}
void VerifyNextBuffer(const scoped_refptr<AudioBuffer>& input) {
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> output = splicer_.GetNextBuffer();
EXPECT_EQ(input->timestamp(), output->timestamp());
EXPECT_EQ(input->duration(), output->duration());
EXPECT_EQ(input->frame_count(), output->frame_count());
EXPECT_TRUE(VerifyData(output, GetValue(input)));
}
void VerifyPreSpliceOutput(
const scoped_refptr<AudioBuffer>& overlapped_buffer,
const scoped_refptr<AudioBuffer>& overlapping_buffer,
int expected_pre_splice_size,
base::TimeDelta expected_pre_splice_duration) {
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> pre_splice_output = splicer_.GetNextBuffer();
EXPECT_EQ(overlapped_buffer->timestamp(), pre_splice_output->timestamp());
EXPECT_EQ(expected_pre_splice_size, pre_splice_output->frame_count());
EXPECT_EQ(expected_pre_splice_duration, pre_splice_output->duration());
EXPECT_TRUE(VerifyData(pre_splice_output, GetValue(overlapped_buffer)));
}
void VerifyCrossfadeOutput(
const scoped_refptr<AudioBuffer>& overlapped_buffer_1,
const scoped_refptr<AudioBuffer>& overlapped_buffer_2,
const scoped_refptr<AudioBuffer>& overlapping_buffer,
int second_overlap_index, int expected_crossfade_size,
base::TimeDelta expected_crossfade_duration) {
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> crossfade_output = splicer_.GetNextBuffer();
EXPECT_EQ(expected_crossfade_size, crossfade_output->frame_count());
EXPECT_EQ(expected_crossfade_duration, crossfade_output->duration());
// The splice timestamp may be adjusted by a microsecond.
EXPECT_NEAR(overlapping_buffer->timestamp().InMicroseconds(),
crossfade_output->timestamp().InMicroseconds(), 1);
// Verify the actual crossfade.
const int frames = crossfade_output->frame_count();
float overlapped_value = GetValue(overlapped_buffer_1);
const float overlapping_value = GetValue(overlapping_buffer);
std::unique_ptr<AudioBus> bus = AudioBus::Create(kChannels, frames);
crossfade_output->ReadFrames(frames, 0, 0, bus.get());
for (int ch = 0; ch < crossfade_output->channel_count(); ++ch) {
float cf_ratio = 0;
const float cf_increment = 1.0f / frames;
for (int i = 0; i < frames; ++i, cf_ratio += cf_increment) {
if (overlapped_buffer_2.get() && i >= second_overlap_index)
overlapped_value = GetValue(overlapped_buffer_2);
const float actual = bus->channel(ch)[i];
const float expected =
(1.0f - cf_ratio) * overlapped_value + cf_ratio * overlapping_value;
ASSERT_FLOAT_EQ(expected, actual) << "i=" << i;
}
}
}
bool AddInput(const scoped_refptr<AudioBuffer>& input) {
// Since the splicer doesn't make copies it's working directly on the input
// buffers. We must make a copy before adding to ensure the original buffer
// is not modified in unexpected ways.
scoped_refptr<AudioBuffer> buffer_copy =
input->end_of_stream()
? AudioBuffer::CreateEOSBuffer()
: AudioBuffer::CopyFrom(kSampleFormat, input->channel_layout(),
input->channel_count(),
input->sample_rate(), input->frame_count(),
&input->channel_data()[0],
input->timestamp());
return splicer_.AddInput(buffer_copy);
}
base::TimeDelta max_crossfade_duration() {
return splicer_.max_crossfade_duration_;
}
protected:
AudioSplicer splicer_;
AudioTimestampHelper input_timestamp_helper_;
private:
DISALLOW_COPY_AND_ASSIGN(AudioSplicerTest);
};
TEST_F(AudioSplicerTest, PassThru) {
EXPECT_FALSE(splicer_.HasNextBuffer());
// Test single buffer pass-thru behavior.
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
EXPECT_TRUE(AddInput(input_1));
VerifyNextBuffer(input_1);
EXPECT_FALSE(splicer_.HasNextBuffer());
// Test that multiple buffers can be queued in the splicer.
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f);
scoped_refptr<AudioBuffer> input_3 = GetNextInputBuffer(0.3f);
EXPECT_TRUE(AddInput(input_2));
EXPECT_TRUE(AddInput(input_3));
VerifyNextBuffer(input_2);
VerifyNextBuffer(input_3);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
TEST_F(AudioSplicerTest, Reset) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
EXPECT_TRUE(AddInput(input_1));
ASSERT_TRUE(splicer_.HasNextBuffer());
splicer_.Reset();
EXPECT_FALSE(splicer_.HasNextBuffer());
// Add some bytes to the timestamp helper so that the
// next buffer starts many frames beyond the end of
// |input_1|. This is to make sure that Reset() actually
// clears its state and doesn't try to insert a gap.
input_timestamp_helper_.AddFrames(100);
// Verify that a new input buffer passes through as expected.
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f);
EXPECT_TRUE(AddInput(input_2));
VerifyNextBuffer(input_2);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
TEST_F(AudioSplicerTest, EndOfStream) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
scoped_refptr<AudioBuffer> input_2 = AudioBuffer::CreateEOSBuffer();
scoped_refptr<AudioBuffer> input_3 = GetNextInputBuffer(0.2f);
EXPECT_TRUE(input_2->end_of_stream());
EXPECT_TRUE(AddInput(input_1));
EXPECT_TRUE(AddInput(input_2));
VerifyNextBuffer(input_1);
scoped_refptr<AudioBuffer> output_2 = splicer_.GetNextBuffer();
EXPECT_FALSE(splicer_.HasNextBuffer());
EXPECT_TRUE(output_2->end_of_stream());
// Verify that buffers can be added again after Reset().
splicer_.Reset();
EXPECT_TRUE(AddInput(input_3));
VerifyNextBuffer(input_3);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test the gap insertion code.
// +--------------+ +--------------+
// |11111111111111| |22222222222222|
// +--------------+ +--------------+
// Results in:
// +--------------+----+--------------+
// |11111111111111|0000|22222222222222|
// +--------------+----+--------------+
TEST_F(AudioSplicerTest, GapInsertion) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
// Add bytes to the timestamp helper so that the next buffer
// will have a starting timestamp that indicates a gap is
// present.
const int kGapSize = 7;
input_timestamp_helper_.AddFrames(kGapSize);
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f);
EXPECT_TRUE(AddInput(input_1));
EXPECT_TRUE(AddInput(input_2));
// Verify that the first input buffer passed through unmodified.
VerifyNextBuffer(input_1);
// Verify the contents of the gap buffer.
scoped_refptr<AudioBuffer> output_2 = splicer_.GetNextBuffer();
base::TimeDelta gap_timestamp = input_1->timestamp() + input_1->duration();
base::TimeDelta gap_duration = input_2->timestamp() - gap_timestamp;
EXPECT_GT(gap_duration, base::TimeDelta());
EXPECT_EQ(gap_timestamp, output_2->timestamp());
EXPECT_NEAR(gap_duration.InMicroseconds(),
output_2->duration().InMicroseconds(), 1);
EXPECT_EQ(kGapSize, output_2->frame_count());
EXPECT_TRUE(VerifyData(output_2, 0.0f));
// Verify that the second input buffer passed through unmodified.
VerifyNextBuffer(input_2);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test that an error is signalled when the gap between input buffers is
// too large.
TEST_F(AudioSplicerTest, GapTooLarge) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
// Add a seconds worth of bytes so that an unacceptably large
// gap exists between |input_1| and |input_2|.
const int kGapSize = kDefaultSampleRate;
input_timestamp_helper_.AddFrames(kGapSize);
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f);
EXPECT_TRUE(AddInput(input_1));
EXPECT_FALSE(AddInput(input_2));
VerifyNextBuffer(input_1);
// Verify that the second buffer is not available.
EXPECT_FALSE(splicer_.HasNextBuffer());
// Reset the timestamp helper so it can generate a buffer that is
// right after |input_1|.
input_timestamp_helper_.SetBaseTimestamp(input_1->timestamp() +
input_1->duration());
// Verify that valid buffers are still accepted.
scoped_refptr<AudioBuffer> input_3 = GetNextInputBuffer(0.3f);
EXPECT_TRUE(AddInput(input_3));
VerifyNextBuffer(input_3);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Verifies that an error is signalled if AddInput() is called
// with a timestamp that is earlier than the first buffer added.
TEST_F(AudioSplicerTest, BufferAddedBeforeBase) {
input_timestamp_helper_.SetBaseTimestamp(
base::TimeDelta::FromMicroseconds(10));
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
// Reset the timestamp helper so the next buffer will have a timestamp earlier
// than |input_1|.
input_timestamp_helper_.SetBaseTimestamp(base::TimeDelta::FromSeconds(0));
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.1f);
EXPECT_GT(input_1->timestamp(), input_2->timestamp());
EXPECT_TRUE(AddInput(input_1));
EXPECT_FALSE(AddInput(input_2));
}
// Test when one buffer partially overlaps another.
// +--------------+
// |11111111111111|
// +--------------+
// +--------------+
// |22222222222222|
// +--------------+
// Results in:
// +--------------+----------+
// |11111111111111|2222222222|
// +--------------+----------+
TEST_F(AudioSplicerTest, PartialOverlap) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
// Reset timestamp helper so that the next buffer will have a
// timestamp that starts in the middle of |input_1|.
const int kOverlapSize = input_1->frame_count() / 4;
input_timestamp_helper_.SetBaseTimestamp(input_1->timestamp());
input_timestamp_helper_.AddFrames(input_1->frame_count() - kOverlapSize);
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f);
EXPECT_TRUE(AddInput(input_1));
EXPECT_TRUE(AddInput(input_2));
// Verify that the first input buffer passed through unmodified.
VerifyNextBuffer(input_1);
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> output_2 = splicer_.GetNextBuffer();
EXPECT_FALSE(splicer_.HasNextBuffer());
// Verify that the second input buffer was truncated to only contain
// the samples that are after the end of |input_1|.
base::TimeDelta expected_timestamp =
input_1->timestamp() + input_1->duration();
base::TimeDelta expected_duration =
(input_2->timestamp() + input_2->duration()) - expected_timestamp;
EXPECT_EQ(expected_timestamp, output_2->timestamp());
EXPECT_EQ(expected_duration, output_2->duration());
EXPECT_TRUE(VerifyData(output_2, GetValue(input_2)));
}
// Test that an input buffer that is completely overlapped by a buffer
// that was already added is dropped.
// +--------------+
// |11111111111111|
// +--------------+
// +-----+
// |22222|
// +-----+
// +-------------+
// |3333333333333|
// +-------------+
// Results in:
// +--------------+-------------+
// |11111111111111|3333333333333|
// +--------------+-------------+
TEST_F(AudioSplicerTest, DropBuffer) {
scoped_refptr<AudioBuffer> input_1 = GetNextInputBuffer(0.1f);
// Reset timestamp helper so that the next buffer will have a
// timestamp that starts in the middle of |input_1|.
const int kOverlapOffset = input_1->frame_count() / 2;
const int kOverlapSize = input_1->frame_count() / 4;
input_timestamp_helper_.SetBaseTimestamp(input_1->timestamp());
input_timestamp_helper_.AddFrames(kOverlapOffset);
scoped_refptr<AudioBuffer> input_2 = GetNextInputBuffer(0.2f, kOverlapSize);
// Reset the timestamp helper so the next buffer will be right after
// |input_1|.
input_timestamp_helper_.SetBaseTimestamp(input_1->timestamp());
input_timestamp_helper_.AddFrames(input_1->frame_count());
scoped_refptr<AudioBuffer> input_3 = GetNextInputBuffer(0.3f);
EXPECT_TRUE(AddInput(input_1));
EXPECT_TRUE(AddInput(input_2));
EXPECT_TRUE(AddInput(input_3));
VerifyNextBuffer(input_1);
VerifyNextBuffer(input_3);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test crossfade when one buffer partially overlaps another.
// +--------------+
// |11111111111111|
// +--------------+
// +--------------+
// |22222222222222|
// +--------------+
// Results in:
// +----------+----+----------+
// |1111111111|xxxx|2222222222|
// +----------+----+----------+
// Where "xxxx" represents the crossfaded portion of the signal.
TEST_F(AudioSplicerTest, PartialOverlapCrossfade) {
const int kCrossfadeSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration());
const int kBufferSize = kCrossfadeSize * 2;
scoped_refptr<AudioBuffer> extra_pre_splice_buffer =
GetNextInputBuffer(0.2f, kBufferSize);
scoped_refptr<AudioBuffer> overlapped_buffer =
GetNextInputBuffer(1.0f, kBufferSize);
// Reset timestamp helper so that the next buffer will have a timestamp that
// starts in the middle of |overlapped_buffer|.
input_timestamp_helper_.SetBaseTimestamp(overlapped_buffer->timestamp());
input_timestamp_helper_.AddFrames(overlapped_buffer->frame_count() -
kCrossfadeSize);
splicer_.SetSpliceTimestamp(input_timestamp_helper_.GetTimestamp());
scoped_refptr<AudioBuffer> overlapping_buffer =
GetNextInputBuffer(0.0f, kBufferSize);
// |extra_pre_splice_buffer| is entirely before the splice and should be ready
// for output.
EXPECT_TRUE(AddInput(extra_pre_splice_buffer));
VerifyNextBuffer(extra_pre_splice_buffer);
// The splicer should be internally queuing input since |overlapped_buffer| is
// part of the splice.
EXPECT_TRUE(AddInput(overlapped_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// |overlapping_buffer| completes the splice.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_TRUE(AddInput(overlapping_buffer));
ASSERT_TRUE(splicer_.HasNextBuffer());
// Add one more buffer to make sure it's passed through untouched.
scoped_refptr<AudioBuffer> extra_post_splice_buffer =
GetNextInputBuffer(0.5f, kBufferSize);
EXPECT_TRUE(AddInput(extra_post_splice_buffer));
VerifyPreSpliceOutput(overlapped_buffer, overlapping_buffer, 221,
base::TimeDelta::FromMicroseconds(5011));
// Due to rounding the crossfade size may vary by up to a frame.
const int kExpectedCrossfadeSize = 220;
EXPECT_NEAR(kExpectedCrossfadeSize, kCrossfadeSize, 1);
VerifyCrossfadeOutput(overlapped_buffer, NULL, overlapping_buffer, 0,
kExpectedCrossfadeSize,
base::TimeDelta::FromMicroseconds(4988));
// Retrieve the remaining portion after crossfade.
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> post_splice_output = splicer_.GetNextBuffer();
EXPECT_EQ(base::TimeDelta::FromMicroseconds(20022),
post_splice_output->timestamp());
EXPECT_EQ(overlapping_buffer->frame_count() - kExpectedCrossfadeSize,
post_splice_output->frame_count());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(5034),
post_splice_output->duration());
EXPECT_TRUE(VerifyData(post_splice_output, GetValue(overlapping_buffer)));
VerifyNextBuffer(extra_post_splice_buffer);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test crossfade when one buffer partially overlaps another, but an end of
// stream buffer is received before the crossfade duration is reached.
// +--------------+
// |11111111111111|
// +--------------+
// +---------++---+
// |222222222||EOS|
// +---------++---+
// Results in:
// +----------+----+----++---+
// |1111111111|xxxx|2222||EOS|
// +----------+----+----++---+
// Where "x" represents the crossfaded portion of the signal.
TEST_F(AudioSplicerTest, PartialOverlapCrossfadeEndOfStream) {
const int kCrossfadeSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration());
scoped_refptr<AudioBuffer> overlapped_buffer =
GetNextInputBuffer(1.0f, kCrossfadeSize * 2);
// Reset timestamp helper so that the next buffer will have a timestamp that
// starts 3/4 of the way into |overlapped_buffer|.
input_timestamp_helper_.SetBaseTimestamp(overlapped_buffer->timestamp());
input_timestamp_helper_.AddFrames(3 * overlapped_buffer->frame_count() / 4);
splicer_.SetSpliceTimestamp(input_timestamp_helper_.GetTimestamp());
scoped_refptr<AudioBuffer> overlapping_buffer =
GetNextInputBuffer(0.0f, kCrossfadeSize / 3);
// The splicer should be internally queuing input since |overlapped_buffer| is
// part of the splice.
EXPECT_TRUE(AddInput(overlapped_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// |overlapping_buffer| should not have enough data to complete the splice, so
// ensure output is not available.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_TRUE(AddInput(overlapping_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// Now add an EOS buffer which should complete the splice.
EXPECT_TRUE(AddInput(AudioBuffer::CreateEOSBuffer()));
VerifyPreSpliceOutput(overlapped_buffer, overlapping_buffer, 331,
base::TimeDelta::FromMicroseconds(7505));
VerifyCrossfadeOutput(overlapped_buffer, NULL, overlapping_buffer, 0,
overlapping_buffer->frame_count(),
overlapping_buffer->duration());
// Ensure the last buffer is an EOS buffer.
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> post_splice_output = splicer_.GetNextBuffer();
EXPECT_TRUE(post_splice_output->end_of_stream());
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test crossfade when one buffer partially overlaps another, but the amount of
// overlapped data is less than the crossfade duration.
// +------------+
// |111111111111|
// +------------+
// +--------------+
// |22222222222222|
// +--------------+
// Results in:
// +----------+-+------------+
// |1111111111|x|222222222222|
// +----------+-+------------+
// Where "x" represents the crossfaded portion of the signal.
TEST_F(AudioSplicerTest, PartialOverlapCrossfadeShortPreSplice) {
const int kCrossfadeSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration());
scoped_refptr<AudioBuffer> overlapped_buffer =
GetNextInputBuffer(1.0f, kCrossfadeSize / 2);
// Reset timestamp helper so that the next buffer will have a timestamp that
// starts in the middle of |overlapped_buffer|.
input_timestamp_helper_.SetBaseTimestamp(overlapped_buffer->timestamp());
input_timestamp_helper_.AddFrames(overlapped_buffer->frame_count() / 2);
splicer_.SetSpliceTimestamp(input_timestamp_helper_.GetTimestamp());
scoped_refptr<AudioBuffer> overlapping_buffer =
GetNextInputBuffer(0.0f, kCrossfadeSize * 2);
// The splicer should be internally queuing input since |overlapped_buffer| is
// part of the splice.
EXPECT_TRUE(AddInput(overlapped_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// |overlapping_buffer| completes the splice.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_TRUE(AddInput(overlapping_buffer));
const int kExpectedPreSpliceSize = 55;
const base::TimeDelta kExpectedPreSpliceDuration =
base::TimeDelta::FromMicroseconds(1247);
VerifyPreSpliceOutput(overlapped_buffer, overlapping_buffer,
kExpectedPreSpliceSize, kExpectedPreSpliceDuration);
VerifyCrossfadeOutput(overlapped_buffer, NULL, overlapping_buffer, 0,
kExpectedPreSpliceSize, kExpectedPreSpliceDuration);
// Retrieve the remaining portion after crossfade.
ASSERT_TRUE(splicer_.HasNextBuffer());
scoped_refptr<AudioBuffer> post_splice_output = splicer_.GetNextBuffer();
EXPECT_EQ(overlapping_buffer->timestamp() + kExpectedPreSpliceDuration,
post_splice_output->timestamp());
EXPECT_EQ(overlapping_buffer->frame_count() - kExpectedPreSpliceSize,
post_splice_output->frame_count());
EXPECT_EQ(overlapping_buffer->duration() - kExpectedPreSpliceDuration,
post_splice_output->duration());
EXPECT_TRUE(VerifyData(post_splice_output, GetValue(overlapping_buffer)));
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test behavior when a splice frame is incorrectly marked and does not actually
// overlap.
// +----------+
// |1111111111|
// +----------+
// +--------------+
// |22222222222222|
// +--------------+
// Results in:
// +----------+--------------+
// |1111111111|22222222222222|
// +----------+--------------+
TEST_F(AudioSplicerTest, IncorrectlyMarkedSplice) {
const int kBufferSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration()) * 2;
scoped_refptr<AudioBuffer> first_buffer =
GetNextInputBuffer(1.0f, kBufferSize);
// Fuzz the duration slightly so that the buffer overlaps the splice timestamp
// by a microsecond, which is not enough to crossfade.
const base::TimeDelta kSpliceTimestamp =
input_timestamp_helper_.GetTimestamp() -
base::TimeDelta::FromMicroseconds(1);
splicer_.SetSpliceTimestamp(kSpliceTimestamp);
scoped_refptr<AudioBuffer> second_buffer =
GetNextInputBuffer(0.0f, kBufferSize);
second_buffer->set_timestamp(kSpliceTimestamp);
// The splicer should be internally queuing input since |first_buffer| is part
// of the supposed splice.
EXPECT_TRUE(AddInput(first_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// |second_buffer| should complete the supposed splice, so ensure output is
// now available.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_TRUE(AddInput(second_buffer));
VerifyNextBuffer(first_buffer);
VerifyNextBuffer(second_buffer);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test behavior when a splice frame is incorrectly marked and there is a gap
// between whats in the pre splice and post splice.
// +--------+
// |11111111|
// +--------+
// +--------------+
// |22222222222222|
// +--------------+
// Results in:
// +--------+-+--------------+
// |11111111|0|22222222222222|
// +--------+-+--------------+
TEST_F(AudioSplicerTest, IncorrectlyMarkedSpliceWithGap) {
const int kBufferSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration()) * 2;
const int kGapSize = 2;
scoped_refptr<AudioBuffer> first_buffer =
GetNextInputBuffer(1.0f, kBufferSize - kGapSize);
scoped_refptr<AudioBuffer> gap_buffer = GetNextInputBuffer(0.0f, kGapSize);
splicer_.SetSpliceTimestamp(input_timestamp_helper_.GetTimestamp());
scoped_refptr<AudioBuffer> second_buffer =
GetNextInputBuffer(0.0f, kBufferSize);
// The splicer should pass through the first buffer since it's not part of the
// splice.
EXPECT_TRUE(AddInput(first_buffer));
VerifyNextBuffer(first_buffer);
// Do not add |gap_buffer|.
// |second_buffer| will complete the supposed splice.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_TRUE(AddInput(second_buffer));
VerifyNextBuffer(gap_buffer);
VerifyNextBuffer(second_buffer);
EXPECT_FALSE(splicer_.HasNextBuffer());
}
// Test behavior when a splice frame is incorrectly marked and there is a gap
// between what's in the pre splice and post splice that is too large to recover
// from.
// +--------+
// |11111111|
// +--------+
// +------+
// |222222|
// +------+
// Results in an error and not a crash.
TEST_F(AudioSplicerTest, IncorrectlyMarkedSpliceWithBadGap) {
const int kBufferSize =
input_timestamp_helper_.GetFramesToTarget(max_crossfade_duration()) * 2;
const int kGapSize = kBufferSize +
input_timestamp_helper_.GetFramesToTarget(
base::TimeDelta::FromMilliseconds(
AudioSplicer::kMaxTimeDeltaInMilliseconds + 1));
scoped_refptr<AudioBuffer> first_buffer =
GetNextInputBuffer(1.0f, kBufferSize);
scoped_refptr<AudioBuffer> gap_buffer = GetNextInputBuffer(0.0f, kGapSize);
splicer_.SetSpliceTimestamp(input_timestamp_helper_.GetTimestamp());
scoped_refptr<AudioBuffer> second_buffer =
GetNextInputBuffer(0.0f, kBufferSize);
// The splicer should pass through the first buffer since it's not part of the
// splice.
EXPECT_TRUE(AddInput(first_buffer));
VerifyNextBuffer(first_buffer);
// Do not add |gap_buffer|.
// |second_buffer| will complete the supposed splice.
splicer_.SetSpliceTimestamp(kNoTimestamp);
EXPECT_FALSE(AddInput(second_buffer));
}
// Ensure we don't crash when a splice frame is incorrectly marked such that the
// splice timestamp has already passed when SetSpliceTimestamp() is called.
// This can happen if the encoded timestamps are too far behind the decoded
// timestamps.
TEST_F(AudioSplicerTest, IncorrectlyMarkedPastSplice) {
const int kBufferSize = 200;
scoped_refptr<AudioBuffer> first_buffer =
GetNextInputBuffer(1.0f, kBufferSize);
EXPECT_TRUE(AddInput(first_buffer));
VerifyNextBuffer(first_buffer);
// Start the splice at a timestamp which has already occurred.
splicer_.SetSpliceTimestamp(base::TimeDelta());
scoped_refptr<AudioBuffer> second_buffer =
GetNextInputBuffer(0.5f, kBufferSize);
EXPECT_TRUE(AddInput(second_buffer));
EXPECT_FALSE(splicer_.HasNextBuffer());
// |third_buffer| will complete the supposed splice. The buffer size is set
// such that unchecked the splicer would try to trim off a negative number of
// frames.
splicer_.SetSpliceTimestamp(kNoTimestamp);
scoped_refptr<AudioBuffer> third_buffer =
GetNextInputBuffer(0.0f, kBufferSize * 10);
third_buffer->set_timestamp(base::TimeDelta());
EXPECT_TRUE(AddInput(third_buffer));
// The second buffer should come through unmodified.
VerifyNextBuffer(second_buffer);
// The third buffer should be partially dropped since it overlaps the second.
ASSERT_TRUE(splicer_.HasNextBuffer());
const base::TimeDelta second_buffer_end_ts =
second_buffer->timestamp() + second_buffer->duration();
scoped_refptr<AudioBuffer> output = splicer_.GetNextBuffer();
EXPECT_EQ(second_buffer_end_ts, output->timestamp());
EXPECT_EQ(third_buffer->duration() -
(second_buffer_end_ts - third_buffer->timestamp()),
output->duration());
EXPECT_TRUE(VerifyData(output, GetValue(third_buffer)));
}
} // namespace media
} // namespace cobalt