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        getActivity();
105        if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
106            MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
107
108        mProcMemWriter = new BufferedWriter(new FileWriter
109                (new File(MEDIA_PROCMEM_OUTPUT), true));
110        mProcMemWriter.write(this.getName() + "\n");
111        mMemWriter = new BufferedWriter(new FileWriter
112                (new File(MEDIA_MEMORY_OUTPUT), true));
113
114    }
115
116    protected void tearDown() throws Exception {
117        if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
118            MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
119        mProcMemWriter.close();
120        mMemWriter.close();
121        super.tearDown();
122    }
123
124    private void initializeMessageLooper() {
125        final ConditionVariable startDone = new ConditionVariable();
126        new Thread() {
127            @Override
128            public void run() {
129                Looper.prepare();
130                Log.v(TAG, "start loopRun");
131                mLooper = Looper.myLooper();
132                mCamera = Camera.open();
133                startDone.open();
134                Looper.loop();
135                Log.v(TAG, "initializeMessageLooper: quit.");
136            }
137        }.start();
138
139        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
140            fail("initializeMessageLooper: start timeout");
141        }
142    }
143
144    private void terminateMessageLooper() throws Exception {
145        mLooper.quit();
146        // Looper.quit() is asynchronous. The looper may still has some
147        // preview callbacks in the queue after quit is called. The preview
148        // callback still uses the camera object (setHasPreviewCallback).
149        // After camera is released, RuntimeException will be thrown from
150        // the method. So we need to join the looper thread here.
151        mLooper.getThread().join();
152        mCamera.release();
153    }
154
155    private final class RawPreviewCallback implements PreviewCallback {
156        public void onPreviewFrame(byte[] rawData, Camera camera) {
157            mPreviewDone.open();
158        }
159    }
160
161    private void waitForPreviewDone() {
162        if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
163            Log.v(TAG, "waitForPreviewDone: timeout");
164        }
165        mPreviewDone.close();
166    }
167
168    public void stressCameraPreview() {
169        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
170            try {
171                initializeMessageLooper();
172                mCamera.setPreviewCallback(mRawPreviewCallback);
173                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
174                mCamera.setPreviewDisplay(mSurfaceHolder);
175                mCamera.startPreview();
176                waitForPreviewDone();
177                Thread.sleep(1000);
178                mCamera.stopPreview();
179                terminateMessageLooper();
180            } catch (Exception e) {
181                Log.v(TAG, e.toString());
182            }
183        }
184    }
185
186    // Note: This test is to assume the mediaserver's pid is 34
187    public void mediaStressPlayback(String testFilePath) {
188        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
189            MediaPlayer mp = new MediaPlayer();
190            try {
191                mp.setDataSource(testFilePath);
192                mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
193                mp.prepare();
194                mp.start();
195                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
196                mp.release();
197            } catch (Exception e) {
198                mp.release();
199                Log.v(TAG, e.toString());
200            }
201        }
202    }
203
204    // Note: This test is to assume the mediaserver's pid is 34
205    private boolean stressVideoRecord(int frameRate, int width, int height, int videoFormat,
206            int outFormat, String outFile, boolean videoOnly) {
207        // Video recording
208        boolean doesTestFail = false;
209        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
210            MediaRecorder mRecorder = new MediaRecorder();
211            try {
212                if (!videoOnly) {
213                    Log.v(TAG, "setAudioSource");
214                    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
215                }
216                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
217                mRecorder.setOutputFormat(outFormat);
218                Log.v(TAG, "output format " + outFormat);
219                mRecorder.setOutputFile(outFile);
220                mRecorder.setVideoFrameRate(frameRate);
221                mRecorder.setVideoSize(width, height);
222                Log.v(TAG, "setEncoder");
223                mRecorder.setVideoEncoder(videoFormat);
224                if (!videoOnly) {
225                    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
226                }
227                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
228                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
229                mRecorder.prepare();
230                mRecorder.start();
231                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
232                mRecorder.stop();
233                mRecorder.release();
234            } catch (Exception e) {
235                Log.v("record video failed ", e.toString());
236                mRecorder.release();
237                doesTestFail = true;
238                break;
239            }
240        }
241        return !doesTestFail;
242    }
243
244    public void stressAudioRecord(String filePath) {
245        // This test is only for the short media file
246        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
247            MediaRecorder mRecorder = new MediaRecorder();
248            try {
249                mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
250                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
251                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
252                mRecorder.setOutputFile(filePath);
253                mRecorder.prepare();
254                mRecorder.start();
255                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
256                mRecorder.stop();
257                mRecorder.release();
258            } catch (Exception e) {
259                Log.v(TAG, e.toString());
260                mRecorder.release();
261            }
262        }
263    }
264
265    //Write the ps output to the file
266    public void getMemoryWriteToLog(int writeCount) {
267        String memusage = null;
268        try {
269            if (writeCount == 0) {
270                mStartMemory = getMediaserverVsize();
271                mMemWriter.write("Start memory : " + mStartMemory + "\n");
272            }
273            memusage = captureMediaserverInfo();
274            mMemWriter.write(memusage);
275            if (writeCount == NUM_STRESS_LOOP - 1) {
276                mEndMemory = getMediaserverVsize();
277                mMemWriter.write("End Memory :" + mEndMemory + "\n");
278            }
279        } catch (Exception e) {
280            e.toString();
281        }
282    }
283
284    public void writeProcmemInfo() throws Exception{
285        String cmd = "procmem " + getMediaserverPid();
286        Process p = Runtime.getRuntime().exec(cmd);
287
288        InputStream inStream = p.getInputStream();
289        InputStreamReader inReader = new InputStreamReader(inStream);
290        BufferedReader inBuffer = new BufferedReader(inReader);
291        String s;
292        while ((s = inBuffer.readLine()) != null) {
293              mProcMemWriter.write(s);
294              mProcMemWriter.write("\n");
295        }
296        mProcMemWriter.write("\n\n");
297    }
298
299    public String captureMediaserverInfo() {
300        String cm = "ps mediaserver";
301        String memoryUsage = null;
302
303        int ch;
304        try {
305            Process p = Runtime.getRuntime().exec(cm);
306            InputStream in = p.getInputStream();
307            StringBuffer sb = new StringBuffer(512);
308            while ((ch = in.read()) != -1) {
309                sb.append((char) ch);
310            }
311            memoryUsage = sb.toString();
312        } catch (IOException e) {
313            Log.v(TAG, e.toString());
314        }
315        String[] poList = memoryUsage.split("\r|\n|\r\n");
316        String memusage = poList[1].concat("\n");
317        return memusage;
318    }
319
320    public int getMediaserverPid(){
321        String memoryUsage = null;
322        int pidvalue = 0;
323        memoryUsage = captureMediaserverInfo();
324        String[] poList2 = memoryUsage.split("\t|\\s+");
325        String pid = poList2[1];
326        pidvalue = Integer.parseInt(pid);
327        Log.v(TAG, "PID = " + pidvalue);
328        return pidvalue;
329    }
330
331    public int getMediaserverVsize(){
332        String memoryUsage = captureMediaserverInfo();
333        String[] poList2 = memoryUsage.split("\t|\\s+");
334        String vsize = poList2[3];
335        int vsizevalue = Integer.parseInt(vsize);
336        Log.v(TAG, "VSIZE = " + vsizevalue);
337        return vsizevalue;
338    }
339
340    public boolean validateMemoryResult(int startPid, int startMemory, int limit)
341            throws Exception {
342        // Wait for 10 seconds to make sure the memory settle.
343        Thread.sleep(10000);
344        mEndPid = getMediaserverPid();
345        int memDiff = mEndMemory - startMemory;
346        if (memDiff < 0) {
347            memDiff = 0;
348        }
349        mMemWriter.write("The total diff = " + memDiff);
350        mMemWriter.write("\n\n");
351        // mediaserver crash
352        if (startPid != mEndPid) {
353            mMemWriter.write("mediaserver died. Test failed\n");
354            return false;
355        }
356        // memory leak greter than the tolerant
357        if (memDiff > limit) return false;
358        return true;
359    }
360
361    // Test case 1: Capture the memory usage after every 20 h263 playback
362    @LargeTest
363    public void testH263VideoPlaybackMemoryUsage() throws Exception {
364        boolean memoryResult = false;
365
366        mStartPid = getMediaserverPid();
367        mMemWriter.write("H263 Video Playback Only\n");
368        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
369            mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
370            getMemoryWriteToLog(i);
371            writeProcmemInfo();
372        }
373        mMemWriter.write("\n");
374        memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
375        assertTrue("H263 playback memory test", memoryResult);
376    }
377
378    // Test case 2: Capture the memory usage after every 20 h264 playback
379    @LargeTest
380    public void testH264VideoPlaybackMemoryUsage() throws Exception {
381        boolean memoryResult = false;
382
383        mStartPid = getMediaserverPid();
384        mMemWriter.write("H264 Video Playback only\n");
385        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
386            mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
387            getMemoryWriteToLog(i);
388            writeProcmemInfo();
389        }
390        mMemWriter.write("\n");
391        memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
392        assertTrue("H264 playback memory test", memoryResult);
393    }
394
395    // Test case 4: Capture the memory usage after every 20 video only recorded
396    @LargeTest
397    public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
398        boolean memoryResult = false;
399
400        mStartPid = getMediaserverPid();
401        mMemWriter.write("H263 video record only\n");
402        int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
403        assertTrue("H263 video recording frame rate", frameRate != -1);
404        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
405            assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
406                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
407            getMemoryWriteToLog(i);
408            writeProcmemInfo();
409        }
410        mMemWriter.write("\n");
411        memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
412        assertTrue("H263 record only memory test", memoryResult);
413    }
414
415    // Test case 5: Capture the memory usage after every 20 video only recorded
416    @LargeTest
417    public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
418        boolean memoryResult = false;
419
420        mStartPid = getMediaserverPid();
421        mMemWriter.write("MPEG4 video record only\n");
422        int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
423        assertTrue("MPEG4 video recording frame rate", frameRate != -1);
424        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
425            assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP,
426                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
427            getMemoryWriteToLog(i);
428            writeProcmemInfo();
429        }
430        mMemWriter.write("\n");
431        memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
432        assertTrue("mpeg4 record only memory test", memoryResult);
433    }
434
435    // Test case 6: Capture the memory usage after every 20 video and audio
436    // recorded
437    @LargeTest
438    public void testRecordVideoAudioMemoryUsage() throws Exception {
439        boolean memoryResult = false;
440
441        mStartPid = getMediaserverPid();
442        int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
443        assertTrue("H263 video recording frame rate", frameRate != -1);
444        mMemWriter.write("Audio and h263 video record\n");
445        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
446            assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
447                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
448            getMemoryWriteToLog(i);
449            writeProcmemInfo();
450        }
451        mMemWriter.write("\n");
452        memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
453        assertTrue("H263 audio video record memory test", memoryResult);
454    }
455
456    // Test case 7: Capture the memory usage after every 20 audio only recorded
457    @LargeTest
458    public void testRecordAudioOnlyMemoryUsage() throws Exception {
459        boolean memoryResult = false;
460
461        mStartPid = getMediaserverPid();
462        mMemWriter.write("Audio record only\n");
463        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
464            stressAudioRecord(MediaNames.RECORDER_OUTPUT);
465            getMemoryWriteToLog(i);
466            writeProcmemInfo();
467        }
468        mMemWriter.write("\n");
469        memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
470        assertTrue("audio record only memory test", memoryResult);
471    }
472
473    // Test case 8: Capture the memory usage after every 20 camera preview
474    @LargeTest
475    public void testCameraPreviewMemoryUsage() throws Exception {
476        boolean memoryResult = false;
477
478        mStartPid = getMediaserverPid();
479        mMemWriter.write("Camera Preview Only\n");
480        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
481            stressCameraPreview();
482            getMemoryWriteToLog(i);
483            writeProcmemInfo();
484        }
485        mMemWriter.write("\n");
486        memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
487        assertTrue("camera preview memory test", memoryResult);
488    }
489}
490