Home | History | Annotate | Download | only in performance
      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