• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.res.AssetFileDescriptor;
19 import android.content.res.Resources;
20 import android.media.MediaRecorder;
21 import android.media.MediaPlayer;
22 import android.os.Environment;
23 import android.platform.test.annotations.AppModeFull;
24 import android.test.ActivityInstrumentationTestCase2;
25 import android.util.Log;
26 import android.view.SurfaceHolder;
27 
28 import java.util.Random;
29 
30 /**
31  * Tests for the MediaPlayer.java and MediaRecorder.java APIs
32  *
33  * These testcases make randomized calls to the public APIs available, and
34  * the focus is on whether the randomized calls can lead to crash in
35  * mediaserver process and/or ANRs.
36  *
37  * The files in res/raw used by testLocalVideo* are (c) copyright 2008,
38  * Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons
39  * Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/.
40  */
41 @MediaHeavyPresubmitTest
42 @AppModeFull(reason = "TODO: evaluate and port to instant")
43 public class MediaRandomTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
44     private static final String TAG = "MediaRandomTest";
45 
46     private static final String OUTPUT_FILE =
47                 Environment.getExternalStorageDirectory().toString() + "/record.3gp";
48 
49     private static final int NUMBER_OF_RECORDER_RANDOM_ACTIONS = 100000;
50     private static final int NUMBER_OF_PLAYER_RANDOM_ACTIONS   = 100000;
51 
52     private MediaRecorder mRecorder;
53     private MediaPlayer mPlayer;
54     private SurfaceHolder mSurfaceHolder;
55     private Resources mResources;
56 
57     // Modified across multiple threads
58     private volatile boolean mMediaServerDied;
59     private volatile int mAction;
60     private volatile int mParam;
61 
62     @Override
setUp()63     protected void setUp() throws Exception {
64         super.setUp();
65         getInstrumentation().waitForIdleSync();
66         mMediaServerDied = false;
67         mSurfaceHolder = getActivity().getSurfaceHolder();
68         mResources = getInstrumentation().getTargetContext().getResources();
69         try {
70             // Running this on UI thread make sure that
71             // onError callback can be received.
72             runTestOnUiThread(new Runnable() {
73                 public void run() {
74                     mRecorder = new MediaRecorder();
75                     mPlayer = new MediaPlayer();
76                 }
77             });
78         } catch (Throwable e) {
79             e.printStackTrace();
80             fail();
81         }
82     }
83 
84     @Override
tearDown()85     protected void tearDown() throws Exception {
86         if (mRecorder != null) {
87             mRecorder.release();
88             mRecorder = null;
89         }
90         if (mPlayer != null) {
91             mPlayer.release();
92             mPlayer = null;
93         }
94         super.tearDown();
95     }
96 
97     /**
98      * This is a watchdog used to stop the process if it hasn't been pinged
99      * for more than specified milli-seconds. It is used like:
100      *
101      * Watchdog w = new Watchdog(10000);  // 10 seconds.
102      * w.start();       // start the watchdog.
103      * ...
104      * w.ping();
105      * ...
106      * w.ping();
107      * ...
108      * w.end();        // ask the watchdog to stop.
109      * w.join();        // join the thread.
110      */
111     class Watchdog extends Thread {
112         private final long mTimeoutMs;
113         private boolean mWatchdogStop;
114         private boolean mWatchdogPinged;
115 
Watchdog(long timeoutMs)116         public Watchdog(long timeoutMs) {
117             mTimeoutMs = timeoutMs;
118             mWatchdogStop = false;
119             mWatchdogPinged = false;
120         }
121 
run()122         public synchronized void run() {
123             while (true) {
124                 // avoid early termination by "spurious" waitup.
125                 final long startTimeMs = System.currentTimeMillis();
126                 long remainingWaitTimeMs = mTimeoutMs;
127                 do {
128                     try {
129                         wait(remainingWaitTimeMs);
130                     } catch (InterruptedException ex) {
131                         // ignore.
132                     }
133                     remainingWaitTimeMs = mTimeoutMs - (System.currentTimeMillis() - startTimeMs);
134                 } while (remainingWaitTimeMs > 0);
135 
136                 if (mWatchdogStop) {
137                     break;
138                 }
139 
140                 if (!mWatchdogPinged) {
141                     fail("Action " + mAction + " Param " + mParam
142                             + " waited over " + (mTimeoutMs - remainingWaitTimeMs) + " ms");
143                     return;
144                 }
145                 mWatchdogPinged = false;
146             }
147         }
148 
ping()149         public synchronized void ping() {
150             mWatchdogPinged = true;
151             this.notify();
152         }
153 
end()154         public synchronized void end() {
155             mWatchdogStop = true;
156             this.notify();
157         }
158     }
159 
MediaRandomTest()160     public MediaRandomTest() {
161         super("android.media.cts", MediaStubActivity.class);
162     }
163 
loadSource(int resid)164     private void loadSource(int resid) throws Exception {
165         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
166         try {
167             mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
168                     afd.getLength());
169         } finally {
170             afd.close();
171         }
172     }
testPlayerRandomActionAV1()173     public void testPlayerRandomActionAV1() throws Exception {
174         testPlayerRandomAction(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz);
175     }
testPlayerRandomActionH264()176     public void testPlayerRandomActionH264() throws Exception {
177         testPlayerRandomAction(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
178     }
testPlayerRandomActionHEVC()179     public void testPlayerRandomActionHEVC() throws Exception {
180         testPlayerRandomAction(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz);
181     }
testPlayerRandomActionMpeg2()182     public void testPlayerRandomActionMpeg2() throws Exception {
183         testPlayerRandomAction(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz);
184     }
testPlayerRandomAction(int resid)185     private void testPlayerRandomAction(int resid) throws Exception {
186         Watchdog watchdog = new Watchdog(5000);
187         try {
188             mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
189                 @Override
190                 public boolean onError(MediaPlayer mp, int what, int extra) {
191                     if (mPlayer == mp &&
192                         what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
193                         Log.e(TAG, "mediaserver process died");
194                         mMediaServerDied = true;
195                     }
196                     return true;
197                 }
198             });
199             loadSource(resid);
200             mPlayer.setDisplay(mSurfaceHolder);
201             mPlayer.prepare();
202             mPlayer.start();
203 
204             long seed = System.currentTimeMillis();
205             Log.v(TAG, "seed = " + seed);
206             Random r = new Random(seed);
207 
208             watchdog.start();
209             for (int i = 0; i < NUMBER_OF_PLAYER_RANDOM_ACTIONS; i++){
210                 watchdog.ping();
211                 assertTrue(!mMediaServerDied);
212 
213                 mAction = (int)(r.nextInt() % 12);
214                 mParam = (int)(r.nextInt() % 1000000);
215                 try {
216                     switch (mAction) {
217                     case 0:
218                         mPlayer.getCurrentPosition();
219                         break;
220                     case 1:
221                         mPlayer.getDuration();
222                         break;
223                     case 2:
224                         mPlayer.getVideoHeight();
225                         break;
226                     case 3:
227                         mPlayer.getVideoWidth();
228                        break;
229                     case 4:
230                         mPlayer.isPlaying();
231                         break;
232                     case 5:
233                         mPlayer.pause();
234                         break;
235                     case 6:
236                         // Don't add mPlayer.prepare() call here for two reasons:
237                         // 1. calling prepare() is a bad idea since it is a blocking call, and
238                         // 2. when prepare() is in progress, mediaserver died message will not be sent to apps
239                         mPlayer.prepareAsync();
240                         break;
241                     case 7:
242                         mPlayer.seekTo((int)(mParam));
243                         break;
244                     case 8:
245                         mPlayer.setLooping(mParam % 2 == 0);
246                         break;
247                     case 9:
248                         mPlayer.setVolume((mParam % 1000) / 500.0f,
249                                      (mParam / 1000) / 500.0f);
250                         break;
251                     case 10:
252                         mPlayer.start();
253                         break;
254                     case 11:
255                         Thread.sleep(mParam % 20);
256                         break;
257                     }
258                 } catch (Exception e) {
259                 }
260             }
261             mPlayer.stop();
262         } catch (Exception e) {
263             Log.v(TAG, e.toString());
264         } finally {
265             watchdog.end();
266             watchdog.join();
267         }
268     }
269 
testRecorderRandomAction()270     public void testRecorderRandomAction() throws Exception {
271         Watchdog watchdog = new Watchdog(5000);
272         try {
273             long seed = System.currentTimeMillis();
274             Log.v(TAG, "seed = " + seed);
275             Random r = new Random(seed);
276 
277             mMediaServerDied = false;
278             mRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
279                 @Override
280                 public void onError(MediaRecorder recorder, int what, int extra) {
281                     if (mRecorder == recorder &&
282                         what == MediaRecorder.MEDIA_ERROR_SERVER_DIED) {
283                         Log.e(TAG, "mediaserver process died");
284                         mMediaServerDied = true;
285                     }
286                 }
287             });
288 
289             final int[] width  = {176, 352, 320, 640, 1280, 1920};
290             final int[] height = {144, 288, 240, 480,  720, 1080};
291             final int[] audioSource = {
292                     MediaRecorder.AudioSource.DEFAULT,
293                     MediaRecorder.AudioSource.MIC,
294                     MediaRecorder.AudioSource.CAMCORDER,
295             };
296 
297             watchdog.start();
298             for (int i = 0; i < NUMBER_OF_RECORDER_RANDOM_ACTIONS; i++) {
299                 watchdog.ping();
300                 assertTrue(!mMediaServerDied);
301 
302                 mAction = (int)(r.nextInt(14));
303                 mParam = (int)(r.nextInt(1000000));
304                 try {
305                     switch (mAction) {
306                     case 0: {
307                         // We restrict the audio sources because setting some sources
308                         // may cause 2+ second delays because the input device may
309                         // retry - loop (e.g. VOICE_UPLINK for voice call to be initiated).
310                         final int index = mParam % audioSource.length;
311                         mRecorder.setAudioSource(audioSource[index]);
312                         break;
313                     }
314                     case 1:
315                         // XXX:
316                         // Fix gralloc source and change
317                         // mRecorder.setVideoSource(mParam % 3);
318                         mRecorder.setVideoSource(mParam % 2);
319                         break;
320                     case 2:
321                         mRecorder.setOutputFormat(mParam % 5);
322                         break;
323                     case 3:
324                         mRecorder.setAudioEncoder(mParam % 3);
325                         break;
326                     case 4:
327                         mRecorder.setVideoEncoder(mParam % 5);
328                         break;
329                     case 5:
330                         mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
331                         break;
332                     case 6:
333                         int index = mParam % width.length;
334                         mRecorder.setVideoSize(width[index], height[index]);
335                         break;
336                     case 7:
337                         mRecorder.setVideoFrameRate(mParam % 40 - 5);
338                         break;
339                     case 8:
340                         mRecorder.setOutputFile(OUTPUT_FILE);
341                         break;
342                     case 9:
343                         mRecorder.prepare();
344                         break;
345                     case 10:
346                         mRecorder.start();
347                         break;
348                     case 11:
349                         Thread.sleep(mParam % 20);
350                         break;
351                     case 12:
352                         mRecorder.stop();
353                         break;
354                     case 13:
355                         mRecorder.reset();
356                         break;
357                     default:
358                         break;
359                     }
360                 } catch (Exception e) {
361                 }
362             }
363         } catch (Exception e) {
364             Log.v(TAG, e.toString());
365         } finally {
366             watchdog.end();
367             watchdog.join();
368         }
369     }
370 }
371