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