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.MediaNames; 21 22 import android.database.sqlite.SQLiteDatabase; 23 import android.hardware.Camera; 24 import android.hardware.Camera.PreviewCallback; 25 import android.media.MediaPlayer; 26 import android.media.MediaRecorder; 27 import android.os.ConditionVariable; 28 import android.os.Looper; 29 import android.os.SystemClock; 30 import android.test.ActivityInstrumentationTestCase; 31 import android.test.suitebuilder.annotation.LargeTest; 32 import android.test.suitebuilder.annotation.Suppress; 33 import android.util.Log; 34 import android.view.SurfaceHolder; 35 36 import java.io.FileDescriptor; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.Writer; 42 import java.io.File; 43 import java.io.FileWriter; 44 import java.io.BufferedWriter; 45 46 import android.media.MediaMetadataRetriever; 47 import com.android.mediaframeworktest.MediaProfileReader; 48 49 import android.hardware.Camera.PreviewCallback; 50 51 /** 52 * Junit / Instrumentation - performance measurement for media player and 53 * recorder 54 */ 55 public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<MediaFrameworkTest> { 56 57 private String TAG = "MediaPlayerPerformance"; 58 59 private SQLiteDatabase mDB; 60 private SurfaceHolder mSurfaceHolder = null; 61 private static final int NUM_STRESS_LOOP = 10; 62 private static final int NUM_PLAYBACk_IN_EACH_LOOP = 20; 63 private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds 64 private static final String MEDIA_MEMORY_OUTPUT = 65 "/sdcard/mediaMemOutput.txt"; 66 67 private static int mStartMemory = 0; 68 private static int mEndMemory = 0; 69 private static int mStartPid = 0; 70 private static int mEndPid = 0; 71 72 private Looper mLooper = null; 73 private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback(); 74 private final ConditionVariable mPreviewDone = new ConditionVariable(); 75 private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; // Milliseconds. 76 77 //the tolerant memory leak 78 private static int ENCODER_LIMIT = 150; 79 private static int DECODER_LIMIT = 150; 80 private static int CAMERA_LIMIT = 80; 81 82 Camera mCamera; 83 84 public MediaPlayerPerformance() { 85 super("com.android.mediaframeworktest", MediaFrameworkTest.class); 86 } 87 88 protected void setUp() throws Exception { 89 super.setUp(); 90 } 91 92 public void createDB() { 93 mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null); 94 mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," + 95 "file TEXT," + "setdatatime LONG," + "preparetime LONG," + 96 "playtime LONG" + ");"); 97 //clean the table before adding new data 98 mDB.execSQL("DELETE FROM perfdata"); 99 } 100 101 public void audioPlaybackStartupTime(String[] testFile) { 102 long t1 = 0; 103 long t2 = 0; 104 long t3 = 0; 105 long t4 = 0; 106 long setDataSourceDuration = 0; 107 long prepareDuration = 0; 108 long startDuration = 0; 109 long totalSetDataTime = 0; 110 long totalPrepareTime = 0; 111 long totalStartDuration = 0; 112 113 int numberOfFiles = testFile.length; 114 Log.v(TAG, "File length " + numberOfFiles); 115 for (int k = 0; k < numberOfFiles; k++) { 116 MediaPlayer mp = new MediaPlayer(); 117 try { 118 t1 = SystemClock.uptimeMillis(); 119 FileInputStream fis = new FileInputStream(testFile[k]); 120 FileDescriptor fd = fis.getFD(); 121 mp.setDataSource(fd); 122 fis.close(); 123 t2 = SystemClock.uptimeMillis(); 124 mp.prepare(); 125 t3 = SystemClock.uptimeMillis(); 126 mp.start(); 127 t4 = SystemClock.uptimeMillis(); 128 } catch (Exception e) { 129 Log.v(TAG, e.toString()); 130 } 131 setDataSourceDuration = t2 - t1; 132 prepareDuration = t3 - t2; 133 startDuration = t4 - t3; 134 totalSetDataTime = totalSetDataTime + setDataSourceDuration; 135 totalPrepareTime = totalPrepareTime + prepareDuration; 136 totalStartDuration = totalStartDuration + startDuration; 137 mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," + 138 " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' + 139 setDataSourceDuration + ',' + prepareDuration + 140 ',' + startDuration + ");"); 141 Log.v(TAG, "File name " + testFile[k]); 142 mp.stop(); 143 mp.release(); 144 } 145 Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles); 146 Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles); 147 Log.v(TAG, "start average " + totalStartDuration / numberOfFiles); 148 149 } 150 151 @Suppress 152 public void testStartUpTime() throws Exception { 153 createDB(); 154 audioPlaybackStartupTime(MediaNames.MP3FILES); 155 audioPlaybackStartupTime(MediaNames.AACFILES); 156 157 //close the database after all transactions 158 if (mDB.isOpen()) { 159 mDB.close(); 160 } 161 } 162 163 public void wmametadatautility(String[] testFile) { 164 long t1 = 0; 165 long t2 = 0; 166 long sum = 0; 167 long duration = 0; 168 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 169 String value; 170 for (int i = 0, n = testFile.length; i < n; ++i) { 171 try { 172 t1 = SystemClock.uptimeMillis(); 173 retriever.setDataSource(testFile[i]); 174 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); 175 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST); 176 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER); 177 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE); 178 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); 179 value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR); 180 value = 181 retriever 182 .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER); 183 t2 = SystemClock.uptimeMillis(); 184 duration = t2 - t1; 185 Log.v(TAG, "Time taken = " + duration); 186 sum = sum + duration; 187 } catch (Exception e) { 188 Log.v(TAG, e.getMessage()); 189 } 190 191 } 192 Log.v(TAG, "Average duration = " + sum / testFile.length); 193 } 194 195 private void initializeMessageLooper() { 196 final ConditionVariable startDone = new ConditionVariable(); 197 new Thread() { 198 @Override 199 public void run() { 200 Looper.prepare(); 201 Log.v(TAG, "start loopRun"); 202 mLooper = Looper.myLooper(); 203 mCamera = Camera.open(); 204 startDone.open(); 205 Looper.loop(); 206 Log.v(TAG, "initializeMessageLooper: quit."); 207 } 208 }.start(); 209 210 if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 211 fail("initializeMessageLooper: start timeout"); 212 } 213 } 214 215 private void terminateMessageLooper() throws Exception { 216 mLooper.quit(); 217 // Looper.quit() is asynchronous. The looper may still has some 218 // preview callbacks in the queue after quit is called. The preview 219 // callback still uses the camera object (setHasPreviewCallback). 220 // After camera is released, RuntimeException will be thrown from 221 // the method. So we need to join the looper thread here. 222 mLooper.getThread().join(); 223 mCamera.release(); 224 } 225 226 private final class RawPreviewCallback implements PreviewCallback { 227 public void onPreviewFrame(byte[] rawData, Camera camera) { 228 mPreviewDone.open(); 229 } 230 } 231 232 private void waitForPreviewDone() { 233 if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 234 Log.v(TAG, "waitForPreviewDone: timeout"); 235 } 236 mPreviewDone.close(); 237 } 238 239 public void stressCameraPreview() { 240 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 241 try { 242 initializeMessageLooper(); 243 mCamera.setPreviewCallback(mRawPreviewCallback); 244 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 245 mCamera.setPreviewDisplay(mSurfaceHolder); 246 mCamera.startPreview(); 247 waitForPreviewDone(); 248 Thread.sleep(1000); 249 mCamera.stopPreview(); 250 terminateMessageLooper(); 251 } catch (Exception e) { 252 Log.v(TAG, e.toString()); 253 } 254 } 255 } 256 257 // Note: This test is to assume the mediaserver's pid is 34 258 public void mediaStressPlayback(String testFilePath) { 259 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 260 MediaPlayer mp = new MediaPlayer(); 261 try { 262 mp.setDataSource(testFilePath); 263 mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); 264 mp.prepare(); 265 mp.start(); 266 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 267 mp.release(); 268 } catch (Exception e) { 269 mp.release(); 270 Log.v(TAG, e.toString()); 271 } 272 } 273 } 274 275 // Note: This test is to assume the mediaserver's pid is 34 276 private void stressVideoRecord(int frameRate, int width, int height, int videoFormat, 277 int outFormat, String outFile, boolean videoOnly) { 278 // Video recording 279 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 280 MediaRecorder mRecorder = new MediaRecorder(); 281 try { 282 if (!videoOnly) { 283 Log.v(TAG, "setAudioSource"); 284 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 285 } 286 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 287 mRecorder.setOutputFormat(outFormat); 288 Log.v(TAG, "output format " + outFormat); 289 mRecorder.setOutputFile(outFile); 290 mRecorder.setVideoFrameRate(frameRate); 291 mRecorder.setVideoSize(width, height); 292 Log.v(TAG, "setEncoder"); 293 mRecorder.setVideoEncoder(videoFormat); 294 if (!videoOnly) { 295 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 296 } 297 mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); 298 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 299 mRecorder.prepare(); 300 mRecorder.start(); 301 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 302 mRecorder.stop(); 303 mRecorder.release(); 304 } catch (Exception e) { 305 Log.v("record video failed ", e.toString()); 306 mRecorder.release(); 307 } 308 } 309 } 310 311 public void stressAudioRecord(String filePath) { 312 // This test is only for the short media file 313 for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) { 314 MediaRecorder mRecorder = new MediaRecorder(); 315 try { 316 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 317 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 318 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 319 mRecorder.setOutputFile(filePath); 320 mRecorder.prepare(); 321 mRecorder.start(); 322 Thread.sleep(MEDIA_STRESS_WAIT_TIME); 323 mRecorder.stop(); 324 mRecorder.release(); 325 } catch (Exception e) { 326 Log.v(TAG, e.toString()); 327 mRecorder.release(); 328 } 329 } 330 } 331 332 //Write the ps output to the file 333 public void getMemoryWriteToLog(Writer output, int writeCount) { 334 String memusage = null; 335 try { 336 if (writeCount == 0) { 337 mStartMemory = getMediaserverVsize(); 338 output.write("Start memory : " + mStartMemory + "\n"); 339 } 340 memusage = captureMediaserverInfo(); 341 output.write(memusage); 342 if (writeCount == NUM_STRESS_LOOP - 1) { 343 mEndMemory = getMediaserverVsize(); 344 output.write("End Memory :" + mEndMemory + "\n"); 345 } 346 } catch (Exception e) { 347 e.toString(); 348 } 349 } 350 351 public String captureMediaserverInfo() { 352 String cm = "ps mediaserver"; 353 String memoryUsage = null; 354 355 int ch; 356 try { 357 Process p = Runtime.getRuntime().exec(cm); 358 InputStream in = p.getInputStream(); 359 StringBuffer sb = new StringBuffer(512); 360 while ((ch = in.read()) != -1) { 361 sb.append((char) ch); 362 } 363 memoryUsage = sb.toString(); 364 } catch (IOException e) { 365 Log.v(TAG, e.toString()); 366 } 367 String[] poList = memoryUsage.split("\r|\n|\r\n"); 368 String memusage = poList[1].concat("\n"); 369 return memusage; 370 } 371 372 public int getMediaserverPid(){ 373 String memoryUsage = null; 374 int pidvalue = 0; 375 memoryUsage = captureMediaserverInfo(); 376 String[] poList2 = memoryUsage.split("\t|\\s+"); 377 String pid = poList2[1]; 378 pidvalue = Integer.parseInt(pid); 379 Log.v(TAG, "PID = " + pidvalue); 380 return pidvalue; 381 } 382 383 public int getMediaserverVsize(){ 384 String memoryUsage = captureMediaserverInfo(); 385 String[] poList2 = memoryUsage.split("\t|\\s+"); 386 String vsize = poList2[3]; 387 int vsizevalue = Integer.parseInt(vsize); 388 Log.v(TAG, "VSIZE = " + vsizevalue); 389 return vsizevalue; 390 } 391 392 public boolean validateMemoryResult(int startPid, int startMemory, Writer output, int limit) 393 throws Exception { 394 // Wait for 10 seconds to make sure the memory settle. 395 Thread.sleep(10000); 396 mEndPid = getMediaserverPid(); 397 int memDiff = mEndMemory - startMemory; 398 if (memDiff < 0) { 399 memDiff = 0; 400 } 401 output.write("The total diff = " + memDiff); 402 output.write("\n\n"); 403 // mediaserver crash 404 if (startPid != mEndPid) { 405 output.write("mediaserver died. Test failed\n"); 406 return false; 407 } 408 // memory leak greter than the tolerant 409 if (memDiff > limit) return false; 410 return true; 411 } 412 413 @Suppress 414 public void testWmaParseTime() throws Exception { 415 // createDB(); 416 wmametadatautility(MediaNames.WMASUPPORTED); 417 } 418 419 420 // Test case 1: Capture the memory usage after every 20 h263 playback 421 @LargeTest 422 public void testH263VideoPlaybackMemoryUsage() throws Exception { 423 boolean memoryResult = false; 424 mStartPid = getMediaserverPid(); 425 426 File h263MemoryOut = new File(MEDIA_MEMORY_OUTPUT); 427 Writer output = new BufferedWriter(new FileWriter(h263MemoryOut, true)); 428 output.write("H263 Video Playback Only\n"); 429 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 430 mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263); 431 getMemoryWriteToLog(output, i); 432 } 433 output.write("\n"); 434 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT); 435 output.close(); 436 assertTrue("H263 playback memory test", memoryResult); 437 } 438 439 // Test case 2: Capture the memory usage after every 20 h264 playback 440 @LargeTest 441 public void testH264VideoPlaybackMemoryUsage() throws Exception { 442 boolean memoryResult = false; 443 mStartPid = getMediaserverPid(); 444 445 File h264MemoryOut = new File(MEDIA_MEMORY_OUTPUT); 446 Writer output = new BufferedWriter(new FileWriter(h264MemoryOut, true)); 447 output.write("H264 Video Playback only\n"); 448 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 449 mediaStressPlayback(MediaNames.VIDEO_H264_AMR); 450 getMemoryWriteToLog(output, i); 451 } 452 output.write("\n"); 453 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT); 454 output.close(); 455 assertTrue("H264 playback memory test", memoryResult); 456 } 457 458 // Test case 3: Capture the memory usage after each 20 WMV playback 459 @LargeTest 460 public void testWMVVideoPlaybackMemoryUsage() throws Exception { 461 boolean memoryResult = false; 462 if (MediaProfileReader.getWMVEnable()){ 463 mStartPid = getMediaserverPid(); 464 File wmvMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 465 Writer output = new BufferedWriter(new FileWriter(wmvMemoryOut, true)); 466 output.write("WMV video playback only\n"); 467 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 468 mediaStressPlayback(MediaNames.VIDEO_WMV); 469 getMemoryWriteToLog(output, i); 470 } 471 output.write("\n"); 472 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT); 473 output.close(); 474 assertTrue("wmv playback memory test", memoryResult); 475 } 476 } 477 478 // Test case 4: Capture the memory usage after every 20 video only recorded 479 @LargeTest 480 public void testH263RecordVideoOnlyMemoryUsage() throws Exception { 481 boolean memoryResult = false; 482 mStartPid = getMediaserverPid(); 483 484 File videoH263RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 485 Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true)); 486 output.write("H263 video record only\n"); 487 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 488 stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263, 489 MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true); 490 getMemoryWriteToLog(output, i); 491 } 492 output.write("\n"); 493 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT); 494 output.close(); 495 assertTrue("H263 record only memory test", memoryResult); 496 } 497 498 // Test case 5: Capture the memory usage after every 20 video only recorded 499 @LargeTest 500 public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception { 501 boolean memoryResult = false; 502 mStartPid = getMediaserverPid(); 503 504 File videoMp4RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 505 Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true)); 506 output.write("MPEG4 video record only\n"); 507 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 508 stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP, 509 MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true); 510 getMemoryWriteToLog(output, i); 511 } 512 output.write("\n"); 513 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT); 514 output.close(); 515 assertTrue("mpeg4 record only memory test", memoryResult); 516 } 517 518 // Test case 6: Capture the memory usage after every 20 video and audio 519 // recorded 520 @LargeTest 521 public void testRecordVideoAudioMemoryUsage() throws Exception { 522 boolean memoryResult = false; 523 mStartPid = getMediaserverPid(); 524 525 File videoRecordAudioMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 526 Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true)); 527 output.write("Audio and h263 video record\n"); 528 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 529 stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263, 530 MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false); 531 getMemoryWriteToLog(output, i); 532 } 533 output.write("\n"); 534 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT); 535 output.close(); 536 assertTrue("H263 audio video record memory test", memoryResult); 537 } 538 539 // Test case 7: Capture the memory usage after every 20 audio only recorded 540 @LargeTest 541 public void testRecordAudioOnlyMemoryUsage() throws Exception { 542 boolean memoryResult = false; 543 mStartPid = getMediaserverPid(); 544 545 File audioOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 546 Writer output = new BufferedWriter(new FileWriter(audioOnlyMemoryOut, true)); 547 output.write("Audio record only\n"); 548 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 549 stressAudioRecord(MediaNames.RECORDER_OUTPUT); 550 getMemoryWriteToLog(output, i); 551 } 552 output.write("\n"); 553 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT); 554 output.close(); 555 assertTrue("audio record only memory test", memoryResult); 556 } 557 558 // Test case 8: Capture the memory usage after every 20 camera preview 559 @LargeTest 560 public void testCameraPreviewMemoryUsage() throws Exception { 561 boolean memoryResult = false; 562 mStartPid = getMediaserverPid(); 563 564 File cameraPreviewMemoryOut = new File(MEDIA_MEMORY_OUTPUT); 565 Writer output = new BufferedWriter(new FileWriter(cameraPreviewMemoryOut, true)); 566 output.write("Camera Preview Only\n"); 567 for (int i = 0; i < NUM_STRESS_LOOP; i++) { 568 stressCameraPreview(); 569 getMemoryWriteToLog(output, i); 570 } 571 output.write("\n"); 572 memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, CAMERA_LIMIT); 573 output.close(); 574 assertTrue("camera preview memory test", memoryResult); 575 } 576 } 577