1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Copyright (c) 2009 The Khronos Group Inc.
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
21 * software and /or associated documentation files (the "Materials "), to deal in the
22 * Materials without restriction, including without limitation the rights to use, copy,
23 * modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials,
24 * and to permit persons to whom the Materials are furnished to do so, subject to
25 * the following conditions:
26 *
27 * The above copyright notice and this permission notice shall be included
28 * in all copies or substantial portions of the Materials.
29 *
30 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
34 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
36 * CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE
37 * MATERIALS.
38 */
39
40#define LOG_NDEBUG 0
41#define LOG_TAG "slesTestPlayUri"
42
43#include <utils/Log.h>
44#include <getopt.h>
45#include <stdlib.h>
46#include <stdio.h>
47#include <string.h>
48#include <unistd.h>
49#include <sys/time.h>
50#include <gtest/gtest.h>
51#include <SLES/OpenSLES.h>
52
53
54#define MAX_NUMBER_INTERFACES 3
55#define MAX_NUMBER_OUTPUT_DEVICES 6
56
57//The expected playback duration
58const int MP3_DURATION = 71030; //71 secs
59
60
61//-----------------------------------------------------------------
62/* Checks for error. If any errors exit the application! */
63void CheckErr( SLresult res )
64{
65    if ( res != SL_RESULT_SUCCESS )  {
66        fprintf(stderr, "%u SL failure, exiting\n", res);
67        //Fail the test case
68        ASSERT_TRUE(false);
69    }
70}
71
72//-----------------------------------------------------------------
73/* PrefetchStatusItf callback for an audio player */
74void PrefetchEventCallback( SLPrefetchStatusItf caller,  void *pContext __unused, SLuint32 event)
75{
76    SLpermille level = 0;
77    (*caller)->GetFillLevel(caller, &level);
78    SLuint32 status;
79    fprintf(stdout, "\t\tPrefetchEventCallback: received event %u\n", event);
80    (*caller)->GetPrefetchStatus(caller, &status);
81    if ((event & (SL_PREFETCHEVENT_STATUSCHANGE|SL_PREFETCHEVENT_FILLLEVELCHANGE))
82            && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
83        fprintf(stderr, "\t\tPrefetchEventCallback: Error while prefetching data, exiting\n");
84        ASSERT_TRUE(false);
85    }
86    if (event & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
87        fprintf(stdout, "\t\tPrefetchEventCallback: Buffer fill level is = %d\n", level);
88    }
89    if (event & SL_PREFETCHEVENT_STATUSCHANGE) {
90        fprintf(stdout, "\t\tPrefetchEventCallback: Prefetch Status is = %u\n", status);
91    }
92
93}
94
95
96//-----------------------------------------------------------------
97
98/* Play some music from a URI  */
99void TestPlayUri( SLObjectItf sl, const char* path)
100{
101    SLEngineItf                EngineItf;
102
103    SLresult                   res;
104
105    SLDataSource               audioSource;
106    SLDataLocator_URI          uri;
107    SLDataFormat_MIME          mime;
108
109    SLDataSink                 audioSink;
110    SLDataLocator_OutputMix    locator_outputmix;
111
112    SLObjectItf                player;
113    SLPlayItf                  playItf;
114    SLVolumeItf                volItf;
115    SLPrefetchStatusItf        prefetchItf;
116
117    SLObjectItf                OutputMix;
118
119    SLboolean required[MAX_NUMBER_INTERFACES];
120    SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
121
122    /* Get the SL Engine Interface which is implicit */
123    res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
124    CheckErr(res);
125
126    /* Initialize arrays required[] and iidArray[] */
127    for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) {
128        required[i] = SL_BOOLEAN_FALSE;
129        iidArray[i] = SL_IID_NULL;
130    }
131
132    // Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface
133    required[0] = SL_BOOLEAN_TRUE;
134    iidArray[0] = SL_IID_VOLUME;
135    required[1] = SL_BOOLEAN_TRUE;
136    iidArray[1] = SL_IID_PREFETCHSTATUS;
137    // Create Output Mix object to be used by player
138    res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0,
139            iidArray, required); CheckErr(res);
140
141    // Realizing the Output Mix object in synchronous mode.
142    res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
143    CheckErr(res);
144
145    /* Setup the data source structure for the URI */
146    uri.locatorType = SL_DATALOCATOR_URI;
147    uri.URI         =  (SLchar*) path;
148    mime.formatType    = SL_DATAFORMAT_MIME;
149    mime.mimeType      = (SLchar*)NULL;
150    mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
151
152    audioSource.pFormat      = (void *)&mime;
153    audioSource.pLocator     = (void *)&uri;
154
155    /* Setup the data sink structure */
156    locator_outputmix.locatorType   = SL_DATALOCATOR_OUTPUTMIX;
157    locator_outputmix.outputMix    = OutputMix;
158    audioSink.pLocator           = (void *)&locator_outputmix;
159    audioSink.pFormat            = NULL;
160
161    /* Create the audio player */
162    res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player,
163            &audioSource, &audioSink, 2, iidArray, required); CheckErr(res);
164
165    /* Realizing the player in synchronous mode. */
166    res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res);
167
168    /* Get interfaces */
169    res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
170    CheckErr(res);
171
172    res = (*player)->GetInterface(player, SL_IID_VOLUME,  (void*)&volItf);
173    CheckErr(res);
174
175    res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
176    CheckErr(res);
177    res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf);
178    CheckErr(res);
179    res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf,
180            SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
181
182    /* Display duration */
183    SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
184    res = (*playItf)->GetDuration(playItf, &durationInMsec);
185    CheckErr(res);
186
187    /* Set the player volume */
188    res = (*volItf)->SetVolumeLevel( volItf, -300);
189    CheckErr(res);
190
191    /* Play the URI */
192    /*     first cause the player to prefetch the data */
193    res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED );
194    CheckErr(res);
195
196    /*     wait until there's data to play */
197    //SLpermille fillLevel = 0;
198    SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
199    SLuint32 timeOutIndex = 100; // 10s
200    while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0)) {
201        usleep(100 * 1000);
202        (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
203        timeOutIndex--;
204    }
205
206    if (timeOutIndex == 0) {
207        fprintf(stderr, "Error: Failed to prefetch data in time, exiting\n");
208        ASSERT_TRUE(false);
209       // goto destroyRes;
210    }
211
212    /* Display duration again, */
213    res = (*playItf)->GetDuration(playItf, &durationInMsec);
214    CheckErr(res);
215    if (durationInMsec == SL_TIME_UNKNOWN) {
216        fprintf(stderr, "Error: GetDuration returned SL_TIME_UNKNOWN (after prefetch completed)\n");
217        ASSERT_TRUE(false);
218    }
219    SLint32 durationDiffMsec = durationInMsec - MP3_DURATION;
220    if (durationDiffMsec < 0) { durationDiffMsec *= -1; }
221    if (durationDiffMsec > (MP3_DURATION/20)) {
222        fprintf(stderr, "Error: GetDuration returned %d, more than 5percent off from expected %d\n",
223                durationInMsec, MP3_DURATION);
224        ASSERT_TRUE(false);
225    }
226
227    res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING );
228    CheckErr(res);
229
230    /* Play for the song duration*/
231    usleep(MP3_DURATION * 1000);
232
233    /* Validate the play position*/
234    SLmillisecond currentPositionInMsec = SL_TIME_UNKNOWN;
235           res = (*playItf)->GetPosition(playItf, &currentPositionInMsec);
236           CheckErr(res);
237    if (currentPositionInMsec == SL_TIME_UNKNOWN) {
238      fprintf(stderr, "Error: GetPosition returns SL_TIME_UNKNOWN after expected duration\n");
239      ASSERT_TRUE(false);
240    } else if ( currentPositionInMsec <= 0 ||
241        currentPositionInMsec > (MP3_DURATION * 1.1) ){
242        fprintf(stderr, "Error: GetPosition returns %i, should be expected duration for test\n",
243                (int) currentPositionInMsec);
244        ASSERT_TRUE(false);
245    }
246
247    /* Make sure player is stopped */
248    res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
249    CheckErr(res);
250
251//destroyRes:
252
253    /* Destroy the player */
254    (*player)->Destroy(player);
255
256    /* Destroy Output Mix object */
257    (*OutputMix)->Destroy(OutputMix);
258
259    fprintf(stdout, "End of test reached\n");
260}
261
262
263// The fixture for testing class MimeUri
264class MimeUri: public ::testing::Test {
265public:
266    SLresult res;
267    SLObjectItf sl;
268
269protected:
270    MimeUri() {
271        // You can do set-up work for each test here.
272        SLEngineOption EngineOption[] = { { (SLuint32) SL_ENGINEOPTION_THREADSAFE,
273            (SLuint32) SL_BOOLEAN_TRUE } };
274
275        res = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL);
276        CheckErr(res);
277
278        /* Realizing the SL Engine in synchronous mode. */
279        res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
280        CheckErr(res);
281    }
282
283    virtual ~MimeUri() {
284        // You can do clean-up work that doesn't throw exceptions here.
285        (*sl)->Destroy(sl);
286    }
287
288    virtual void SetUp() {
289        // Code here will be called immediately after the constructor (right
290        // before each test).
291
292    }
293
294    virtual void TearDown() {
295        // Code here will be called immediately after each test (right
296        // before the destructor).
297
298    }
299};
300
301TEST_F(MimeUri, testPlayAbsPath){
302    TestPlayUri(sl, "/sdcard/media_api/music/MP3_256kbps_2ch.mp3");
303}
304
305TEST_F(MimeUri, testPlayfilePath){
306    TestPlayUri(sl, "file:///sdcard/media_api/music/MP3_256kbps_2ch.mp3");
307}
308
309//-----------------------------------------------------------------
310int main(int argc, char **argv)
311{
312    testing::InitGoogleTest(&argc, argv);
313    return RUN_ALL_TESTS();
314}
315