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.Environment; 35import android.os.Handler; 36import android.os.Looper; 37import android.test.ActivityInstrumentationTestCase2; 38import android.test.suitebuilder.annotation.LargeTest; 39import android.util.Log; 40import android.view.SurfaceHolder; 41import com.android.mediaframeworktest.MediaRecorderStressTestRunner; 42 43/** 44 * Junit / Instrumentation test case for the media player api 45 */ 46public 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_CAMERA_STRESS_LOOPS = 100; 54 private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100; 55 private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50; 56 private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200; 57 private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25; 58 private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds 59 private static final int USE_TEST_RUNNER_PROFILE = -1; 60 private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds 61 private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds 62 private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds 63 private static final String OUTPUT_FILE_EXT = ".3gp"; 64 private static final String MEDIA_STRESS_OUTPUT = "mediaStressOutput.txt"; 65 66 private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback(); 67 private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback(); 68 69 private Handler mHandler; 70 private Thread mLooperThread; 71 private Writer mOutput; 72 73 public MediaRecorderStressTest() { 74 super("com.android.mediaframeworktest", MediaFrameworkTest.class); 75 } 76 77 protected void setUp() throws Exception { 78 final Semaphore sem = new Semaphore(0); 79 mLooperThread = new Thread() { 80 @Override 81 public void run() { 82 Log.v(TAG, "starting looper"); 83 Looper.prepare(); 84 mHandler = new Handler(); 85 sem.release(); 86 Looper.loop(); 87 Log.v(TAG, "quit looper"); 88 } 89 }; 90 mLooperThread.start(); 91 if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) { 92 fail("Failed to start the looper."); 93 } 94 //Insert a 2 second before launching the test activity. This is 95 //the workaround for the race condition of requesting the updated surface. 96 Thread.sleep(2000); 97 getActivity(); 98 super.setUp(); 99 100 File stressOutFile = new File(String.format("%s/%s", 101 Environment.getExternalStorageDirectory(), MEDIA_STRESS_OUTPUT)); 102 mOutput = new BufferedWriter(new FileWriter(stressOutFile, true)); 103 mOutput.write(this.getName() + "\n"); 104 } 105 106 @Override 107 protected void tearDown() throws Exception { 108 if (mHandler != null) { 109 mHandler.getLooper().quit(); 110 mHandler = null; 111 } 112 if (mLooperThread != null) { 113 mLooperThread.join(WAIT_TIMEOUT); 114 if (mLooperThread.isAlive()) { 115 fail("Failed to stop the looper."); 116 } 117 mLooperThread = null; 118 } 119 mOutput.write("\n\n"); 120 mOutput.close(); 121 super.tearDown(); 122 } 123 124 private void runOnLooper(final Runnable command) throws InterruptedException { 125 final Semaphore sem = new Semaphore(0); 126 mHandler.post(new Runnable() { 127 @Override 128 public void run() { 129 try { 130 command.run(); 131 } finally { 132 sem.release(); 133 } 134 } 135 }); 136 if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) { 137 fail("Failed to run the command on the looper."); 138 } 139 } 140 141 private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback { 142 public void onError(int error, android.hardware.Camera camera) { 143 fail(String.format("Camera error, code: %d", error)); 144 } 145 } 146 147 private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener { 148 public void onError(MediaRecorder mr, int what, int extra) { 149 fail(String.format("Media recorder error, code: %d\textra: %d", what, extra)); 150 } 151 } 152 153 //Test case for stressing the camera preview. 154 @LargeTest 155 public void testStressCamera() throws Exception { 156 SurfaceHolder mSurfaceHolder; 157 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 158 Log.v(TAG, "Camera start preview stress test"); 159 mOutput.write("Total number of loops:" + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n"); 160 try { 161 Log.v(TAG, "Start preview"); 162 mOutput.write("No of loop: "); 163 164 for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) { 165 runOnLooper(new Runnable() { 166 @Override 167 public void run() { 168 mCamera = Camera.open(CAMERA_ID); 169 } 170 }); 171 mCamera.setErrorCallback(mCameraErrorCallback); 172 mCamera.setPreviewDisplay(mSurfaceHolder); 173 mCamera.startPreview(); 174 Thread.sleep(WAIT_TIME_CAMERA_TEST); 175 mCamera.stopPreview(); 176 mCamera.release(); 177 if (i == 0) { 178 mOutput.write(i + 1); 179 } else { 180 mOutput.write(String.format(", %d", (i + 1))); 181 } 182 } 183 } catch (Exception e) { 184 Log.e(TAG, e.toString()); 185 fail("Camera startup preview stress test"); 186 } 187 } 188 189 //Test case for stressing the camera preview. 190 @LargeTest 191 public void testStressRecorder() throws Exception { 192 SurfaceHolder mSurfaceHolder; 193 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 194 Log.v(TAG, "H263 video record: reset after prepare Stress test"); 195 mOutput.write("Total number of loops:" + NUMBER_OF_RECORDER_STRESS_LOOPS + "\n"); 196 try { 197 mOutput.write("No of loop: "); 198 Log.v(TAG, "Start preview"); 199 for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) { 200 runOnLooper(new Runnable() { 201 @Override 202 public void run() { 203 mRecorder = new MediaRecorder(); 204 } 205 }); 206 Log.v(TAG, "counter = " + i); 207 String fileName = String.format("%s/temp%d%s", 208 Environment.getExternalStorageDirectory(), 209 i, OUTPUT_FILE_EXT); 210 211 Log.v(TAG, fileName); 212 mRecorder.setOnErrorListener(mRecorderErrorCallback); 213 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 214 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 215 mRecorder.setOutputFile(fileName); 216 mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate); 217 mRecorder.setVideoSize(176,144); 218 Log.v(TAG, "setEncoder"); 219 mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); 220 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 221 Log.v(TAG, "setPreview"); 222 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 223 Log.v(TAG, "prepare"); 224 mRecorder.prepare(); 225 Log.v(TAG, "before release"); 226 Thread.sleep(WAIT_TIME_RECORDER_TEST); 227 mRecorder.reset(); 228 mRecorder.release(); 229 if (i == 0) { 230 mOutput.write(i + 1); 231 } else { 232 mOutput.write(String.format(", %d", (i + 1))); 233 } 234 } 235 } catch (Exception e) { 236 Log.e(TAG, e.toString()); 237 fail("H263 video recording stress test"); 238 } 239 } 240 241 //Stress test case for switching camera and video recorder preview. 242 @LargeTest 243 public void testStressCameraSwitchRecorder() throws Exception { 244 SurfaceHolder mSurfaceHolder; 245 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 246 Log.v(TAG, "Camera and video recorder preview switching"); 247 mOutput.write("Total number of loops: " + 248 NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n"); 249 try { 250 Log.v(TAG, "Start preview"); 251 mOutput.write("No of loop: "); 252 for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) { 253 runOnLooper(new Runnable() { 254 @Override 255 public void run() { 256 mCamera = Camera.open(CAMERA_ID); 257 } 258 }); 259 mCamera.setErrorCallback(mCameraErrorCallback); 260 mCamera.setPreviewDisplay(mSurfaceHolder); 261 mCamera.startPreview(); 262 Thread.sleep(WAIT_TIME_CAMERA_TEST); 263 mCamera.stopPreview(); 264 mCamera.release(); 265 mCamera = null; 266 Log.v(TAG, "release camera"); 267 String fileName = String.format("%s/temp%d%s", 268 Environment.getExternalStorageDirectory(), 269 i, OUTPUT_FILE_EXT); 270 Log.v(TAG, fileName); 271 runOnLooper(new Runnable() { 272 @Override 273 public void run() { 274 mRecorder = new MediaRecorder(); 275 } 276 }); 277 mRecorder.setOnErrorListener(mRecorderErrorCallback); 278 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 279 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 280 mRecorder.setOutputFile(fileName); 281 mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate); 282 mRecorder.setVideoSize(176,144); 283 Log.v(TAG, "Media recorder setEncoder"); 284 mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); 285 Log.v(TAG, "mediaRecorder setPreview"); 286 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 287 Log.v(TAG, "prepare"); 288 mRecorder.prepare(); 289 Log.v(TAG, "before release"); 290 Thread.sleep(WAIT_TIME_CAMERA_TEST); 291 mRecorder.release(); 292 Log.v(TAG, "release video recorder"); 293 if (i == 0) { 294 mOutput.write(i + 1); 295 } else { 296 mOutput.write(String.format(", %d", (i + 1))); 297 } 298 } 299 } catch (Exception e) { 300 Log.e(TAG, e.toString()); 301 fail("Camera and recorder switch mode"); 302 } 303 } 304 305 public void validateRecordedVideo(String recordedFile) { 306 try { 307 MediaPlayer mp = new MediaPlayer(); 308 mp.setDataSource(recordedFile); 309 mp.prepare(); 310 int duration = mp.getDuration(); 311 if (duration <= 0){ 312 fail("stressRecordAndPlayback"); 313 } 314 mp.release(); 315 } catch (Exception e) { 316 fail("stressRecordAndPlayback"); 317 } 318 } 319 320 public void removeRecordedVideo(String fileName){ 321 File video = new File(fileName); 322 Log.v(TAG, "remove recorded video " + fileName); 323 video.delete(); 324 } 325 326 // Helper method for record & playback testing with different camcorder profiles 327 private void recordVideoAndPlayback(int profile) throws Exception { 328 int iterations; 329 int recordDuration; 330 boolean removeVideo; 331 332 int videoEncoder; 333 int audioEncoder; 334 int frameRate; 335 int videoWidth; 336 int videoHeight; 337 int bitRate; 338 339 if (profile != USE_TEST_RUNNER_PROFILE) { 340 assertTrue(String.format("Camera doesn't support profile %d", profile), 341 CamcorderProfile.hasProfile(CAMERA_ID, profile)); 342 CamcorderProfile camcorderProfile = CamcorderProfile.get(CAMERA_ID, profile); 343 videoEncoder = camcorderProfile.videoCodec; 344 audioEncoder = camcorderProfile.audioCodec; 345 frameRate = camcorderProfile.videoFrameRate; 346 videoWidth = camcorderProfile.videoFrameWidth; 347 videoHeight = camcorderProfile.videoFrameHeight; 348 bitRate = camcorderProfile.videoBitRate; 349 } else { 350 videoEncoder = MediaRecorderStressTestRunner.mVideoEncoder; 351 audioEncoder = MediaRecorderStressTestRunner.mAudioEncoder; 352 frameRate = MediaRecorderStressTestRunner.mFrameRate; 353 videoWidth = MediaRecorderStressTestRunner.mVideoWidth; 354 videoHeight = MediaRecorderStressTestRunner.mVideoHeight; 355 bitRate = MediaRecorderStressTestRunner.mBitRate; 356 } 357 iterations = MediaRecorderStressTestRunner.mIterations; 358 recordDuration = MediaRecorderStressTestRunner.mDuration; 359 removeVideo = MediaRecorderStressTestRunner.mRemoveVideo; 360 361 SurfaceHolder surfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 362 mOutput.write("Total number of loops: " + iterations + "\n"); 363 364 try { 365 mOutput.write("No of loop: "); 366 for (int i = 0; i < iterations; i++) { 367 String fileName = String.format("%s/temp%d%s", 368 Environment.getExternalStorageDirectory(), i, OUTPUT_FILE_EXT); 369 Log.v(TAG, fileName); 370 371 runOnLooper(new Runnable() { 372 @Override 373 public void run() { 374 mRecorder = new MediaRecorder(); 375 } 376 }); 377 378 Log.v(TAG, "iterations : " + iterations); 379 Log.v(TAG, "video encoder : " + videoEncoder); 380 Log.v(TAG, "audio encoder : " + audioEncoder); 381 Log.v(TAG, "frame rate : " + frameRate); 382 Log.v(TAG, "video width : " + videoWidth); 383 Log.v(TAG, "video height : " + videoHeight); 384 Log.v(TAG, "bit rate : " + bitRate); 385 Log.v(TAG, "record duration : " + recordDuration); 386 387 mRecorder.setOnErrorListener(mRecorderErrorCallback); 388 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 389 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 390 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 391 mRecorder.setOutputFile(fileName); 392 mRecorder.setVideoFrameRate(frameRate); 393 mRecorder.setVideoSize(videoWidth, videoHeight); 394 mRecorder.setVideoEncoder(videoEncoder); 395 mRecorder.setAudioEncoder(audioEncoder); 396 mRecorder.setVideoEncodingBitRate(bitRate); 397 398 Log.v(TAG, "mediaRecorder setPreview"); 399 mRecorder.setPreviewDisplay(surfaceHolder.getSurface()); 400 mRecorder.prepare(); 401 mRecorder.start(); 402 Thread.sleep(recordDuration); 403 Log.v(TAG, "Before stop"); 404 mRecorder.stop(); 405 mRecorder.release(); 406 407 //start the playback 408 MediaPlayer mp = new MediaPlayer(); 409 mp.setDataSource(fileName); 410 mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); 411 mp.prepare(); 412 mp.start(); 413 Thread.sleep(recordDuration); 414 mp.release(); 415 validateRecordedVideo(fileName); 416 if (removeVideo) { 417 removeRecordedVideo(fileName); 418 } 419 if (i == 0) { 420 mOutput.write(i + 1); 421 } else { 422 mOutput.write(String.format(", %d", (i + 1))); 423 } 424 } 425 } catch (Exception e) { 426 Log.e(TAG, e.toString()); 427 fail("Record and playback"); 428 } 429 } 430 431 // Record and playback stress test @ 1080P quality 432 @LargeTest 433 public void testStressRecordVideoAndPlayback1080P() throws Exception { 434 recordVideoAndPlayback(CamcorderProfile.QUALITY_1080P); 435 } 436 437 // Record and playback stress test @ 720P quality 438 @LargeTest 439 public void testStressRecordVideoAndPlayback720P() throws Exception { 440 recordVideoAndPlayback(CamcorderProfile.QUALITY_720P); 441 } 442 443 // Record and playback stress test @ 480P quality 444 @LargeTest 445 public void testStressRecordVideoAndPlayback480P() throws Exception { 446 recordVideoAndPlayback(CamcorderProfile.QUALITY_480P); 447 } 448 449 // This test method uses the codec info from the test runner. Use this 450 // for more granular control of video encoding. 451 @LargeTest 452 public void defaultStressRecordVideoAndPlayback() throws Exception { 453 recordVideoAndPlayback(USE_TEST_RUNNER_PROFILE); 454 } 455 456 // Test case for stressing time lapse 457 @LargeTest 458 public void testStressTimeLapse() throws Exception { 459 SurfaceHolder mSurfaceHolder; 460 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 461 int recordDuration = MediaRecorderStressTestRunner.mTimeLapseDuration; 462 boolean removeVideo = MediaRecorderStressTestRunner.mRemoveVideo; 463 double captureRate = MediaRecorderStressTestRunner.mCaptureRate; 464 Log.v(TAG, "Start camera time lapse stress:"); 465 mOutput.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n"); 466 467 try { 468 for (int i = 0, n = Camera.getNumberOfCameras(); i < n; i++) { 469 mOutput.write("No of loop: camera " + i); 470 for (int j = 0; j < NUMBER_OF_TIME_LAPSE_LOOPS; j++) { 471 String fileName = String.format("%s/temp%d_%d%s", 472 Environment.getExternalStorageDirectory(), i, j, OUTPUT_FILE_EXT); 473 Log.v(TAG, fileName); 474 runOnLooper(new Runnable() { 475 @Override 476 public void run() { 477 mRecorder = new MediaRecorder(); 478 } 479 }); 480 481 // Set callback 482 mRecorder.setOnErrorListener(mRecorderErrorCallback); 483 484 // Set video source 485 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 486 487 // Set camcorder profile for time lapse 488 CamcorderProfile profile = 489 CamcorderProfile.get(j, CamcorderProfile.QUALITY_TIME_LAPSE_HIGH); 490 mRecorder.setProfile(profile); 491 492 // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc 493 // http://developer.android.com/guide/topics/media/camera.html#time-lapse-video 494 mRecorder.setCaptureRate(captureRate); 495 496 // Set output file 497 mRecorder.setOutputFile(fileName); 498 499 // Set the preview display 500 Log.v(TAG, "mediaRecorder setPreviewDisplay"); 501 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 502 503 mRecorder.prepare(); 504 mRecorder.start(); 505 Thread.sleep(recordDuration); 506 Log.v(TAG, "Before stop"); 507 mRecorder.stop(); 508 mRecorder.release(); 509 510 // Start the playback 511 MediaPlayer mp = new MediaPlayer(); 512 mp.setDataSource(fileName); 513 mp.setDisplay(mSurfaceHolder); 514 mp.prepare(); 515 mp.start(); 516 Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME); 517 mp.release(); 518 validateRecordedVideo(fileName); 519 if (removeVideo) { 520 removeRecordedVideo(fileName); 521 } 522 523 if (j == 0) { 524 mOutput.write(j + 1); 525 } else { 526 mOutput.write(String.format(", %d", (j + 1))); 527 } 528 } 529 } 530 } catch (IllegalStateException e) { 531 Log.e(TAG, e.toString()); 532 fail("Camera time lapse stress test IllegalStateException"); 533 } catch (IOException e) { 534 Log.e(TAG, e.toString()); 535 fail("Camera time lapse stress test IOException"); 536 } catch (Exception e) { 537 Log.e(TAG, e.toString()); 538 fail("Camera time lapse stress test Exception"); 539 } 540 } 541} 542