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 3: Capture the memory usage after every 20 hevc playback
    421     @LargeTest
    422     public void testHEVCVideoPlaybackMemoryUsage() throws Exception {
    423         boolean memoryResult = false;
    424 
    425         mStartPid = getMediaserverPid();
    426         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    427             mediaStressPlayback(MediaNames.VIDEO_HEVC_AAC);
    428             getMemoryWriteToLog(i);
    429             writeProcmemInfo();
    430         }
    431         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
    432         assertTrue("HEVC playback memory test", memoryResult);
    433     }
    434 
    435     // Test case 4: Capture the memory usage after every 20 mpeg2 playback
    436     @LargeTest
    437     public void testMPEG2VideoPlaybackMemoryUsage() throws Exception {
    438         boolean memoryResult = false;
    439 
    440         mStartPid = getMediaserverPid();
    441         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    442             mediaStressPlayback(MediaNames.VIDEO_MPEG2_AAC);
    443             getMemoryWriteToLog(i);
    444             writeProcmemInfo();
    445         }
    446         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
    447         assertTrue("MPEG2 playback memory test", memoryResult);
    448     }
    449 
    450     // Test case 5: Capture the memory usage after every 20 video only recorded
    451     @LargeTest
    452     public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
    453         if (mCamcorderProfile != null) {
    454             boolean memoryResult = false;
    455             mStartPid = getMediaserverPid();
    456             int frameRate = MediaProfileReader
    457                     .getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
    458             assertTrue("H263 video recording frame rate", frameRate != -1);
    459             for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    460                 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight,
    461                         MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4,
    462                         MediaNames.RECORDED_VIDEO_3GP, true));
    463                 getMemoryWriteToLog(i);
    464                 writeProcmemInfo();
    465             }
    466             memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
    467             assertTrue("H263 record only memory test", memoryResult);
    468         }
    469     }
    470 
    471     // Test case 6: Capture the memory usage after every 20 video only recorded
    472     @LargeTest
    473     public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
    474         if (mCamcorderProfile != null) {
    475             boolean memoryResult = false;
    476             mStartPid = getMediaserverPid();
    477             int frameRate = MediaProfileReader.getMaxFrameRateForCodec
    478                     (MediaRecorder.VideoEncoder.MPEG_4_SP);
    479             assertTrue("MPEG4 video recording frame rate", frameRate != -1);
    480             for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    481                 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight,
    482                         MediaRecorder.VideoEncoder.MPEG_4_SP, MediaRecorder.OutputFormat.MPEG_4,
    483                         MediaNames.RECORDED_VIDEO_3GP, true));
    484                 getMemoryWriteToLog(i);
    485                 writeProcmemInfo();
    486             }
    487             memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
    488             assertTrue("mpeg4 record only memory test", memoryResult);
    489         }
    490     }
    491 
    492     // Test case 7: Capture the memory usage after every 20 video and audio
    493     // recorded
    494     @LargeTest
    495     public void testRecordVideoAudioMemoryUsage() throws Exception {
    496         if (mCamcorderProfile != null) {
    497             boolean memoryResult = false;
    498             mStartPid = getMediaserverPid();
    499             int frameRate = MediaProfileReader
    500                     .getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
    501             assertTrue("H263 video recording frame rate", frameRate != -1);
    502             for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    503                 assertTrue(stressVideoRecord(frameRate, mVideoWidth, mVideoHeight,
    504                         MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4,
    505                         MediaNames.RECORDED_VIDEO_3GP, false));
    506                 getMemoryWriteToLog(i);
    507                 writeProcmemInfo();
    508             }
    509             memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
    510             assertTrue("H263 audio video record memory test", memoryResult);
    511         }
    512     }
    513 
    514     // Test case 8: Capture the memory usage after every 20 audio only recorded
    515     @LargeTest
    516     public void testRecordAudioOnlyMemoryUsage() throws Exception {
    517         boolean memoryResult = false;
    518 
    519         mStartPid = getMediaserverPid();
    520         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    521             stressAudioRecord(MediaNames.RECORDER_OUTPUT);
    522             getMemoryWriteToLog(i);
    523             writeProcmemInfo();
    524         }
    525         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
    526         assertTrue("audio record only memory test", memoryResult);
    527     }
    528 
    529     // Test case 9: Capture the memory usage after every 20 camera preview
    530     @LargeTest
    531     public void testCameraPreviewMemoryUsage() throws Exception {
    532         boolean memoryResult = false;
    533 
    534         mStartPid = getMediaserverPid();
    535         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
    536             stressCameraPreview();
    537             getMemoryWriteToLog(i);
    538             writeProcmemInfo();
    539         }
    540         memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
    541         assertTrue("camera preview memory test", memoryResult);
    542     }
    543 }
    544