1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.mediaframeworktest.performance; 18 19 import com.android.mediaframeworktest.MediaFrameworkTest; 20 import com.android.mediaframeworktest.MediaFrameworkPerfTestRunner; 21 import com.android.mediaframeworktest.MediaNames; 22 import com.android.mediaframeworktest.MediaTestUtil; 23 24 import android.database.sqlite.SQLiteDatabase; 25 import android.hardware.Camera; 26 import android.hardware.Camera.PreviewCallback; 27 import android.media.CamcorderProfile; 28 import android.media.MediaPlayer; 29 import android.media.MediaRecorder; 30 import android.media.EncoderCapabilities.VideoEncoderCap; 31 import android.os.ConditionVariable; 32 import android.os.Looper; 33 import android.test.ActivityInstrumentationTestCase2; 34 import android.test.suitebuilder.annotation.LargeTest; 35 import android.util.Log; 36 import android.view.SurfaceHolder; 37 38 import java.util.List; 39 import java.io.BufferedReader; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.InputStreamReader; 43 import java.io.Writer; 44 import java.io.File; 45 import java.io.FileWriter; 46 import java.io.BufferedWriter; 47 48 import com.android.mediaframeworktest.MediaProfileReader; 49 50 /** 51 * Junit / Instrumentation - performance measurement for media player and 52 * recorder 53 * 54 * FIXME: 55 * Add tests on H264 video encoder 56 */ 57 public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { 58 59 private String TAG = "MediaPlayerPerformance"; 60 61 private SurfaceHolder mSurfaceHolder = null; 62 private static final int NUM_STRESS_LOOP = 10; 63 private static final int NUM_PLAYBACk_IN_EACH_LOOP = 20; 64 private static final int SHORT_WAIT = 2 * 1000; // 2 seconds 65 private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds 66 private static final String MEDIA_MEMORY_OUTPUT = 67 "/sdcard/mediaMemOutput.txt"; 68 private static final String MEDIA_PROCMEM_OUTPUT = 69 "/sdcard/mediaProcmemOutput.txt"; 70 private static final int CAMERA_ID = 0; 71 72 private static int mStartMemory = 0; 73 private static int mEndMemory = 0; 74 private static int mStartPid = 0; 75 private static int mEndPid = 0; 76 77 private Looper mLooper = null; 78 private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback(); 79 private final ConditionVariable mPreviewDone = new ConditionVariable(); 80 private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; // Milliseconds. 81 82 //the tolerant memory leak 83 private static int ENCODER_LIMIT = 150; 84 private static int DECODER_LIMIT = 150; 85 private static int CAMERA_LIMIT = 80; 86 87 private Writer mProcMemWriter; 88 private Writer mMemWriter; 89 90 private CamcorderProfile mCamcorderProfile; 91 private int mVideoWidth; 92 private int mVideoHeight; 93 94 Camera mCamera; 95 96 public MediaPlayerPerformance() { 97 super("com.android.mediaframeworktest", MediaFrameworkTest.class); 98 } 99 100 @Override 101 protected void setUp() throws Exception { 102 super.setUp(); 103 //Insert a 2 second before launching the test activity. This is 104 //the workaround for the race condition of requesting the updated surface. 105 Thread.sleep(SHORT_WAIT); 106 getActivity(); 107 //Check if the device support the camcorder 108 mCamcorderProfile = CamcorderProfile.get(CAMERA_ID); 109 if (mCamcorderProfile != null) { 110 mVideoWidth = mCamcorderProfile.videoFrameWidth; 111 mVideoHeight = mCamcorderProfile.videoFrameHeight; 112 Log.v(TAG, "height = " + mVideoHeight + " width= " + mVideoWidth); 113 } 114 if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump) 115 MediaTestUtil.getNativeHeapDump(this.getName() + "_before"); 116 117 if (MediaFrameworkPerfTestRunner.mGetProcmem) { 118 mProcMemWriter = new BufferedWriter(new FileWriter 119 (new File(MEDIA_PROCMEM_OUTPUT), true)); 120 mProcMemWriter.write(this.getName() + "\n"); 121 } 122 mMemWriter = new BufferedWriter(new FileWriter 123 (new File(MEDIA_MEMORY_OUTPUT), true)); 124 mMemWriter.write(this.getName() + "\n"); 125 } 126 127 @Override 128 protected void tearDown() throws Exception { 129 if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump) 130 MediaTestUtil.getNativeHeapDump(this.getName() + "_after"); 131 132 if (MediaFrameworkPerfTestRunner.mGetProcmem) { 133 mProcMemWriter.close(); 134 } 135 mMemWriter.write("\n"); 136 mMemWriter.close(); 137 super.tearDown(); 138 } 139 140 private void initializeMessageLooper() { 141 final ConditionVariable startDone = new ConditionVariable(); 142 new Thread() { 143 @Override 144 public void run() { 145 Looper.prepare(); 146 Log.v(TAG, "start loopRun"); 147 mLooper = Looper.myLooper(); 148 mCamera = Camera.open(CAMERA_ID); 149 startDone.open(); 150 Looper.loop(); 151 Log.v(TAG, "initializeMessageLooper: quit."); 152 } 153 }.start(); 154 155 if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 156 fail("initializeMessageLooper: start timeout"); 157 } 158 } 159 160 private void terminateMessageLooper() throws Exception { 161 mLooper.quit(); 162 // Looper.quit() is asynchronous. The looper may still has some 163 // preview callbacks in the queue after quit is called. The preview 164 // callback still uses the camera object (setHasPreviewCallback). 165 // After camera is released, RuntimeException will be thrown from 166 // the method. So we need to join the looper thread here. 167 mLooper.getThread().join(); 168 mCamera.release(); 169 } 170 171 private final class RawPreviewCallback implements PreviewCallback { 172 @Override 173 public void onPreviewFrame(byte[] rawData, Camera camera) { 174 mPreviewDone.open(); 175 } 176 } 177 178 private void waitForPreviewDone() { 179 if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 180 Log.v(TAG, "waitForPreviewDone: timeout"); 181 } 182 mPreviewDone.close(); 183 } 184 185 public void stressCameraPreview() { 186 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 187 try { 188 initializeMessageLooper(); 189 mCamera.setPreviewCallback(mRawPreviewCallback); 190 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 191 mCamera.setPreviewDisplay(mSurfaceHolder); 192 mCamera.startPreview(); 193 waitForPreviewDone(); 194 Thread.sleep(1000); 195 mCamera.stopPreview(); 196 terminateMessageLooper(); 197 } catch (Exception e) { 198 Log.v(TAG, e.toString()); 199 } 200 } 201 } 202 203 // Note: This test is to assume the mediaserver's pid is 34 204 public void mediaStressPlayback(String testFilePath) { 205 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 206 MediaPlayer mp = new MediaPlayer(); 207 try { 208 mp.setDataSource(testFilePath); 209 mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); 210 mp.prepare(); 211 mp.start(); 212 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 213 mp.release(); 214 } catch (Exception e) { 215 mp.release(); 216 Log.v(TAG, e.toString()); 217 } 218 } 219 } 220 221 // Note: This test is to assume the mediaserver's pid is 34 222 private boolean stressVideoRecord(int frameRate, int width, int height, int videoFormat, 223 int outFormat, String outFile, boolean videoOnly) { 224 // Video recording 225 boolean doesTestFail = false; 226 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 227 MediaRecorder mRecorder = new MediaRecorder(); 228 try { 229 if (!videoOnly) { 230 Log.v(TAG, "setAudioSource"); 231 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 232 } 233 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 234 mRecorder.setOutputFormat(outFormat); 235 Log.v(TAG, "output format " + outFormat); 236 mRecorder.setOutputFile(outFile); 237 mRecorder.setVideoFrameRate(frameRate); 238 mRecorder.setVideoSize(width, height); 239 Log.v(TAG, "setEncoder"); 240 mRecorder.setVideoEncoder(videoFormat); 241 if (!videoOnly) { 242 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 243 } 244 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 245 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 246 mRecorder.prepare(); 247 mRecorder.start(); 248 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 249 mRecorder.stop(); 250 mRecorder.release(); 251 //Insert 2 seconds to make sure the camera released. 252 Thread.sleep(SHORT_WAIT); 253 } catch (Exception e) { 254 Log.v("record video failed ", e.toString()); 255 mRecorder.release(); 256 doesTestFail = true; 257 break; 258 } 259 } 260 return !doesTestFail; 261 } 262 263 public void stressAudioRecord(String filePath) { 264 // This test is only for the short media file 265 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 266 MediaRecorder mRecorder = new MediaRecorder(); 267 try { 268 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 269 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 270 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 271 mRecorder.setOutputFile(filePath); 272 mRecorder.prepare(); 273 mRecorder.start(); 274 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 275 mRecorder.stop(); 276 mRecorder.release(); 277 } catch (Exception e) { 278 Log.v(TAG, e.toString()); 279 mRecorder.release(); 280 } 281 } 282 } 283 284 //Write the ps output to the file 285 public void getMemoryWriteToLog(int writeCount) { 286 String memusage = null; 287 try { 288 if (writeCount == 0) { 289 mStartMemory = getMediaserverVsize(); 290 mMemWriter.write("Start memory : " + mStartMemory + "\n"); 291 } 292 memusage = captureMediaserverInfo(); 293 mMemWriter.write(memusage); 294 if (writeCount == NUM_STRESS_LOOP - 1) { 295 mEndMemory = getMediaserverVsize(); 296 mMemWriter.write("End Memory :" + mEndMemory + "\n"); 297 } 298 } catch (Exception e) { 299 e.toString(); 300 } 301 } 302 303 public void writeProcmemInfo() throws Exception { 304 if (MediaFrameworkPerfTestRunner.mGetProcmem) { 305 String cmd = "procmem " + getMediaserverPid(); 306 Process p = Runtime.getRuntime().exec(cmd); 307 308 InputStream inStream = p.getInputStream(); 309 InputStreamReader inReader = new InputStreamReader(inStream); 310 BufferedReader inBuffer = new BufferedReader(inReader); 311 String s; 312 while ((s = inBuffer.readLine()) != null) { 313 mProcMemWriter.write(s); 314 mProcMemWriter.write("\n"); 315 } 316 mProcMemWriter.write("\n\n"); 317 } 318 } 319 320 public String captureMediaserverInfo() { 321 String cm = "ps mediaserver"; 322 String memoryUsage = null; 323 324 int ch; 325 try { 326 Process p = Runtime.getRuntime().exec(cm); 327 InputStream in = p.getInputStream(); 328 StringBuffer sb = new StringBuffer(512); 329 while ((ch = in.read()) != -1) { 330 sb.append((char) ch); 331 } 332 memoryUsage = sb.toString(); 333 } catch (IOException e) { 334 Log.v(TAG, e.toString()); 335 } 336 String[] poList = memoryUsage.split("\r|\n|\r\n"); 337 // A new media.log is enabled with ro.test_harness is set. 338 // The output of "ps mediaserver" will include the 339 // media.log process in the first line. Update the parsing 340 // to only read the thrid line. 341 // Smaple ps mediaserver output: 342 // USER PID PPID VSIZE RSS WCHAN PC NAME 343 // media 131 1 13676 4796 ffffffff 400b1bd0 S media.log 344 // media 219 131 37768 6892 ffffffff 400b236c S /system/bin/mediaserver 345 String memusage = poList[poList.length-1].concat("\n"); 346 return memusage; 347 } 348 349 public int getMediaserverPid(){ 350 String memoryUsage = null; 351 int pidvalue = 0; 352 memoryUsage = captureMediaserverInfo(); 353 String[] poList2 = memoryUsage.split("\t|\\s+"); 354 String pid = poList2[1]; 355 pidvalue = Integer.parseInt(pid); 356 Log.v(TAG, "PID = " + pidvalue); 357 return pidvalue; 358 } 359 360 public int getMediaserverVsize(){ 361 String memoryUsage = captureMediaserverInfo(); 362 String[] poList2 = memoryUsage.split("\t|\\s+"); 363 String vsize = poList2[3]; 364 int vsizevalue = Integer.parseInt(vsize); 365 Log.v(TAG, "VSIZE = " + vsizevalue); 366 return vsizevalue; 367 } 368 369 public boolean validateMemoryResult(int startPid, int startMemory, int limit) 370 throws Exception { 371 // Wait for 10 seconds to make sure the memory settle. 372 Thread.sleep(10000); 373 mEndPid = getMediaserverPid(); 374 int memDiff = mEndMemory - startMemory; 375 if (memDiff < 0) { 376 memDiff = 0; 377 } 378 mMemWriter.write("The total diff = " + memDiff); 379 mMemWriter.write("\n\n"); 380 // mediaserver crash 381 if (startPid != mEndPid) { 382 mMemWriter.write("mediaserver died. Test failed\n"); 383 return false; 384 } 385 // memory leak greter than the tolerant 386 if (memDiff > limit) return false; 387 return true; 388 } 389 390 // Test case 1: Capture the memory usage after every 20 h263 playback 391 @LargeTest 392 public void testH263VideoPlaybackMemoryUsage() throws Exception { 393 boolean memoryResult = false; 394 395 mStartPid = getMediaserverPid(); 396 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 397 mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263); 398 getMemoryWriteToLog(i); 399 writeProcmemInfo(); 400 } 401 memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT); 402 assertTrue("H263 playback memory test", memoryResult); 403 } 404 405 // Test case 2: Capture the memory usage after every 20 h264 playback 406 @LargeTest 407 public void testH264VideoPlaybackMemoryUsage() throws Exception { 408 boolean memoryResult = false; 409 410 mStartPid = getMediaserverPid(); 411 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 412 mediaStressPlayback(MediaNames.VIDEO_H264_AMR); 413 getMemoryWriteToLog(i); 414 writeProcmemInfo(); 415 } 416 memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT); 417 assertTrue("H264 playback memory test", memoryResult); 418 } 419 420 // Test case 4: Capture the memory usage after every 20 video only recorded 421 @LargeTest 422 public void testH263RecordVideoOnlyMemoryUsage() throws Exception { 423 if (mCamcorderProfile != null) { 424 boolean memoryResult = false; 425 mStartPid = getMediaserverPid(); 426 int frameRate = MediaProfileReader 427 .getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263); 428 assertTrue("H263 video recording frame rate", frameRate != -1); 429 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 430 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight, 431 MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4, 432 MediaNames.RECORDED_VIDEO_3GP, true)); 433 getMemoryWriteToLog(i); 434 writeProcmemInfo(); 435 } 436 memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT); 437 assertTrue("H263 record only memory test", memoryResult); 438 } 439 } 440 441 // Test case 5: Capture the memory usage after every 20 video only recorded 442 @LargeTest 443 public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception { 444 if (mCamcorderProfile != null) { 445 boolean memoryResult = false; 446 mStartPid = getMediaserverPid(); 447 int frameRate = MediaProfileReader.getMaxFrameRateForCodec 448 (MediaRecorder.VideoEncoder.MPEG_4_SP); 449 assertTrue("MPEG4 video recording frame rate", frameRate != -1); 450 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 451 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight, 452 MediaRecorder.VideoEncoder.MPEG_4_SP, MediaRecorder.OutputFormat.MPEG_4, 453 MediaNames.RECORDED_VIDEO_3GP, true)); 454 getMemoryWriteToLog(i); 455 writeProcmemInfo(); 456 } 457 memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT); 458 assertTrue("mpeg4 record only memory test", memoryResult); 459 } 460 } 461 462 // Test case 6: Capture the memory usage after every 20 video and audio 463 // recorded 464 @LargeTest 465 public void testRecordVideoAudioMemoryUsage() throws Exception { 466 if (mCamcorderProfile != null) { 467 boolean memoryResult = false; 468 mStartPid = getMediaserverPid(); 469 int frameRate = MediaProfileReader 470 .getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263); 471 assertTrue("H263 video recording frame rate", frameRate != -1); 472 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 473 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight, 474 MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4, 475 MediaNames.RECORDED_VIDEO_3GP, false)); 476 getMemoryWriteToLog(i); 477 writeProcmemInfo(); 478 } 479 memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT); 480 assertTrue("H263 audio video record memory test", memoryResult); 481 } 482 } 483 484 // Test case 7: Capture the memory usage after every 20 audio only recorded 485 @LargeTest 486 public void testRecordAudioOnlyMemoryUsage() throws Exception { 487 boolean memoryResult = false; 488 489 mStartPid = getMediaserverPid(); 490 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 491 stressAudioRecord(MediaNames.RECORDER_OUTPUT); 492 getMemoryWriteToLog(i); 493 writeProcmemInfo(); 494 } 495 memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT); 496 assertTrue("audio record only memory test", memoryResult); 497 } 498 499 // Test case 8: Capture the memory usage after every 20 camera preview 500 @LargeTest 501 public void testCameraPreviewMemoryUsage() throws Exception { 502 boolean memoryResult = false; 503 504 mStartPid = getMediaserverPid(); 505 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 506 stressCameraPreview(); 507 getMemoryWriteToLog(i); 508 writeProcmemInfo(); 509 } 510 memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT); 511 assertTrue("camera preview memory test", memoryResult); 512 } 513 } 514