| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| /** \file BufferQueue_test.cpp */ |
| |
| #define LOG_NDEBUG 0 |
| #define LOG_TAG "BufferQueue_test" |
| |
| #ifdef ANDROID |
| #include <utils/Log.h> |
| #else |
| #define ALOGV printf |
| #endif |
| |
| #include <assert.h> |
| #include <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <SLES/OpenSLES.h> |
| #include "OpenSLESUT.h" |
| #include <gtest/gtest.h> |
| |
| typedef struct { |
| short left; |
| short right; |
| } stereo; |
| |
| // volume of sine wave in range 0.0 to 1.0 |
| static float gVolume = 1.0f; |
| |
| // 1 second of stereo audio at 44.1 kHz |
| static stereo stereoBuffer1[44100 * 1]; |
| static const SLuint32 invalidNumBuffers[] = { 0, 0xFFFFFFFF, 0x80000000, 0x10002, 0x102, |
| 0x101, 0x100 }; |
| static const SLuint32 validNumBuffers[] = { 1, 2, 3, 4, 5, 6, 7, 8, 255 }; |
| |
| //----------------------------------------------------------------- |
| /* Checks for error. If any errors exit the application! */ |
| void CheckErr(SLresult res) { |
| if (SL_RESULT_SUCCESS != res) { |
| const char *str = slesutResultToString(res); |
| if (NULL == str) |
| str = "unknown"; |
| fprintf(stderr, "CheckErr failure: %s (0x%x), exiting\n", str, res); |
| //Fail the test case |
| FAIL(); |
| } |
| } |
| |
| static const SLInterfaceID ids[1] = { SL_IID_BUFFERQUEUE }; |
| static const SLboolean flags[1] = { SL_BOOLEAN_TRUE }; |
| static const SLInterfaceID ids_mutesolo[2] = { SL_IID_BUFFERQUEUE, SL_IID_MUTESOLO }; |
| static const SLboolean flags_mutesolo[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; |
| static const SLInterfaceID ids_seek[2] = { SL_IID_BUFFERQUEUE, SL_IID_SEEK }; |
| static const SLboolean flags_seek[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; |
| |
| // The fixture for testing class BufferQueue |
| class TestBufferQueue: public ::testing::Test { |
| public: |
| SLresult res; |
| SLObjectItf outputmixObject; |
| SLObjectItf engineObject; |
| |
| SLDataSource audiosrc; |
| SLDataSink audiosnk; |
| SLDataFormat_PCM pcm; |
| SLDataLocator_OutputMix locator_outputmix; |
| SLDataLocator_BufferQueue locator_bufferqueue; |
| SLBufferQueueItf playerBufferQueue; |
| SLBufferQueueState bufferqueueState; |
| SLPlayItf playerPlay; |
| SLObjectItf playerObject; |
| SLEngineItf engineEngine; |
| SLuint32 playerState; |
| |
| protected: |
| TestBufferQueue() { |
| } |
| |
| virtual ~TestBufferQueue() { |
| |
| } |
| |
| /*Test setup*/ |
| virtual void SetUp() { |
| |
| // create engine |
| res = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); |
| CheckErr(res); |
| res = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); |
| CheckErr(res); |
| res = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); |
| CheckErr(res); |
| |
| // create output mix |
| res = (*engineEngine)->CreateOutputMix(engineEngine, &outputmixObject, 0, NULL, NULL); |
| CheckErr(res); |
| res = (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE); |
| CheckErr(res); |
| |
| locator_bufferqueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE; |
| locator_bufferqueue.numBuffers = 0; |
| locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
| locator_outputmix.outputMix = outputmixObject; |
| |
| pcm.formatType = SL_DATAFORMAT_PCM; |
| pcm.numChannels = 2; |
| pcm.samplesPerSec = SL_SAMPLINGRATE_44_1; |
| pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; |
| pcm.containerSize = 16; |
| pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
| pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; |
| |
| audiosrc.pLocator = &locator_bufferqueue; |
| audiosrc.pFormat = &pcm; |
| audiosnk.pLocator = &locator_outputmix; |
| audiosnk.pFormat = NULL; |
| |
| // initialize the test tone to be a sine sweep from 441 Hz to 882 Hz |
| unsigned nframes = sizeof(stereoBuffer1) / sizeof(stereoBuffer1[0]); |
| float nframes_ = (float) nframes; |
| SLuint32 i; |
| for (i = 0; i < nframes; ++i) { |
| float i_ = (float) i; |
| float pcm_ = sin((i_ * (1.0f + 0.5f * (i_ / nframes_)) * 0.01 * M_PI * 2.0)); |
| int pcm = (int) (pcm_ * 32766.0 * gVolume); |
| ASSERT_TRUE(-32768 <= pcm && pcm <= 32767) << "pcm out of bound " << pcm; |
| stereoBuffer1[i].left = pcm; |
| stereoBuffer1[nframes - 1 - i].right = pcm; |
| } |
| } |
| |
| virtual void TearDown() { |
| // Clean up the mixer and the engine |
| // (must be done in that order, and after player destroyed) |
| if (outputmixObject){ |
| (*outputmixObject)->Destroy(outputmixObject); |
| outputmixObject = NULL; |
| } |
| if (engineObject){ |
| (*engineObject)->Destroy(engineObject); |
| engineObject = NULL; |
| } |
| } |
| |
| void DestroyPlayer() { |
| if (playerObject){ |
| //printf("destroy player\n"); |
| (*playerObject)->Destroy(playerObject); |
| playerObject = NULL; |
| } |
| } |
| |
| /* Test case for creating audio player with various invalid values for numBuffers*/ |
| void InvalidBuffer() { |
| |
| for (unsigned i = 0; i < sizeof(invalidNumBuffers) / sizeof(invalidNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = invalidNumBuffers[i]; |
| |
| locator_bufferqueue.numBuffers = numBuffers; |
| //printf("create audio player - invalid\n"); |
| SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, |
| &audiosrc, &audiosnk, 1, ids, flags); |
| ASSERT_EQ(SL_RESULT_PARAMETER_INVALID, result); |
| ASSERT_EQ(NULL, playerObject); |
| |
| } |
| } |
| |
| /*Prepare the buffer*/ |
| void PrepareValidBuffer(SLuint32 numBuffers) { |
| |
| locator_bufferqueue.numBuffers = numBuffers; |
| //printf("create audio player - valid\n"); |
| res = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk, |
| 1, ids, flags); |
| CheckErr(res); |
| res = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE); |
| CheckErr(res); |
| // get the play interface |
| res = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay); |
| CheckErr(res); |
| // verify that player is initially stopped |
| res = (*playerPlay)->GetPlayState(playerPlay, &playerState); |
| CheckErr(res); |
| ASSERT_EQ(SL_PLAYSTATE_STOPPED, playerState); |
| |
| // get the buffer queue interface |
| res = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue); |
| CheckErr(res); |
| |
| // verify that buffer queue is initially empty |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.playIndex); |
| } |
| |
| void EnqueueMaxBuffer(SLuint32 numBuffers) { |
| SLuint32 j; |
| |
| for (j = 0; j < numBuffers; ++j) { |
| res = (*playerBufferQueue)->Enqueue(playerBufferQueue, "test", 4); |
| CheckErr(res); |
| // verify that each buffer is enqueued properly and increments the buffer count |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ(j + 1, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.playIndex); |
| } |
| } |
| |
| void EnqueueExtraBuffer(SLuint32 numBuffers) { |
| // enqueue one more buffer and make sure it fails |
| res = (*playerBufferQueue)->Enqueue(playerBufferQueue, "test", 4); |
| ASSERT_EQ(SL_RESULT_BUFFER_INSUFFICIENT, res); |
| // verify that the failed enqueue did not affect the buffer count |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ(numBuffers, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.playIndex); |
| } |
| |
| void SetPlayerState(SLuint32 state) { |
| res = (*playerPlay)->SetPlayState(playerPlay, state); |
| CheckErr(res); |
| //verify the state can set correctly |
| GetPlayerState(state); |
| } |
| |
| void GetPlayerState(SLuint32 state) { |
| res = (*playerPlay)->GetPlayState(playerPlay, &playerState); |
| CheckErr(res); |
| ASSERT_EQ(state, playerState); |
| } |
| |
| void ClearQueue() { |
| // now clear the buffer queue |
| res = (*playerBufferQueue)->Clear(playerBufferQueue); |
| CheckErr(res); |
| // make sure the clear works |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.playIndex); |
| } |
| |
| void CheckBufferCount(SLuint32 ExpectedCount, SLuint32 ExpectedPlayIndex) { |
| // changing the play state should not affect the buffer count |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ(ExpectedCount, bufferqueueState.count); |
| ASSERT_EQ(ExpectedPlayIndex, bufferqueueState.playIndex); |
| } |
| |
| void PlayBufferQueue() { |
| // enqueue a buffer |
| res = (*playerBufferQueue)->Enqueue(playerBufferQueue, stereoBuffer1, |
| sizeof(stereoBuffer1)); |
| CheckErr(res); |
| // set play state to playing |
| res = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); |
| CheckErr(res); |
| // state should be playing immediately after enqueue |
| res = (*playerPlay)->GetPlayState(playerPlay, &playerState); |
| CheckErr(res); |
| ASSERT_EQ(SL_PLAYSTATE_PLAYING, playerState); |
| // buffer should still be on the queue |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ((SLuint32) 1, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.playIndex); |
| //ALOGV("Before 1.5 sec"); |
| // wait 1.5 seconds |
| usleep(1500000); |
| //ALOGV("After 1.5 sec"); |
| // state should still be playing |
| res = (*playerPlay)->GetPlayState(playerPlay, &playerState); |
| //ALOGV("GetPlayState"); |
| CheckErr(res); |
| ASSERT_EQ(SL_PLAYSTATE_PLAYING, playerState); |
| // buffer should be removed from the queue |
| res = (*playerBufferQueue)->GetState(playerBufferQueue, &bufferqueueState); |
| CheckErr(res); |
| ASSERT_EQ((SLuint32) 0, bufferqueueState.count); |
| ASSERT_EQ((SLuint32) 1, bufferqueueState.playIndex); |
| //ALOGV("TestEnd"); |
| } |
| }; |
| |
| TEST_F(TestBufferQueue, testInvalidBuffer){ |
| //ALOGV("Test Fixture: InvalidBuffer"); |
| InvalidBuffer(); |
| } |
| |
| TEST_F(TestBufferQueue, testMuteSolo) { |
| // create audio player with buffer queue data source in stereo PCM format and ask for mute solo |
| locator_bufferqueue.numBuffers = 1; |
| SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, |
| &audiosnk, 2, ids_mutesolo, flags_mutesolo); |
| ASSERT_EQ(SL_RESULT_SUCCESS, result); |
| ASSERT_TRUE(NULL != playerObject); |
| DestroyPlayer(); |
| // create audio player with buffer queue data source in mono PCM format and ask for mute solo |
| pcm.numChannels = 1; |
| pcm.channelMask = SL_SPEAKER_FRONT_CENTER; |
| result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk, |
| 2, ids_mutesolo, flags_mutesolo); |
| ASSERT_EQ(SL_RESULT_FEATURE_UNSUPPORTED, result); |
| ASSERT_EQ(NULL, playerObject); |
| DestroyPlayer(); |
| } |
| |
| TEST_F(TestBufferQueue, testSeek) { |
| // can create audio player with buffer queue data source and ask for seek |
| locator_bufferqueue.numBuffers = 1; |
| SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, |
| &audiosrc, &audiosnk, 2, ids_seek, flags_seek); |
| ASSERT_EQ(SL_RESULT_FEATURE_UNSUPPORTED, result); |
| ASSERT_EQ(NULL, playerObject); |
| DestroyPlayer(); |
| } |
| |
| TEST_F(TestBufferQueue, testValidBuffer) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testEnqueueMaxBuffer) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| EnqueueMaxBuffer(numBuffers); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testEnqueueExtraBuffer) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| EnqueueMaxBuffer(numBuffers); |
| EnqueueExtraBuffer(numBuffers); |
| GetPlayerState(SL_PLAYSTATE_STOPPED); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testEnqueueAtStopped) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| SetPlayerState(SL_PLAYSTATE_STOPPED); |
| EnqueueMaxBuffer(numBuffers); |
| CheckBufferCount(numBuffers, (SLuint32) 0); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testEnqueueAtPaused) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| SetPlayerState(SL_PLAYSTATE_PAUSED); |
| EnqueueMaxBuffer(numBuffers); |
| CheckBufferCount(numBuffers, (SLuint32) 0); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testClearQueue) { |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| EnqueueMaxBuffer(numBuffers); |
| ClearQueue(); |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testStateTransitionEmptyQueue) { |
| static const SLuint32 newStates[] = { |
| SL_PLAYSTATE_PAUSED, // paused -> paused |
| SL_PLAYSTATE_STOPPED, // paused -> stopped |
| SL_PLAYSTATE_PAUSED, // stopped -> paused |
| SL_PLAYSTATE_PLAYING, // paused -> playing |
| SL_PLAYSTATE_PLAYING, // playing -> playing |
| SL_PLAYSTATE_STOPPED, // playing -> stopped |
| SL_PLAYSTATE_STOPPED, // stopped -> stopped |
| SL_PLAYSTATE_PLAYING, // stopped -> playing |
| SL_PLAYSTATE_PAUSED // playing -> paused |
| }; |
| |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| SLuint32 j; |
| |
| PrepareValidBuffer(numBuffers); |
| /* Set initial state to paused*/ |
| SetPlayerState(SL_PLAYSTATE_PAUSED); |
| |
| for (j = 0; j < sizeof(newStates) / sizeof(newStates[0]); ++j) { |
| SetPlayerState(newStates[j]); |
| CheckBufferCount((SLuint32) 0, (SLuint32) 0); |
| } |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testStateTransitionNonEmptyQueue) { |
| static const SLuint32 newStates[] = { |
| SL_PLAYSTATE_PAUSED, // paused -> paused |
| SL_PLAYSTATE_STOPPED, // paused -> stopped |
| SL_PLAYSTATE_STOPPED, // stopped -> stopped |
| SL_PLAYSTATE_PAUSED // stopped -> paused |
| }; |
| |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| SLuint32 j; |
| |
| /* Prepare the player */ |
| PrepareValidBuffer(numBuffers); |
| EnqueueMaxBuffer(numBuffers); |
| SetPlayerState(SL_PLAYSTATE_PAUSED); |
| |
| for (j = 0; j < sizeof(newStates) / sizeof(newStates[0]); ++j) { |
| SetPlayerState(newStates[j]); |
| CheckBufferCount(numBuffers, (SLuint32) 0); |
| } |
| DestroyPlayer(); |
| } |
| } |
| |
| TEST_F(TestBufferQueue, testStatePlayBuffer){ |
| for (unsigned i = 0; i < sizeof(validNumBuffers) / sizeof(validNumBuffers[0]); ++i) { |
| SLuint32 numBuffers = validNumBuffers[i]; |
| PrepareValidBuffer(numBuffers); |
| PlayBufferQueue(); |
| DestroyPlayer(); |
| } |
| } |
| |
| int main(int argc, char **argv) { |
| testing::InitGoogleTest(&argc, argv); |
| #if 1 // temporary workaround if hardware volume control is not working |
| const char *VOLUME = getenv("BufferQueue_test_VOLUME"); |
| if (NULL != VOLUME) { |
| float volume = atof(VOLUME); |
| if (volume >= 0.0f && volume <= 1.0f) { |
| gVolume = volume; |
| } |
| } |
| #endif |
| return RUN_ALL_TESTS(); |
| } |