Home | History | Annotate | Download | only in stress
      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 
     17 package com.android.mediaframeworktest.stress;
     18 
     19 
     20 import com.android.mediaframeworktest.MediaFrameworkTest;
     21 
     22 import java.io.BufferedWriter;
     23 import java.io.File;
     24 import java.io.FileWriter;
     25 import java.io.IOException;
     26 import java.io.Writer;
     27 import java.util.concurrent.Semaphore;
     28 import java.util.concurrent.TimeUnit;
     29 
     30 import android.hardware.Camera;
     31 import android.media.CamcorderProfile;
     32 import android.media.MediaPlayer;
     33 import android.media.MediaRecorder;
     34 import android.os.Environment;
     35 import android.os.Handler;
     36 import android.os.Looper;
     37 import android.test.ActivityInstrumentationTestCase2;
     38 import android.test.suitebuilder.annotation.LargeTest;
     39 import android.util.Log;
     40 import android.view.SurfaceHolder;
     41 import com.android.mediaframeworktest.MediaRecorderStressTestRunner;
     42 
     43 /**
     44  * Junit / Instrumentation test case for the media player api
     45  */
     46 public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     47 
     48     private String TAG = "MediaRecorderStressTest";
     49     private MediaRecorder mRecorder;
     50     private Camera mCamera;
     51 
     52     private static final int CAMERA_ID = 0;
     53     private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 1;
     54     private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 30 * 1000; // 30 seconds
     55     private static final int USE_TEST_RUNNER_PROFILE = -1;
     56     private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
     57     private static final String OUTPUT_FILE_EXT = ".3gp";
     58     private static final String MEDIA_STRESS_OUTPUT = "mediaStressOutput.txt";
     59 
     60     private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
     61     private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
     62 
     63     private Handler mHandler;
     64     private Thread mLooperThread;
     65     private Writer mOutput;
     66 
     67     public MediaRecorderStressTest() {
     68         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
     69     }
     70 
     71     protected void setUp() throws Exception {
     72         final Semaphore sem = new Semaphore(0);
     73         mLooperThread = new Thread() {
     74             @Override
     75             public void run() {
     76                 Log.v(TAG, "starting looper");
     77                 Looper.prepare();
     78                 mHandler = new Handler();
     79                 sem.release();
     80                 Looper.loop();
     81                 Log.v(TAG, "quit looper");
     82             }
     83         };
     84         mLooperThread.start();
     85         if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
     86             fail("Failed to start the looper.");
     87         }
     88         //Insert a 2 second before launching the test activity. This is
     89         //the workaround for the race condition of requesting the updated surface.
     90         Thread.sleep(2000);
     91         getActivity();
     92         super.setUp();
     93 
     94         File stressOutFile = new File(String.format("%s/%s",
     95                 Environment.getExternalStorageDirectory(), MEDIA_STRESS_OUTPUT));
     96         mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
     97         mOutput.write(this.getName() + "\n");
     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         mOutput.write("\n\n");
    114         mOutput.close();
    115         super.tearDown();
    116     }
    117 
    118     private void runOnLooper(final Runnable command) throws InterruptedException {
    119         final Semaphore sem = new Semaphore(0);
    120         mHandler.post(new Runnable() {
    121             @Override
    122             public void run() {
    123                 try {
    124                     command.run();
    125                 } finally {
    126                     sem.release();
    127                 }
    128             }
    129         });
    130         if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
    131             fail("Failed to run the command on the looper.");
    132         }
    133     }
    134 
    135     private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
    136         public void onError(int error, android.hardware.Camera camera) {
    137             fail(String.format("Camera error, code: %d", error));
    138         }
    139     }
    140 
    141     private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener {
    142         public void onError(MediaRecorder mr, int what, int extra) {
    143             fail(String.format("Media recorder error, code: %d\textra: %d", what, extra));
    144         }
    145     }
    146 
    147     public void validateRecordedVideo(String recordedFile) {
    148         try {
    149             MediaPlayer mp = new MediaPlayer();
    150             mp.setDataSource(recordedFile);
    151             mp.prepare();
    152             int duration = mp.getDuration();
    153             if (duration <= 0){
    154                 fail("stressRecordAndPlayback");
    155             }
    156             mp.release();
    157         } catch (Exception e) {
    158             fail("stressRecordAndPlayback");
    159         }
    160     }
    161 
    162     public void removeRecordedVideo(String fileName){
    163         File video = new File(fileName);
    164         Log.v(TAG, "remove recorded video " + fileName);
    165         video.delete();
    166     }
    167 
    168     // Helper method for record & playback testing with different camcorder profiles
    169     private void recordVideoAndPlayback(int profile) throws Exception {
    170         int iterations;
    171         int recordDuration;
    172         boolean removeVideo;
    173 
    174         int videoEncoder;
    175         int audioEncoder;
    176         int frameRate;
    177         int videoWidth;
    178         int videoHeight;
    179         int bitRate;
    180 
    181         if (profile != USE_TEST_RUNNER_PROFILE) {
    182             assertTrue(String.format("Camera doesn't support profile %d", profile),
    183                     CamcorderProfile.hasProfile(CAMERA_ID, profile));
    184             CamcorderProfile camcorderProfile = CamcorderProfile.get(CAMERA_ID, profile);
    185             videoEncoder = camcorderProfile.videoCodec;
    186             audioEncoder = camcorderProfile.audioCodec;
    187             frameRate = camcorderProfile.videoFrameRate;
    188             videoWidth = camcorderProfile.videoFrameWidth;
    189             videoHeight = camcorderProfile.videoFrameHeight;
    190             bitRate = camcorderProfile.videoBitRate;
    191         } else {
    192             videoEncoder = MediaRecorderStressTestRunner.mVideoEncoder;
    193             audioEncoder = MediaRecorderStressTestRunner.mAudioEncoder;
    194             frameRate = MediaRecorderStressTestRunner.mFrameRate;
    195             videoWidth = MediaRecorderStressTestRunner.mVideoWidth;
    196             videoHeight = MediaRecorderStressTestRunner.mVideoHeight;
    197             bitRate = MediaRecorderStressTestRunner.mBitRate;
    198         }
    199         iterations = MediaRecorderStressTestRunner.mIterations;
    200         recordDuration = MediaRecorderStressTestRunner.mDuration;
    201         removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
    202 
    203         SurfaceHolder surfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
    204         mOutput.write("Total number of loops: " + iterations + "\n");
    205 
    206         try {
    207             mOutput.write("No of loop: ");
    208             for (int i = 0; i < iterations; i++) {
    209                 String fileName = String.format("%s/temp%d%s",
    210                         Environment.getExternalStorageDirectory(), i, OUTPUT_FILE_EXT);
    211                 Log.v(TAG, fileName);
    212 
    213                 runOnLooper(new Runnable() {
    214                     @Override
    215                     public void run() {
    216                         mRecorder = new MediaRecorder();
    217                     }
    218                 });
    219 
    220                 Log.v(TAG, "iterations : " + iterations);
    221                 Log.v(TAG, "video encoder : " + videoEncoder);
    222                 Log.v(TAG, "audio encoder : " + audioEncoder);
    223                 Log.v(TAG, "frame rate : " + frameRate);
    224                 Log.v(TAG, "video width : " + videoWidth);
    225                 Log.v(TAG, "video height : " + videoHeight);
    226                 Log.v(TAG, "bit rate : " + bitRate);
    227                 Log.v(TAG, "record duration : " + recordDuration);
    228 
    229                 mRecorder.setOnErrorListener(mRecorderErrorCallback);
    230                 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    231                 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    232                 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    233                 mRecorder.setOutputFile(fileName);
    234                 mRecorder.setVideoFrameRate(frameRate);
    235                 mRecorder.setVideoSize(videoWidth, videoHeight);
    236                 mRecorder.setVideoEncoder(videoEncoder);
    237                 mRecorder.setAudioEncoder(audioEncoder);
    238                 mRecorder.setVideoEncodingBitRate(bitRate);
    239 
    240                 Log.v(TAG, "mediaRecorder setPreview");
    241                 mRecorder.setPreviewDisplay(surfaceHolder.getSurface());
    242                 mRecorder.prepare();
    243                 mRecorder.start();
    244                 Thread.sleep(recordDuration);
    245                 Log.v(TAG, "Before stop");
    246                 mRecorder.stop();
    247                 mRecorder.release();
    248 
    249                 //start the playback
    250                 MediaPlayer mp = new MediaPlayer();
    251                 mp.setDataSource(fileName);
    252                 mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
    253                 mp.prepare();
    254                 mp.start();
    255                 Thread.sleep(recordDuration);
    256                 mp.release();
    257                 validateRecordedVideo(fileName);
    258                 if (removeVideo) {
    259                     removeRecordedVideo(fileName);
    260                 }
    261                 if (i == 0) {
    262                     mOutput.write(i + 1);
    263                 } else {
    264                     mOutput.write(String.format(", %d", (i + 1)));
    265                 }
    266             }
    267         } catch (Exception e) {
    268             Log.e(TAG, e.toString());
    269             fail("Record and playback");
    270         }
    271     }
    272 
    273     // Record and playback stress test @ 1080P quality
    274     @LargeTest
    275     public void testStressRecordVideoAndPlayback1080P() throws Exception {
    276         recordVideoAndPlayback(CamcorderProfile.QUALITY_1080P);
    277     }
    278 
    279     // Record and playback stress test @ 720P quality
    280     @LargeTest
    281     public void testStressRecordVideoAndPlayback720P() throws Exception {
    282         recordVideoAndPlayback(CamcorderProfile.QUALITY_720P);
    283     }
    284 
    285     // Record and playback stress test @ 480P quality
    286     @LargeTest
    287     public void testStressRecordVideoAndPlayback480P() throws Exception {
    288         recordVideoAndPlayback(CamcorderProfile.QUALITY_480P);
    289     }
    290 
    291     // This test method uses the codec info from the test runner. Use this
    292     // for more granular control of video encoding.
    293     @LargeTest
    294     public void defaultStressRecordVideoAndPlayback() throws Exception {
    295         recordVideoAndPlayback(USE_TEST_RUNNER_PROFILE);
    296     }
    297 
    298     // Test case for stressing time lapse
    299     @LargeTest
    300     public void testStressTimeLapse() throws Exception {
    301         SurfaceHolder mSurfaceHolder;
    302         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
    303         int recordDuration = MediaRecorderStressTestRunner.mTimeLapseDuration;
    304         boolean removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
    305         double captureRate = MediaRecorderStressTestRunner.mCaptureRate;
    306         Log.v(TAG, "Start camera time lapse stress:");
    307         mOutput.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
    308 
    309         try {
    310             for (int i = 0, n = Camera.getNumberOfCameras(); i < n; i++) {
    311                 mOutput.write("No of loop: camera " + i);
    312                 for (int j = 0; j < NUMBER_OF_TIME_LAPSE_LOOPS; j++) {
    313                     String fileName = String.format("%s/temp%d_%d%s",
    314                             Environment.getExternalStorageDirectory(), i, j, OUTPUT_FILE_EXT);
    315                     Log.v(TAG, fileName);
    316                     runOnLooper(new Runnable() {
    317                         @Override
    318                         public void run() {
    319                             mRecorder = new MediaRecorder();
    320                         }
    321                     });
    322 
    323                     // Set callback
    324                     mRecorder.setOnErrorListener(mRecorderErrorCallback);
    325 
    326                     // Set video source
    327                     mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    328 
    329                     // Set camcorder profile for time lapse
    330                     CamcorderProfile profile =
    331                         CamcorderProfile.get(j, CamcorderProfile.QUALITY_TIME_LAPSE_HIGH);
    332                     mRecorder.setProfile(profile);
    333 
    334                     // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc
    335                     // http://developer.android.com/guide/topics/media/camera.html#time-lapse-video
    336                     mRecorder.setCaptureRate(captureRate);
    337 
    338                     // Set output file
    339                     mRecorder.setOutputFile(fileName);
    340 
    341                     // Set the preview display
    342                     Log.v(TAG, "mediaRecorder setPreviewDisplay");
    343                     mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
    344 
    345                     mRecorder.prepare();
    346                     mRecorder.start();
    347                     Thread.sleep(recordDuration);
    348                     Log.v(TAG, "Before stop");
    349                     mRecorder.stop();
    350                     mRecorder.release();
    351 
    352                     // Start the playback
    353                     MediaPlayer mp = new MediaPlayer();
    354                     mp.setDataSource(fileName);
    355                     mp.setDisplay(mSurfaceHolder);
    356                     mp.prepare();
    357                     mp.start();
    358                     Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME);
    359                     mp.release();
    360                     validateRecordedVideo(fileName);
    361                     if (removeVideo) {
    362                         removeRecordedVideo(fileName);
    363                     }
    364 
    365                     if (j == 0) {
    366                         mOutput.write(j + 1);
    367                     } else {
    368                         mOutput.write(String.format(", %d", (j + 1)));
    369                     }
    370                 }
    371             }
    372         } catch (IllegalStateException e) {
    373             Log.e(TAG, e.toString());
    374             fail("Camera time lapse stress test IllegalStateException");
    375         } catch (IOException e) {
    376             Log.e(TAG, e.toString());
    377             fail("Camera time lapse stress test IOException");
    378         } catch (Exception e) {
    379             Log.e(TAG, e.toString());
    380             fail("Camera time lapse stress test Exception");
    381         }
    382     }
    383 }
    384