1/*
2 * Copyright (C) 2009 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
17package com.android.mediaframeworktest.stress;
18
19
20import com.android.mediaframeworktest.MediaFrameworkTest;
21
22import java.io.BufferedWriter;
23import java.io.File;
24import java.io.FileWriter;
25import java.io.IOException;
26import java.io.Writer;
27import java.util.concurrent.Semaphore;
28import java.util.concurrent.TimeUnit;
29
30import android.hardware.Camera;
31import android.media.CamcorderProfile;
32import android.media.MediaPlayer;
33import android.media.MediaRecorder;
34import android.os.Handler;
35import android.os.Looper;
36import android.test.ActivityInstrumentationTestCase2;
37import android.test.suitebuilder.annotation.LargeTest;
38import android.util.Log;
39import android.view.SurfaceHolder;
40import com.android.mediaframeworktest.MediaRecorderStressTestRunner;
41
42/**
43 * Junit / Instrumentation test case for the media player api
44 */
45public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
46
47    private String TAG = "MediaRecorderStressTest";
48    private MediaRecorder mRecorder;
49    private Camera mCamera;
50
51    private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100;
52    private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100;
53    private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50;
54    private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
55    private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25;
56    private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds
57    private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds
58    private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds
59    private static final String OUTPUT_FILE = "/sdcard/temp";
60    private static final String OUTPUT_FILE_EXT = ".3gp";
61    private static final String MEDIA_STRESS_OUTPUT =
62        "/sdcard/mediaStressOutput.txt";
63    private static final int CAMERA_ID = 0;
64
65    private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
66    private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
67
68    private final static int WAIT_TIMEOUT = 10 * 1000; // 10 seconds
69    private Thread mLooperThread;
70    private Handler mHandler;
71
72    public MediaRecorderStressTest() {
73        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
74    }
75
76    protected void setUp() throws Exception {
77        final Semaphore sem = new Semaphore(0);
78        mLooperThread = new Thread() {
79            @Override
80            public void run() {
81                Log.v(TAG, "starting looper");
82                Looper.prepare();
83                mHandler = new Handler();
84                sem.release();
85                Looper.loop();
86                Log.v(TAG, "quit looper");
87            }
88        };
89        mLooperThread.start();
90        if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
91            fail("Failed to start the looper.");
92        }
93        //Insert a 2 second before launching the test activity. This is
94        //the workaround for the race condition of requesting the updated surface.
95        Thread.sleep(2000);
96        getActivity();
97        super.setUp();
98    }
99
100    @Override
101    protected void tearDown() throws Exception {
102        if (mHandler != null) {
103            mHandler.getLooper().quit();
104            mHandler = null;
105        }
106        if (mLooperThread != null) {
107            mLooperThread.join(WAIT_TIMEOUT);
108            if (mLooperThread.isAlive()) {
109                fail("Failed to stop the looper.");
110            }
111            mLooperThread = null;
112        }
113
114        super.tearDown();
115    }
116
117    private void runOnLooper(final Runnable command) throws InterruptedException {
118        final Semaphore sem = new Semaphore(0);
119        mHandler.post(new Runnable() {
120            @Override
121            public void run() {
122                try {
123                    command.run();
124                } finally {
125                    sem.release();
126                }
127            }
128        });
129        if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
130            fail("Failed to run the command on the looper.");
131        }
132    }
133
134    private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
135        public void onError(int error, android.hardware.Camera camera) {
136            if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
137                assertTrue("Camera test mediaserver died", false);
138            }
139        }
140    }
141
142    private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener {
143        public void onError(MediaRecorder mr, int what, int extra) {
144            // fail the test case no matter what error come up
145            assertTrue("mediaRecorder error", false);
146        }
147    }
148
149    //Test case for stressing the camera preview.
150    @LargeTest
151    public void testStressCamera() throws Exception {
152        SurfaceHolder mSurfaceHolder;
153        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
154        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
155        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
156        output.write("Camera start preview stress:\n");
157        output.write("Total number of loops:" +
158                NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
159        try {
160            Log.v(TAG, "Start preview");
161            output.write("No of loop: ");
162
163            for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) {
164                runOnLooper(new Runnable() {
165                    @Override
166                    public void run() {
167                        mCamera = Camera.open(CAMERA_ID);
168                    }
169                });
170                mCamera.setErrorCallback(mCameraErrorCallback);
171                mCamera.setPreviewDisplay(mSurfaceHolder);
172                mCamera.startPreview();
173                Thread.sleep(WAIT_TIME_CAMERA_TEST);
174                mCamera.stopPreview();
175                mCamera.release();
176                output.write(" ," + i);
177            }
178        } catch (Exception e) {
179            assertTrue("CameraStressTest", false);
180            Log.v(TAG, e.toString());
181        }
182        output.write("\n\n");
183        output.close();
184    }
185
186    //Test case for stressing the camera preview.
187    @LargeTest
188    public void testStressRecorder() throws Exception {
189        String filename;
190        SurfaceHolder mSurfaceHolder;
191        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
192        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
193        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
194        output.write("H263 video record- reset after prepare Stress test\n");
195        output.write("Total number of loops:" +
196                NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
197        try {
198            output.write("No of loop: ");
199            Log.v(TAG, "Start preview");
200            for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) {
201                runOnLooper(new Runnable() {
202                    @Override
203                    public void run() {
204                        mRecorder = new MediaRecorder();
205                    }
206                });
207                Log.v(TAG, "counter = " + i);
208                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
209                Log.v(TAG, filename);
210                mRecorder.setOnErrorListener(mRecorderErrorCallback);
211                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
212                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
213                mRecorder.setOutputFile(filename);
214                mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
215                mRecorder.setVideoSize(176,144);
216                Log.v(TAG, "setEncoder");
217                mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
218                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
219                Log.v(TAG, "setPreview");
220                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
221                Log.v(TAG, "prepare");
222                mRecorder.prepare();
223                Log.v(TAG, "before release");
224                Thread.sleep(WAIT_TIME_RECORDER_TEST);
225                mRecorder.reset();
226                mRecorder.release();
227                output.write(", " + i);
228            }
229        } catch (Exception e) {
230            assertTrue("Recorder Stress test", false);
231            Log.v(TAG, e.toString());
232        }
233        output.write("\n\n");
234        output.close();
235    }
236
237    //Stress test case for switching camera and video recorder preview.
238    @LargeTest
239    public void testStressCameraSwitchRecorder() throws Exception {
240        String filename;
241        SurfaceHolder mSurfaceHolder;
242        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
243        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
244        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
245        output.write("Camera and video recorder preview switching\n");
246        output.write("Total number of loops:"
247                + NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
248        try {
249            Log.v(TAG, "Start preview");
250            output.write("No of loop: ");
251            for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) {
252                runOnLooper(new Runnable() {
253                    @Override
254                    public void run() {
255                        mCamera = Camera.open(CAMERA_ID);
256                    }
257                });
258                mCamera.setErrorCallback(mCameraErrorCallback);
259                mCamera.setPreviewDisplay(mSurfaceHolder);
260                mCamera.startPreview();
261                Thread.sleep(WAIT_TIME_CAMERA_TEST);
262                mCamera.stopPreview();
263                mCamera.release();
264                mCamera = null;
265                Log.v(TAG, "release camera");
266                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
267                Log.v(TAG, filename);
268                runOnLooper(new Runnable() {
269                    @Override
270                    public void run() {
271                        mRecorder = new MediaRecorder();
272                    }
273                });
274                mRecorder.setOnErrorListener(mRecorderErrorCallback);
275                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
276                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
277                mRecorder.setOutputFile(filename);
278                mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
279                mRecorder.setVideoSize(176,144);
280                Log.v(TAG, "Media recorder setEncoder");
281                mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
282                Log.v(TAG, "mediaRecorder setPreview");
283                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
284                Log.v(TAG, "prepare");
285                mRecorder.prepare();
286                Log.v(TAG, "before release");
287                Thread.sleep(WAIT_TIME_CAMERA_TEST);
288                mRecorder.release();
289                Log.v(TAG, "release video recorder");
290                output.write(", " + i);
291            }
292        } catch (Exception e) {
293            assertTrue("Camer and recorder switch mode", false);
294                Log.v(TAG, e.toString());
295        }
296        output.write("\n\n");
297        output.close();
298    }
299
300    public void validateRecordedVideo(String recorded_file) {
301        try {
302            MediaPlayer mp = new MediaPlayer();
303            mp.setDataSource(recorded_file);
304            mp.prepare();
305            int duration = mp.getDuration();
306            if (duration <= 0){
307                assertTrue("stressRecordAndPlayback", false);
308            }
309            mp.release();
310        } catch (Exception e) {
311            assertTrue("stressRecordAndPlayback", false);
312        }
313    }
314
315    public void removeRecordedVideo(String filename){
316        File video = new File(filename);
317        Log.v(TAG, "remove recorded video " + filename);
318        video.delete();
319    }
320
321    //Stress test case for record a video and play right away.
322    @LargeTest
323    public void testStressRecordVideoAndPlayback() throws Exception {
324        int iterations = MediaRecorderStressTestRunner.mIterations;
325        int video_encoder = MediaRecorderStressTestRunner.mVideoEncoder;
326        int audio_encoder = MediaRecorderStressTestRunner.mAudioEncdoer;
327        int frame_rate = MediaRecorderStressTestRunner.mFrameRate;
328        int video_width = MediaRecorderStressTestRunner.mVideoWidth;
329        int video_height = MediaRecorderStressTestRunner.mVideoHeight;
330        int bit_rate = MediaRecorderStressTestRunner.mBitRate;
331        boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
332        int record_duration = MediaRecorderStressTestRunner.mDuration;
333
334        String filename;
335        SurfaceHolder mSurfaceHolder;
336        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
337        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
338        Writer output = new BufferedWriter(
339                new FileWriter(stressOutFile, true));
340        output.write("Video record and play back stress test:\n");
341        output.write("Total number of loops:"
342                + NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS + "\n");
343        try {
344            output.write("No of loop: ");
345            for (int i = 0; i < iterations; i++){
346                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
347                Log.v(TAG, filename);
348                runOnLooper(new Runnable() {
349                    @Override
350                    public void run() {
351                        mRecorder = new MediaRecorder();
352                    }
353                });
354                Log.v(TAG, "iterations : " + iterations);
355                Log.v(TAG, "video_encoder : " + video_encoder);
356                Log.v(TAG, "audio_encoder : " + audio_encoder);
357                Log.v(TAG, "frame_rate : " + frame_rate);
358                Log.v(TAG, "video_width : " + video_width);
359                Log.v(TAG, "video_height : " + video_height);
360                Log.v(TAG, "bit rate : " + bit_rate);
361                Log.v(TAG, "record_duration : " + record_duration);
362
363                mRecorder.setOnErrorListener(mRecorderErrorCallback);
364                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
365                mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
366                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
367                mRecorder.setOutputFile(filename);
368                mRecorder.setVideoFrameRate(frame_rate);
369                mRecorder.setVideoSize(video_width, video_height);
370                mRecorder.setVideoEncoder(video_encoder);
371                mRecorder.setAudioEncoder(audio_encoder);
372                mRecorder.setVideoEncodingBitRate(bit_rate);
373                Log.v(TAG, "mediaRecorder setPreview");
374                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
375                mRecorder.prepare();
376                mRecorder.start();
377                Thread.sleep(record_duration);
378                Log.v(TAG, "Before stop");
379                mRecorder.stop();
380                mRecorder.release();
381                //start the playback
382                MediaPlayer mp = new MediaPlayer();
383                mp.setDataSource(filename);
384                mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
385                mp.prepare();
386                mp.start();
387                Thread.sleep(record_duration);
388                mp.release();
389                validateRecordedVideo(filename);
390                if (remove_video) {
391                    removeRecordedVideo(filename);
392                }
393                output.write(", " + i);
394            }
395        } catch (Exception e) {
396            Log.v(TAG, e.toString());
397            assertTrue("record and playback", false);
398        }
399        output.write("\n\n");
400        output.close();
401    }
402
403    // Test case for stressing time lapse
404    @LargeTest
405    public void testStressTimeLapse() throws Exception {
406        SurfaceHolder mSurfaceHolder;
407        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
408        int record_duration = MediaRecorderStressTestRunner.mTimeLapseDuration;
409        boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
410        double captureRate = MediaRecorderStressTestRunner.mCaptureRate;
411        String filename;
412        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
413        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
414        output.write("Start camera time lapse stress:\n");
415        output.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
416
417        try {
418            for (int j = 0, n = Camera.getNumberOfCameras(); j < n; j++) {
419                output.write("No of loop: camera " + j);
420                for (int i = 0; i < NUMBER_OF_TIME_LAPSE_LOOPS; i++) {
421                    filename = OUTPUT_FILE + j + "_" + i + OUTPUT_FILE_EXT;
422                    Log.v(TAG, filename);
423                    runOnLooper(new Runnable() {
424                        @Override
425                        public void run() {
426                            mRecorder = new MediaRecorder();
427                        }
428                    });
429
430                    // Set callback
431                    mRecorder.setOnErrorListener(mRecorderErrorCallback);
432
433                    // Set video source
434                    mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
435
436                    // Set camcorder profile for time lapse
437                    CamcorderProfile profile =
438                        CamcorderProfile.get(j, CamcorderProfile.QUALITY_TIME_LAPSE_HIGH);
439                    mRecorder.setProfile(profile);
440
441                    // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc.
442                    // http://developer.android.com/guide/topics/media/camera.html#time-lapse-video
443                    mRecorder.setCaptureRate(captureRate);
444
445                    // Set output file
446                    mRecorder.setOutputFile(filename);
447
448                    // Set the preview display
449                    Log.v(TAG, "mediaRecorder setPreviewDisplay");
450                    mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
451
452                    mRecorder.prepare();
453                    mRecorder.start();
454                    Thread.sleep(record_duration);
455                    Log.v(TAG, "Before stop");
456                    mRecorder.stop();
457                    mRecorder.release();
458
459                    // Start the playback
460                    MediaPlayer mp = new MediaPlayer();
461                    mp.setDataSource(filename);
462                    mp.setDisplay(mSurfaceHolder);
463                    mp.prepare();
464                    mp.start();
465                    Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME);
466                    mp.release();
467                    validateRecordedVideo(filename);
468                    if (remove_video) {
469                        removeRecordedVideo(filename);
470                    }
471                    output.write(", " + i);
472                }
473            }
474        }
475        catch (IllegalStateException e) {
476            assertTrue("Camera time lapse stress test IllegalStateException", false);
477            Log.v(TAG, e.toString());
478        }
479        catch (IOException e) {
480            assertTrue("Camera time lapse stress test IOException", false);
481            Log.v(TAG, e.toString());
482        }
483        catch (Exception e) {
484            assertTrue("Camera time lapse stress test Exception", false);
485            Log.v(TAG, e.toString());
486        }
487        output.write("\n\n");
488        output.close();
489    }
490}
491