| // 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 |