blob: d1a05c815906a9202648c1bf5512aa4ea9f3b84a [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
#include <functional>
#include <set>
#include "base/logging.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/media.h"
#include "starboard/memory.h"
#include "starboard/shared/starboard/media/media_util.h"
#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
#include "starboard/shared/starboard/player/filter/mock_audio_decoder.h"
#include "starboard/shared/starboard/player/filter/mock_audio_renderer_sink.h"
#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace filter {
namespace testing {
namespace {
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
// TODO: Write tests to cover callbacks.
class AudioRendererTest : public ::testing::Test {
protected:
static const int kDefaultNumberOfChannels = 2;
static const int kDefaultSamplesPerSecond;
static const SbMediaAudioSampleType kDefaultAudioSampleType =
kSbMediaAudioSampleTypeFloat32;
static const SbMediaAudioFrameStorageType kDefaultAudioFrameStorageType =
kSbMediaAudioFrameStorageTypeInterleaved;
AudioRendererTest() {
ResetToFormat(kSbMediaAudioSampleTypeFloat32,
kSbMediaAudioFrameStorageTypeInterleaved);
}
// This function should be called in the fixture before any other functions
// to set the desired format of the decoder.
void ResetToFormat(SbMediaAudioSampleType sample_type,
SbMediaAudioFrameStorageType storage_type) {
audio_renderer_.reset(NULL);
sample_type_ = sample_type;
storage_type_ = storage_type;
audio_renderer_sink_ = new ::testing::StrictMock<MockAudioRendererSink>;
audio_decoder_ = new MockAudioDecoder(sample_type_, storage_type_,
kDefaultSamplesPerSecond);
ON_CALL(*audio_decoder_, Read(_))
.WillByDefault(
DoAll(SetArgPointee<0>(kDefaultSamplesPerSecond),
Return(scoped_refptr<DecodedAudio>(new DecodedAudio()))));
ON_CALL(*audio_renderer_sink_, Start(_, _, _, _, _, _, _, _))
.WillByDefault(DoAll(InvokeWithoutArgs([this]() {
audio_renderer_sink_->SetHasStarted(true);
}),
SaveArg<7>(&renderer_callback_)));
ON_CALL(*audio_renderer_sink_, Stop())
.WillByDefault(InvokeWithoutArgs([this]() {
audio_renderer_sink_->SetHasStarted(false);
})); // NOLINT
ON_CALL(*audio_renderer_sink_, HasStarted())
.WillByDefault(::testing::ReturnPointee(
audio_renderer_sink_->HasStartedPointer()));
EXPECT_CALL(*audio_renderer_sink_, HasStarted()).Times(AnyNumber());
// This allows audio renderers to query different sample types, and only
// same type of float32 will be returned as supported.
ON_CALL(*audio_renderer_sink_,
IsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32))
.WillByDefault(Return(true));
EXPECT_CALL(*audio_renderer_sink_, IsAudioSampleTypeSupported(_))
.Times(AnyNumber());
// This allows audio renderers to query different sample types, and only
// sample frequency of 100Khz will be supported.
const int kSupportedSampleFrequency = 100000;
ON_CALL(*audio_renderer_sink_,
GetNearestSupportedSampleFrequency(kSupportedSampleFrequency))
.WillByDefault(Return(kSupportedSampleFrequency));
EXPECT_CALL(*audio_renderer_sink_, GetNearestSupportedSampleFrequency(_))
.Times(AnyNumber());
EXPECT_CALL(*audio_decoder_, Initialize(_, _))
.WillOnce(SaveArg<0>(&output_cb_));
const int kMaxCachedFrames = 256 * 1024;
const int kMaxFramesPerAppend = 16384;
audio_renderer_.reset(new AudioRendererImpl(
make_scoped_ptr<AudioDecoder>(audio_decoder_),
make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_),
GetDefaultAudioSampleInfo(), kMaxCachedFrames, kMaxFramesPerAppend));
audio_renderer_->Initialize(
std::bind(&AudioRendererTest::OnError, this),
std::bind(&AudioRendererTest::OnPrerolled, this),
std::bind(&AudioRendererTest::OnEnded, this));
}
// Creates audio buffers, decodes them, and passes them onto the renderer,
// until the renderer reaches its preroll threshold.
// Once the renderer is "full", an EndOfStream is written.
// Returns the number of frames written.
int FillRendererWithDecodedAudioAndWriteEOS(SbTime start_timestamp) {
const int kFramesPerBuffer = 1024;
int frames_written = 0;
while (!prerolled_) {
SbTime timestamp = start_timestamp + frames_written * kSbTimeSecond /
kDefaultSamplesPerSecond;
scoped_refptr<InputBuffer> input_buffer = CreateInputBuffer(timestamp);
WriteSample(input_buffer);
CallConsumedCB();
scoped_refptr<DecodedAudio> decoded_audio =
CreateDecodedAudio(timestamp, kFramesPerBuffer);
SendDecoderOutput(decoded_audio);
frames_written += kFramesPerBuffer;
}
WriteEndOfStream();
return frames_written;
}
void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) {
ASSERT_TRUE(input_buffer != NULL);
ASSERT_FALSE(consumed_cb_);
buffers_in_decoder_.insert(input_buffer->data());
EXPECT_CALL(*audio_decoder_, Decode(input_buffer, _))
.WillOnce(SaveArg<1>(&consumed_cb_));
audio_renderer_->WriteSample(input_buffer);
job_queue_.RunUntilIdle();
ASSERT_TRUE(consumed_cb_);
}
void WriteEndOfStream() {
EXPECT_CALL(*audio_decoder_, WriteEndOfStream());
audio_renderer_->WriteEndOfStream();
job_queue_.RunUntilIdle();
}
void Seek(SbTime seek_to_time) {
EXPECT_TRUE(prerolled_);
prerolled_ = false;
audio_renderer_->Seek(seek_to_time);
job_queue_.RunUntilIdle();
EXPECT_FALSE(prerolled_);
}
void CallConsumedCB() {
ASSERT_TRUE(consumed_cb_);
consumed_cb_();
consumed_cb_ = nullptr;
job_queue_.RunUntilIdle();
}
void SendDecoderOutput(const scoped_refptr<DecodedAudio>& decoded_audio) {
ASSERT_TRUE(output_cb_);
EXPECT_CALL(*audio_decoder_, Read(_))
.WillOnce(DoAll(SetArgPointee<0>(kDefaultSamplesPerSecond),
Return(decoded_audio)));
output_cb_();
job_queue_.RunUntilIdle();
}
scoped_refptr<InputBuffer> CreateInputBuffer(SbTime timestamp) {
const int kInputBufferSize = 4;
SbPlayerSampleInfo sample_info = {};
sample_info.buffer = SbMemoryAllocate(kInputBufferSize);
sample_info.buffer_size = kInputBufferSize;
sample_info.timestamp = timestamp;
sample_info.drm_info = NULL;
sample_info.type = kSbMediaTypeAudio;
sample_info.audio_sample_info = GetDefaultAudioSampleInfo();
return new InputBuffer(DeallocateSampleCB, NULL, this, sample_info);
}
scoped_refptr<DecodedAudio> CreateDecodedAudio(SbTime timestamp, int frames) {
scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
kDefaultNumberOfChannels, sample_type_, storage_type_, timestamp,
frames * kDefaultNumberOfChannels *
media::GetBytesPerSample(sample_type_));
SbMemorySet(decoded_audio->buffer(), 0, decoded_audio->size());
return decoded_audio;
}
void OnError() {}
void OnPrerolled() {
SB_DCHECK(job_queue_.BelongsToCurrentThread());
prerolled_ = true;
}
void OnEnded() {}
SbMediaAudioSampleType sample_type_;
SbMediaAudioFrameStorageType storage_type_;
JobQueue job_queue_;
std::set<const void*> buffers_in_decoder_;
AudioDecoder::OutputCB output_cb_;
AudioDecoder::ConsumedCB consumed_cb_;
bool prerolled_ = true;
scoped_ptr<AudioRendererImpl> audio_renderer_;
MockAudioDecoder* audio_decoder_;
MockAudioRendererSink* audio_renderer_sink_;
AudioRendererSink::RenderCallback* renderer_callback_;
void OnDeallocateSample(const void* sample_buffer) {
ASSERT_TRUE(buffers_in_decoder_.find(sample_buffer) !=
buffers_in_decoder_.end());
buffers_in_decoder_.erase(buffers_in_decoder_.find(sample_buffer));
SbMemoryDeallocate(const_cast<void*>(sample_buffer));
}
static const SbMediaAudioSampleInfo& GetDefaultAudioSampleInfo() {
static starboard::media::AudioSampleInfo audio_sample_info = {};
audio_sample_info.codec = kSbMediaAudioCodecAac;
#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
audio_sample_info.mime = "";
#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
audio_sample_info.number_of_channels = kDefaultNumberOfChannels;
audio_sample_info.samples_per_second = kDefaultSamplesPerSecond;
audio_sample_info.bits_per_sample = 32;
audio_sample_info.average_bytes_per_second =
audio_sample_info.samples_per_second *
audio_sample_info.number_of_channels *
audio_sample_info.bits_per_sample / 8;
audio_sample_info.block_alignment = 4;
audio_sample_info.audio_specific_config_size = 0;
return audio_sample_info;
}
static void DeallocateSampleCB(SbPlayer player,
void* context,
const void* sample_buffer) {
AudioRendererTest* test = static_cast<AudioRendererTest*>(context);
EXPECT_TRUE(test != NULL);
test->OnDeallocateSample(sample_buffer);
}
};
bool HasAsyncAudioFramesReporting() {
#if SB_API_VERSION >= 12
// TODO: When deprecating Starboard API versions less than
// 12 it is safe to assume that all tests can be run regardless of
// whether the platform has asynchronous audio frames reporting.
// This function can be removed then.
return false;
#else // SB_API_VERSION >= 12
#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
return true;
#else // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
return false;
#endif // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
#endif // SB_API_VERSION >= 12
}
// static
const int AudioRendererTest::kDefaultSamplesPerSecond = 100000;
TEST_F(AudioRendererTest, StateAfterConstructed) {
EXPECT_FALSE(audio_renderer_->IsEndOfStreamWritten());
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
EXPECT_TRUE(audio_renderer_->CanAcceptMoreData());
bool is_playing = true;
bool is_eos_played = true;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
}
TEST_F(AudioRendererTest, SunnyDay) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
bool is_playing = true;
bool is_eos_played = true;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
// Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
// is incrementing in an expected manner.
const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GT(new_media_time, media_time);
media_time = new_media_time;
const int remaining_frames = frames_in_buffer - frames_to_consume;
renderer_callback_->ConsumeFrames(remaining_frames, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GT(new_media_time, media_time);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
TEST_F(AudioRendererTest, SunnyDayWithDoublePlaybackRateAndInt16Samples) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
const int kPlaybackRate = 2;
// Resets |audio_renderer_sink_|, so all the gtest codes need to be below
// this line.
ResetToFormat(kSbMediaAudioSampleTypeInt16,
kSbMediaAudioFrameStorageTypeInterleaved);
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
// It is OK to set the rate to 1.0 any number of times.
EXPECT_CALL(*audio_renderer_sink_, SetPlaybackRate(1.0)).Times(AnyNumber());
audio_renderer_->SetPlaybackRate(static_cast<float>(kPlaybackRate));
Seek(0);
int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
bool is_playing = false;
bool is_eos_played = true;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, kPlaybackRate);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
// Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
// is incrementing in an expected manner.
const int frames_to_consume =
std::min(frames_written / kPlaybackRate, frames_in_buffer) / 2;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GT(new_media_time, media_time);
media_time = new_media_time;
const int remaining_frames = frames_in_buffer - frames_to_consume;
renderer_callback_->ConsumeFrames(remaining_frames, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GT(new_media_time, media_time);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
TEST_F(AudioRendererTest, StartPlayBeforePreroll) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
audio_renderer_->Play();
int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
SendDecoderOutput(new DecodedAudio);
bool is_playing = false;
bool is_eos_played = true;
bool is_underflow = true;
double playback_rate = -1.0;
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
// Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
// is incrementing in an expected manner.
const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GE(new_media_time, media_time);
media_time = new_media_time;
const int remaining_frames = frames_in_buffer - frames_to_consume;
renderer_callback_->ConsumeFrames(remaining_frames, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GE(new_media_time, media_time);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
TEST_F(AudioRendererTest, DecoderReturnsEOSWithoutAnyData) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
WriteSample(CreateInputBuffer(0));
CallConsumedCB();
WriteEndOfStream();
EXPECT_TRUE(audio_renderer_->IsEndOfStreamWritten());
EXPECT_FALSE(audio_renderer_->CanAcceptMoreData());
EXPECT_FALSE(prerolled_);
// Return EOS from decoder without sending any audio data, which is valid.
SendDecoderOutput(new DecodedAudio);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
EXPECT_TRUE(prerolled_);
bool is_playing = true;
bool is_eos_played = false;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_TRUE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
}
// Test decoders that take many input samples before returning any output.
TEST_F(AudioRendererTest, DecoderConsumeAllInputBeforeReturningData) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
for (int i = 0; i < 128; ++i) {
WriteSample(CreateInputBuffer(i));
CallConsumedCB();
if (!audio_renderer_->CanAcceptMoreData()) {
break;
}
}
// Send an EOS to "force" the decoder return data.
WriteEndOfStream();
EXPECT_TRUE(audio_renderer_->IsEndOfStreamWritten());
EXPECT_FALSE(audio_renderer_->CanAcceptMoreData());
EXPECT_FALSE(prerolled_);
// Return EOS from decoder without sending any audio data, which is valid.
SendDecoderOutput(new DecodedAudio);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
EXPECT_TRUE(prerolled_);
bool is_playing = true;
bool is_eos_played = false;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_TRUE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
}
TEST_F(AudioRendererTest, MoreNumberOfOutputBuffersThanInputBuffers) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
const int kFramesPerBuffer = 1024;
int frames_written = 0;
while (!prerolled_) {
SbTime timestamp =
frames_written * kSbTimeSecond / kDefaultSamplesPerSecond;
WriteSample(CreateInputBuffer(timestamp));
CallConsumedCB();
SendDecoderOutput(CreateDecodedAudio(timestamp, kFramesPerBuffer / 2));
frames_written += kFramesPerBuffer / 2;
timestamp = frames_written * kSbTimeSecond / kDefaultSamplesPerSecond;
SendDecoderOutput(CreateDecodedAudio(timestamp, kFramesPerBuffer / 2));
frames_written += kFramesPerBuffer / 2;
}
WriteEndOfStream();
bool is_playing = true;
bool is_eos_played = true;
bool is_underflow = true;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_FALSE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_TRUE(is_eos_reached);
// Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
// is incrementing in an expected manner.
const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_FALSE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GE(new_media_time, media_time);
media_time = new_media_time;
const int remaining_frames = frames_in_buffer - frames_to_consume;
renderer_callback_->ConsumeFrames(remaining_frames, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_played);
EXPECT_EQ(playback_rate, 1.0);
EXPECT_GE(new_media_time, media_time);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
TEST_F(AudioRendererTest, LessNumberOfOutputBuffersThanInputBuffers) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(*audio_renderer_sink_, HasStarted())
.WillRepeatedly(Return(false));
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _))
.WillOnce(SaveArg<7>(&renderer_callback_));
EXPECT_CALL(*audio_renderer_sink_, HasStarted())
.WillRepeatedly(Return(true));
}
Seek(0);
const int kFramesPerBuffer = 1024;
int frames_written = 0;
while (!prerolled_) {
SbTime timestamp =
frames_written * kSbTimeSecond / kDefaultSamplesPerSecond;
SbTime output_time = timestamp;
WriteSample(CreateInputBuffer(timestamp));
CallConsumedCB();
frames_written += kFramesPerBuffer / 2;
timestamp = frames_written * kSbTimeSecond / kDefaultSamplesPerSecond;
WriteSample(CreateInputBuffer(timestamp));
CallConsumedCB();
frames_written += kFramesPerBuffer / 2;
SendDecoderOutput(CreateDecodedAudio(output_time, kFramesPerBuffer));
}
WriteEndOfStream();
bool is_playing;
bool is_eos_played;
bool is_underflow;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
// Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
// is incrementing in an expected manner.
const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GE(new_media_time, media_time);
media_time = new_media_time;
const int remaining_frames = frames_in_buffer - frames_to_consume;
renderer_callback_->ConsumeFrames(remaining_frames, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GE(new_media_time, media_time);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
TEST_F(AudioRendererTest, Seek) {
if (HasAsyncAudioFramesReporting()) {
SB_LOG(INFO) << "Platform has async audio frames reporting. Test skipped.";
return;
}
const double kSeekTime = 0.5 * kSbTimeSecond;
{
::testing::InSequence seq;
EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
EXPECT_CALL(
*audio_renderer_sink_,
Start(0, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
EXPECT_CALL(*audio_renderer_sink_, Stop());
EXPECT_CALL(*audio_decoder_, Reset());
EXPECT_CALL(
*audio_renderer_sink_,
Start(kSeekTime, kDefaultNumberOfChannels, kDefaultSamplesPerSecond,
kDefaultAudioSampleType, kDefaultAudioFrameStorageType, _, _, _));
}
Seek(0);
int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
bool is_playing;
bool is_eos_played;
bool is_underflow;
double playback_rate = -1.0;
EXPECT_EQ(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
0);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
SbTime media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
int frames_in_buffer;
int offset_in_frames;
bool is_eos_reached;
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
// Consume frames in multiple batches, so we can test if
// |GetCurrentMediaTime()| is incrementing in an expected manner.
const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 10;
SbTime new_media_time;
EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
renderer_callback_->ConsumeFrames(frames_to_consume, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GE(new_media_time, media_time);
Seek(kSeekTime);
frames_written += FillRendererWithDecodedAudioAndWriteEOS(kSeekTime);
EXPECT_GE(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
&is_underflow, &playback_rate),
kSeekTime);
EXPECT_TRUE(prerolled_);
audio_renderer_->Play();
SendDecoderOutput(new DecodedAudio);
renderer_callback_->GetSourceStatus(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached);
EXPECT_GT(frames_in_buffer, 0);
EXPECT_GE(offset_in_frames, 0);
EXPECT_TRUE(is_playing);
EXPECT_TRUE(is_eos_reached);
renderer_callback_->ConsumeFrames(frames_in_buffer, SbTimeGetMonotonicNow());
new_media_time = audio_renderer_->GetCurrentMediaTime(
&is_playing, &is_eos_played, &is_underflow, &playback_rate);
EXPECT_GE(new_media_time, kSeekTime);
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
// TODO: Add more Seek tests.
} // namespace
} // namespace testing
} // namespace filter
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard