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