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