1 /*
2  * Copyright (C) 2011 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 package android.media.cts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 
23 import android.media.MediaPlayer;
24 import android.media.cts.TestUtils.Monitor;
25 import android.net.Uri;
26 import android.os.PersistableBundle;
27 
28 import org.junit.After;
29 import org.junit.Before;
30 
31 import java.io.IOException;
32 import java.net.HttpCookie;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.logging.Logger;
37 
38 /**
39  * Base class for tests which use MediaPlayer to play audio or video.
40  */
41 public class MediaPlayerTestBase extends MediaTestBase {
42     private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName());
43 
44     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
45     protected Monitor mOnVideoRenderingStartCalled = new Monitor();
46     protected Monitor mOnBufferingUpdateCalled = new Monitor();
47     protected Monitor mOnPrepareCalled = new Monitor();
48     protected Monitor mOnSeekCompleteCalled = new Monitor();
49     protected Monitor mOnCompletionCalled = new Monitor();
50     protected Monitor mOnInfoCalled = new Monitor();
51     protected Monitor mOnErrorCalled = new Monitor();
52 
53     protected MediaPlayer mMediaPlayer = null;
54     protected MediaPlayer mMediaPlayer2 = null;
55 
56     @Before
57     @Override
setUp()58     public void setUp() throws Throwable {
59         super.setUp();
60         runOnUiThread(() -> {
61             mMediaPlayer = new MediaPlayer();
62             mMediaPlayer2 = new MediaPlayer();
63         });
64     }
65     @After
66     @Override
tearDown()67     public void tearDown() {
68         if (mMediaPlayer != null) {
69             mMediaPlayer.release();
70             mMediaPlayer = null;
71         }
72         if (mMediaPlayer2 != null) {
73             mMediaPlayer2.release();
74             mMediaPlayer2 = null;
75         }
76         super.tearDown();
77     }
78 
runOnUiThread(Runnable runnable)79     protected void runOnUiThread(Runnable runnable) throws Throwable {
80         Throwable[] throwableHolder = new Throwable[1];
81         getInstrumentation().runOnMainSync(() -> {
82             try {
83                 runnable.run();
84             } catch (Throwable throwable) {
85                 throwableHolder[0] = throwable;
86             }
87         });
88         if (throwableHolder[0] != null) {
89             throw throwableHolder[0];
90         }
91     }
92 
playLiveVideoTest(String path, int playTime)93     protected void playLiveVideoTest(String path, int playTime) throws Exception {
94         playVideoWithRetries(path, null, null, playTime);
95     }
96 
playLiveAudioOnlyTest(String path, int playTime)97     protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
98         playVideoWithRetries(path, -1, -1, playTime);
99     }
100 
playVideoTest(String path, int width, int height)101     protected void playVideoTest(String path, int width, int height) throws Exception {
102         playVideoWithRetries(path, width, height, 0);
103     }
104 
playVideoWithRetries(String path, Integer width, Integer height, int playTime)105     protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
106             throws Exception {
107         boolean playedSuccessfully = false;
108         for (int i = 0; i < STREAM_RETRIES; i++) {
109           try {
110             mMediaPlayer.reset();
111             mMediaPlayer.setDataSource(path);
112             playLoadedVideo(width, height, playTime);
113             playedSuccessfully = true;
114             break;
115           } catch (PrepareFailedException e) {
116             // prepare() can fail because of network issues, so try again
117             LOG.warning("prepare() failed on try " + i + ", trying playback again");
118           }
119         }
120         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
121     }
122 
playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)123     protected void playLiveVideoTest(
124             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
125             int playTime) throws Exception {
126         playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
127     }
128 
playLiveAudioOnlyTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)129     protected void playLiveAudioOnlyTest(
130             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
131             int playTime) throws Exception {
132         playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime);
133     }
134 
playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)135     protected void playVideoWithRetries(
136             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
137             Integer width, Integer height, int playTime) throws Exception {
138         boolean playedSuccessfully = false;
139         for (int i = 0; i < STREAM_RETRIES; i++) {
140             try {
141                 mMediaPlayer.reset();
142                 mMediaPlayer.setDataSource(
143                         getInstrumentation().getTargetContext(),
144                         uri,
145                         headers,
146                         cookies);
147                 playLoadedVideo(width, height, playTime);
148                 playedSuccessfully = true;
149                 break;
150             } catch (PrepareFailedException e) {
151                 // prepare() can fail because of network issues, so try again
152                 // playLoadedVideo already has reset the player so we can try again safely.
153                 LOG.warning("prepare() failed on try " + i + ", trying playback again");
154             }
155         }
156         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
157     }
158 
159     /**
160      * Play a video which has already been loaded with setDataSource().
161      *
162      * @param width width of the video to verify, or null to skip verification
163      * @param height height of the video to verify, or null to skip verification
164      * @param playTime length of time to play video, or 0 to play entire video.
165      * with a non-negative value, this method stops the playback after the length of
166      * time or the duration the video is elapsed. With a value of -1,
167      * this method simply starts the video and returns immediately without
168      * stoping the video playback.
169      */
playLoadedVideo(final Integer width, final Integer height, int playTime)170     protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
171             throws Exception {
172         final float leftVolume = 0.5f;
173         final float rightVolume = 0.5f;
174 
175         boolean audioOnly = (width != null && width == -1) ||
176                 (height != null && height == -1);
177 
178         mMediaPlayer.setDisplay(mActivity.getSurfaceHolder());
179         mMediaPlayer.setScreenOnWhilePlaying(true);
180         mMediaPlayer.setOnVideoSizeChangedListener((mp, w, h) -> {
181             if (w == 0 && h == 0) {
182                 // A size of 0x0 can be sent initially one time when using NuPlayer.
183                 assertFalse(mOnVideoSizeChangedCalled.isSignalled());
184                 return;
185             }
186             mOnVideoSizeChangedCalled.signal();
187             if (width != null) {
188                 assertEquals(width.intValue(), w);
189             }
190             if (height != null) {
191                 assertEquals(height.intValue(), h);
192             }
193         });
194         mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
195             fail("Media player had error " + what + " playing video");
196             return true;
197         });
198         mMediaPlayer.setOnInfoListener((mp, what, extra) -> {
199             if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
200                 mOnVideoRenderingStartCalled.signal();
201             }
202             return true;
203         });
204         try {
205           mMediaPlayer.prepare();
206         } catch (IOException e) {
207           mMediaPlayer.reset();
208           throw new PrepareFailedException();
209         }
210 
211         mMediaPlayer.start();
212         if (!audioOnly) {
213             mOnVideoSizeChangedCalled.waitForSignal();
214             mOnVideoRenderingStartCalled.waitForSignal();
215         }
216         mMediaPlayer.setVolume(leftVolume, rightVolume);
217 
218         // waiting to complete
219         if (playTime == -1) {
220             return;
221         } else if (playTime == 0) {
222             while (mMediaPlayer.isPlaying()) {
223                 Thread.sleep(SLEEP_TIME);
224             }
225         } else {
226             Thread.sleep(playTime);
227         }
228 
229         // validate a few MediaMetrics.
230         PersistableBundle metrics = mMediaPlayer.getMetrics();
231         if (metrics == null) {
232             fail("MediaPlayer.getMetrics() returned null metrics");
233         } else if (metrics.isEmpty()) {
234             fail("MediaPlayer.getMetrics() returned empty metrics");
235         } else {
236 
237             int size = metrics.size();
238             Set<String> keys = metrics.keySet();
239 
240             if (keys == null) {
241                 fail("MediaMetricsSet returned no keys");
242             } else if (keys.size() != size) {
243                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
244             }
245 
246             // we played something; so one of these should be non-null
247             String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null);
248             String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null);
249             if (vmime == null && amime == null) {
250                 fail("getMetrics() returned neither video nor audio mime value");
251             }
252 
253             long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2);
254             if (duration == -2) {
255                 fail("getMetrics() didn't return a duration");
256             }
257             long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2);
258             if (playing == -2) {
259                 fail("getMetrics() didn't return a playing time");
260             }
261             if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) {
262                 fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING);
263             }
264         }
265 
266         mMediaPlayer.stop();
267     }
268 
269     private static class PrepareFailedException extends Exception {}
270 
setOnErrorListener()271     protected void setOnErrorListener() {
272         mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
273             mOnErrorCalled.signal();
274             return false;
275         });
276     }
277 }
278