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