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