You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
10 KiB
315 lines
10 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2009 The Khronos Group Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
* software and /or associated documentation files (the "Materials "), to deal in the
|
|
* Materials without restriction, including without limitation the rights to use, copy,
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials,
|
|
* and to permit persons to whom the Materials are furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Materials.
|
|
*
|
|
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE
|
|
* MATERIALS.
|
|
*/
|
|
|
|
#define LOG_NDEBUG 0
|
|
#define LOG_TAG "slesTestPlayUri"
|
|
|
|
#include <utils/Log.h>
|
|
#include <getopt.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <gtest/gtest.h>
|
|
#include <SLES/OpenSLES.h>
|
|
|
|
|
|
#define MAX_NUMBER_INTERFACES 3
|
|
#define MAX_NUMBER_OUTPUT_DEVICES 6
|
|
|
|
//The expected playback duration
|
|
const int MP3_DURATION = 71030; //71 secs
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
/* Checks for error. If any errors exit the application! */
|
|
void CheckErr( SLresult res )
|
|
{
|
|
if ( res != SL_RESULT_SUCCESS ) {
|
|
fprintf(stderr, "%u SL failure, exiting\n", res);
|
|
//Fail the test case
|
|
ASSERT_TRUE(false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
/* PrefetchStatusItf callback for an audio player */
|
|
void PrefetchEventCallback( SLPrefetchStatusItf caller, void *pContext __unused, SLuint32 event)
|
|
{
|
|
SLpermille level = 0;
|
|
(*caller)->GetFillLevel(caller, &level);
|
|
SLuint32 status;
|
|
fprintf(stdout, "\t\tPrefetchEventCallback: received event %u\n", event);
|
|
(*caller)->GetPrefetchStatus(caller, &status);
|
|
if ((event & (SL_PREFETCHEVENT_STATUSCHANGE|SL_PREFETCHEVENT_FILLLEVELCHANGE))
|
|
&& (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
|
|
fprintf(stderr, "\t\tPrefetchEventCallback: Error while prefetching data, exiting\n");
|
|
ASSERT_TRUE(false);
|
|
}
|
|
if (event & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
|
|
fprintf(stdout, "\t\tPrefetchEventCallback: Buffer fill level is = %d\n", level);
|
|
}
|
|
if (event & SL_PREFETCHEVENT_STATUSCHANGE) {
|
|
fprintf(stdout, "\t\tPrefetchEventCallback: Prefetch Status is = %u\n", status);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
/* Play some music from a URI */
|
|
void TestPlayUri( SLObjectItf sl, const char* path)
|
|
{
|
|
SLEngineItf EngineItf;
|
|
|
|
SLresult res;
|
|
|
|
SLDataSource audioSource;
|
|
SLDataLocator_URI uri;
|
|
SLDataFormat_MIME mime;
|
|
|
|
SLDataSink audioSink;
|
|
SLDataLocator_OutputMix locator_outputmix;
|
|
|
|
SLObjectItf player;
|
|
SLPlayItf playItf;
|
|
SLVolumeItf volItf;
|
|
SLPrefetchStatusItf prefetchItf;
|
|
|
|
SLObjectItf OutputMix;
|
|
|
|
SLboolean required[MAX_NUMBER_INTERFACES];
|
|
SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
|
|
|
|
/* Get the SL Engine Interface which is implicit */
|
|
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
|
|
CheckErr(res);
|
|
|
|
/* Initialize arrays required[] and iidArray[] */
|
|
for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) {
|
|
required[i] = SL_BOOLEAN_FALSE;
|
|
iidArray[i] = SL_IID_NULL;
|
|
}
|
|
|
|
// Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface
|
|
required[0] = SL_BOOLEAN_TRUE;
|
|
iidArray[0] = SL_IID_VOLUME;
|
|
required[1] = SL_BOOLEAN_TRUE;
|
|
iidArray[1] = SL_IID_PREFETCHSTATUS;
|
|
// Create Output Mix object to be used by player
|
|
res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0,
|
|
iidArray, required); CheckErr(res);
|
|
|
|
// Realizing the Output Mix object in synchronous mode.
|
|
res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
|
|
CheckErr(res);
|
|
|
|
/* Setup the data source structure for the URI */
|
|
uri.locatorType = SL_DATALOCATOR_URI;
|
|
uri.URI = (SLchar*) path;
|
|
mime.formatType = SL_DATAFORMAT_MIME;
|
|
mime.mimeType = (SLchar*)NULL;
|
|
mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
|
|
|
|
audioSource.pFormat = (void *)&mime;
|
|
audioSource.pLocator = (void *)&uri;
|
|
|
|
/* Setup the data sink structure */
|
|
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
|
locator_outputmix.outputMix = OutputMix;
|
|
audioSink.pLocator = (void *)&locator_outputmix;
|
|
audioSink.pFormat = NULL;
|
|
|
|
/* Create the audio player */
|
|
res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player,
|
|
&audioSource, &audioSink, 2, iidArray, required); CheckErr(res);
|
|
|
|
/* Realizing the player in synchronous mode. */
|
|
res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res);
|
|
|
|
/* Get interfaces */
|
|
res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
|
|
CheckErr(res);
|
|
|
|
res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf);
|
|
CheckErr(res);
|
|
|
|
res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
|
|
CheckErr(res);
|
|
res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf);
|
|
CheckErr(res);
|
|
res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf,
|
|
SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
|
|
|
|
/* Display duration */
|
|
SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
|
|
res = (*playItf)->GetDuration(playItf, &durationInMsec);
|
|
CheckErr(res);
|
|
|
|
/* Set the player volume */
|
|
res = (*volItf)->SetVolumeLevel( volItf, -300);
|
|
CheckErr(res);
|
|
|
|
/* Play the URI */
|
|
/* first cause the player to prefetch the data */
|
|
res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED );
|
|
CheckErr(res);
|
|
|
|
/* wait until there's data to play */
|
|
//SLpermille fillLevel = 0;
|
|
SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
|
|
SLuint32 timeOutIndex = 100; // 10s
|
|
while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0)) {
|
|
usleep(100 * 1000);
|
|
(*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
|
|
timeOutIndex--;
|
|
}
|
|
|
|
if (timeOutIndex == 0) {
|
|
fprintf(stderr, "Error: Failed to prefetch data in time, exiting\n");
|
|
ASSERT_TRUE(false);
|
|
// goto destroyRes;
|
|
}
|
|
|
|
/* Display duration again, */
|
|
res = (*playItf)->GetDuration(playItf, &durationInMsec);
|
|
CheckErr(res);
|
|
if (durationInMsec == SL_TIME_UNKNOWN) {
|
|
fprintf(stderr, "Error: GetDuration returned SL_TIME_UNKNOWN (after prefetch completed)\n");
|
|
ASSERT_TRUE(false);
|
|
}
|
|
SLint32 durationDiffMsec = durationInMsec - MP3_DURATION;
|
|
if (durationDiffMsec < 0) { durationDiffMsec *= -1; }
|
|
if (durationDiffMsec > (MP3_DURATION/20)) {
|
|
fprintf(stderr, "Error: GetDuration returned %d, more than 5percent off from expected %d\n",
|
|
durationInMsec, MP3_DURATION);
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING );
|
|
CheckErr(res);
|
|
|
|
/* Play for the song duration*/
|
|
usleep(MP3_DURATION * 1000);
|
|
|
|
/* Validate the play position*/
|
|
SLmillisecond currentPositionInMsec = SL_TIME_UNKNOWN;
|
|
res = (*playItf)->GetPosition(playItf, ¤tPositionInMsec);
|
|
CheckErr(res);
|
|
if (currentPositionInMsec == SL_TIME_UNKNOWN) {
|
|
fprintf(stderr, "Error: GetPosition returns SL_TIME_UNKNOWN after expected duration\n");
|
|
ASSERT_TRUE(false);
|
|
} else if ( currentPositionInMsec <= 0 ||
|
|
currentPositionInMsec > (MP3_DURATION * 1.1) ){
|
|
fprintf(stderr, "Error: GetPosition returns %i, should be expected duration for test\n",
|
|
(int) currentPositionInMsec);
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
/* Make sure player is stopped */
|
|
res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
|
|
CheckErr(res);
|
|
|
|
//destroyRes:
|
|
|
|
/* Destroy the player */
|
|
(*player)->Destroy(player);
|
|
|
|
/* Destroy Output Mix object */
|
|
(*OutputMix)->Destroy(OutputMix);
|
|
|
|
fprintf(stdout, "End of test reached\n");
|
|
}
|
|
|
|
|
|
// The fixture for testing class MimeUri
|
|
class MimeUri: public ::testing::Test {
|
|
public:
|
|
SLresult res;
|
|
SLObjectItf sl;
|
|
|
|
protected:
|
|
MimeUri() {
|
|
// You can do set-up work for each test here.
|
|
SLEngineOption EngineOption[] = { { (SLuint32) SL_ENGINEOPTION_THREADSAFE,
|
|
(SLuint32) SL_BOOLEAN_TRUE } };
|
|
|
|
res = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL);
|
|
CheckErr(res);
|
|
|
|
/* Realizing the SL Engine in synchronous mode. */
|
|
res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
|
|
CheckErr(res);
|
|
}
|
|
|
|
virtual ~MimeUri() {
|
|
// You can do clean-up work that doesn't throw exceptions here.
|
|
(*sl)->Destroy(sl);
|
|
}
|
|
|
|
virtual void SetUp() {
|
|
// Code here will be called immediately after the constructor (right
|
|
// before each test).
|
|
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
// Code here will be called immediately after each test (right
|
|
// before the destructor).
|
|
|
|
}
|
|
};
|
|
|
|
TEST_F(MimeUri, testPlayAbsPath){
|
|
TestPlayUri(sl, "/sdcard/media_api/music/MP3_256kbps_2ch.mp3");
|
|
}
|
|
|
|
TEST_F(MimeUri, testPlayfilePath){
|
|
TestPlayUri(sl, "file:///sdcard/media_api/music/MP3_256kbps_2ch.mp3");
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int main(int argc, char **argv)
|
|
{
|
|
testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|