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 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