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 android.content.Context;
19 import android.content.pm.PackageManager;
20 import android.content.res.AssetFileDescriptor;
21 import android.content.res.Resources;
22 import android.media.MediaPlayer;
23 import android.media.cts.TestUtils.Monitor;
24 import android.net.Uri;
25 import android.os.PersistableBundle;
26 import android.test.ActivityInstrumentationTestCase2;
27 
28 import com.android.compatibility.common.util.MediaUtils;
29 
30 import java.io.IOException;
31 import java.net.HttpCookie;
32 import java.util.List;
33 import java.util.logging.Logger;
34 import java.util.Map;
35 import java.util.Set;
36 
37 /**
38  * Base class for tests which use MediaPlayer to play audio or video.
39  */
40 public class MediaPlayerTestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> {
41     private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName());
42 
43     protected static final int SLEEP_TIME = 1000;
44     protected static final int LONG_SLEEP_TIME = 6000;
45     protected static final int STREAM_RETRIES = 20;
46     protected static boolean sUseScaleToFitMode = false;
47 
48     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
49     protected Monitor mOnVideoRenderingStartCalled = new Monitor();
50     protected Monitor mOnBufferingUpdateCalled = new Monitor();
51     protected Monitor mOnPrepareCalled = new Monitor();
52     protected Monitor mOnSeekCompleteCalled = new Monitor();
53     protected Monitor mOnCompletionCalled = new Monitor();
54     protected Monitor mOnInfoCalled = new Monitor();
55     protected Monitor mOnErrorCalled = new Monitor();
56 
57     protected Context mContext;
58     protected Resources mResources;
59 
60 
61     protected MediaPlayer mMediaPlayer = null;
62     protected MediaPlayer mMediaPlayer2 = null;
63     protected MediaStubActivity mActivity;
64 
MediaPlayerTestBase()65     public MediaPlayerTestBase() {
66         super(MediaStubActivity.class);
67     }
68 
69     @Override
setUp()70     protected void setUp() throws Exception {
71         super.setUp();
72         mActivity = getActivity();
73         getInstrumentation().waitForIdleSync();
74         try {
75             runTestOnUiThread(new Runnable() {
76                 public void run() {
77                     mMediaPlayer = new MediaPlayer();
78                     mMediaPlayer2 = new MediaPlayer();
79                 }
80             });
81         } catch (Throwable e) {
82             e.printStackTrace();
83             fail();
84         }
85         mContext = getInstrumentation().getTargetContext();
86         mResources = mContext.getResources();
87     }
88 
89     @Override
tearDown()90     protected void tearDown() throws Exception {
91         if (mMediaPlayer != null) {
92             mMediaPlayer.release();
93             mMediaPlayer = null;
94         }
95         if (mMediaPlayer2 != null) {
96             mMediaPlayer2.release();
97             mMediaPlayer2 = null;
98         }
99         mActivity = null;
100         super.tearDown();
101     }
102 
103     // returns true on success
loadResource(int resid)104     protected boolean loadResource(int resid) throws Exception {
105         if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
106             return false;
107         }
108 
109         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
110         try {
111             mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
112                     afd.getLength());
113 
114             // Although it is only meant for video playback, it should not
115             // cause issues for audio-only playback.
116             int videoScalingMode = sUseScaleToFitMode?
117                                     MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT
118                                   : MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
119 
120             mMediaPlayer.setVideoScalingMode(videoScalingMode);
121         } finally {
122             afd.close();
123         }
124         sUseScaleToFitMode = !sUseScaleToFitMode;  // Alternate the scaling mode
125         return true;
126     }
127 
checkLoadResource(int resid)128     protected boolean checkLoadResource(int resid) throws Exception {
129         return MediaUtils.check(loadResource(resid), "no decoder found");
130     }
131 
loadSubtitleSource(int resid)132     protected void loadSubtitleSource(int resid) throws Exception {
133         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
134         try {
135             mMediaPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
136                       afd.getLength(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
137         } finally {
138             afd.close();
139         }
140     }
141 
playLiveVideoTest(String path, int playTime)142     protected void playLiveVideoTest(String path, int playTime) throws Exception {
143         playVideoWithRetries(path, null, null, playTime);
144     }
145 
playLiveAudioOnlyTest(String path, int playTime)146     protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
147         playVideoWithRetries(path, -1, -1, playTime);
148     }
149 
playVideoTest(String path, int width, int height)150     protected void playVideoTest(String path, int width, int height) throws Exception {
151         playVideoWithRetries(path, width, height, 0);
152     }
153 
playVideoWithRetries(String path, Integer width, Integer height, int playTime)154     protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
155             throws Exception {
156         boolean playedSuccessfully = false;
157         for (int i = 0; i < STREAM_RETRIES; i++) {
158           try {
159             mMediaPlayer.reset();
160             mMediaPlayer.setDataSource(path);
161             playLoadedVideo(width, height, playTime);
162             playedSuccessfully = true;
163             break;
164           } catch (PrepareFailedException e) {
165             // prepare() can fail because of network issues, so try again
166             LOG.warning("prepare() failed on try " + i + ", trying playback again");
167           }
168         }
169         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
170     }
171 
playVideoTest(int resid, int width, int height)172     protected void playVideoTest(int resid, int width, int height) throws Exception {
173         if (!checkLoadResource(resid)) {
174             return; // skip
175         }
176 
177         playLoadedVideo(width, height, 0);
178     }
179 
playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)180     protected void playLiveVideoTest(
181             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
182             int playTime) throws Exception {
183         playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
184     }
185 
playLiveAudioOnlyTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)186     protected void playLiveAudioOnlyTest(
187             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
188             int playTime) throws Exception {
189         playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime);
190     }
191 
playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)192     protected void playVideoWithRetries(
193             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
194             Integer width, Integer height, int playTime) throws Exception {
195         boolean playedSuccessfully = false;
196         for (int i = 0; i < STREAM_RETRIES; i++) {
197             try {
198                 mMediaPlayer.reset();
199                 mMediaPlayer.setDataSource(getInstrumentation().getTargetContext(),
200                         uri, headers, cookies);
201                 playLoadedVideo(width, height, playTime);
202                 playedSuccessfully = true;
203                 break;
204             } catch (PrepareFailedException e) {
205                 // prepare() can fail because of network issues, so try again
206                 // playLoadedVideo already has reset the player so we can try again safely.
207                 LOG.warning("prepare() failed on try " + i + ", trying playback again");
208             }
209         }
210         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
211     }
212 
213     /**
214      * Play a video which has already been loaded with setDataSource().
215      *
216      * @param width width of the video to verify, or null to skip verification
217      * @param height height of the video to verify, or null to skip verification
218      * @param playTime length of time to play video, or 0 to play entire video.
219      * with a non-negative value, this method stops the playback after the length of
220      * time or the duration the video is elapsed. With a value of -1,
221      * this method simply starts the video and returns immediately without
222      * stoping the video playback.
223      */
playLoadedVideo(final Integer width, final Integer height, int playTime)224     protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
225             throws Exception {
226         final float leftVolume = 0.5f;
227         final float rightVolume = 0.5f;
228 
229         boolean audioOnly = (width != null && width.intValue() == -1) ||
230                 (height != null && height.intValue() == -1);
231 
232         mMediaPlayer.setDisplay(mActivity.getSurfaceHolder());
233         mMediaPlayer.setScreenOnWhilePlaying(true);
234         mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
235             @Override
236             public void onVideoSizeChanged(MediaPlayer mp, int w, int h) {
237                 if (w == 0 && h == 0) {
238                     // A size of 0x0 can be sent initially one time when using NuPlayer.
239                     assertFalse(mOnVideoSizeChangedCalled.isSignalled());
240                     return;
241                 }
242                 mOnVideoSizeChangedCalled.signal();
243                 if (width != null) {
244                     assertEquals(width.intValue(), w);
245                 }
246                 if (height != null) {
247                     assertEquals(height.intValue(), h);
248                 }
249             }
250         });
251         mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
252             @Override
253             public boolean onError(MediaPlayer mp, int what, int extra) {
254                 fail("Media player had error " + what + " playing video");
255                 return true;
256             }
257         });
258         mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
259             @Override
260             public boolean onInfo(MediaPlayer mp, int what, int extra) {
261                 if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
262                     mOnVideoRenderingStartCalled.signal();
263                 }
264                 return true;
265             }
266         });
267         try {
268           mMediaPlayer.prepare();
269         } catch (IOException e) {
270           mMediaPlayer.reset();
271           throw new PrepareFailedException();
272         }
273 
274         mMediaPlayer.start();
275         if (!audioOnly) {
276             mOnVideoSizeChangedCalled.waitForSignal();
277             mOnVideoRenderingStartCalled.waitForSignal();
278         }
279         mMediaPlayer.setVolume(leftVolume, rightVolume);
280 
281         // waiting to complete
282         if (playTime == -1) {
283             return;
284         } else if (playTime == 0) {
285             while (mMediaPlayer.isPlaying()) {
286                 Thread.sleep(SLEEP_TIME);
287             }
288         } else {
289             Thread.sleep(playTime);
290         }
291 
292         // validate a few MediaMetrics.
293         PersistableBundle metrics = mMediaPlayer.getMetrics();
294         if (metrics == null) {
295             fail("MediaPlayer.getMetrics() returned null metrics");
296         } else if (metrics.isEmpty()) {
297             fail("MediaPlayer.getMetrics() returned empty metrics");
298         } else {
299 
300             int size = metrics.size();
301             Set<String> keys = metrics.keySet();
302 
303             if (keys == null) {
304                 fail("MediaMetricsSet returned no keys");
305             } else if (keys.size() != size) {
306                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
307             }
308 
309             // we played something; so one of these should be non-null
310             String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null);
311             String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null);
312             if (vmime == null && amime == null) {
313                 fail("getMetrics() returned neither video nor audio mime value");
314             }
315 
316             long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2);
317             if (duration == -2) {
318                 fail("getMetrics() didn't return a duration");
319             }
320             long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2);
321             if (playing == -2) {
322                 fail("getMetrics() didn't return a playing time");
323             }
324             if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) {
325                 fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING);
326             }
327         }
328 
329         mMediaPlayer.stop();
330     }
331 
332     private static class PrepareFailedException extends Exception {}
333 
isTv()334     public boolean isTv() {
335         PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
336         return pm.hasSystemFeature(pm.FEATURE_TELEVISION)
337                 && pm.hasSystemFeature(pm.FEATURE_LEANBACK);
338     }
339 
checkTv()340     public boolean checkTv() {
341         return MediaUtils.check(isTv(), "not a TV");
342     }
343 
setOnErrorListener()344     protected void setOnErrorListener() {
345         mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
346             @Override
347             public boolean onError(MediaPlayer mp, int what, int extra) {
348                 mOnErrorCalled.signal();
349                 return false;
350             }
351         });
352     }
353 }
354