MediaPlayerPerformance.java revision 48584d7b8f5e56b73bb13180bf6546b2647c1b28
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.MediaNames;
21
22import android.database.sqlite.SQLiteDatabase;
23import android.hardware.Camera;
24import android.hardware.Camera.PreviewCallback;
25import android.media.MediaPlayer;
26import android.media.MediaRecorder;
27import android.os.ConditionVariable;
28import android.os.Looper;
29import android.os.SystemClock;
30import android.test.ActivityInstrumentationTestCase;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.test.suitebuilder.annotation.Suppress;
33import android.util.Log;
34import android.view.SurfaceHolder;
35
36import java.io.FileDescriptor;
37import java.io.FileInputStream;
38import java.io.FileOutputStream;
39import java.io.IOException;
40import java.io.InputStream;
41import java.io.Writer;
42import java.io.File;
43import java.io.FileWriter;
44import java.io.BufferedWriter;
45
46import android.media.MediaMetadataRetriever;
47import com.android.mediaframeworktest.MediaProfileReader;
48
49import android.hardware.Camera.PreviewCallback;
50
51/**
52 * Junit / Instrumentation - performance measurement for media player and
53 * recorder
54 */
55public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
56
57    private String TAG = "MediaPlayerPerformance";
58
59    private SQLiteDatabase mDB;
60    private SurfaceHolder mSurfaceHolder = null;
61    private static final int NUM_STRESS_LOOP = 10;
62    private static final int NUM_PLAYBACk_IN_EACH_LOOP = 20;
63    private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds
64    private static final String MEDIA_MEMORY_OUTPUT =
65        "/sdcard/mediaMemOutput.txt";
66
67    private static int mStartMemory = 0;
68    private static int mEndMemory = 0;
69    private static int mStartPid = 0;
70    private static int mEndPid = 0;
71
72    private Looper mLooper = null;
73    private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback();
74    private final ConditionVariable mPreviewDone = new ConditionVariable();
75    private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000;  // Milliseconds.
76
77    //the tolerant memory leak
78    private static int ENCODER_LIMIT = 150;
79    private static int DECODER_LIMIT = 150;
80    private static int CAMERA_LIMIT = 80;
81
82    Camera mCamera;
83
84    public MediaPlayerPerformance() {
85        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
86    }
87
88    protected void setUp() throws Exception {
89        super.setUp();
90    }
91
92    public void createDB() {
93        mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
94        mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," +
95                "file TEXT," + "setdatatime LONG," + "preparetime LONG," +
96                "playtime LONG" + ");");
97        //clean the table before adding new data
98        mDB.execSQL("DELETE FROM perfdata");
99    }
100
101    public void audioPlaybackStartupTime(String[] testFile) {
102        long t1 = 0;
103        long t2 = 0;
104        long t3 = 0;
105        long t4 = 0;
106        long setDataSourceDuration = 0;
107        long prepareDuration = 0;
108        long startDuration = 0;
109        long totalSetDataTime = 0;
110        long totalPrepareTime = 0;
111        long totalStartDuration = 0;
112
113        int numberOfFiles = testFile.length;
114        Log.v(TAG, "File length " + numberOfFiles);
115        for (int k = 0; k < numberOfFiles; k++) {
116            MediaPlayer mp = new MediaPlayer();
117            try {
118                t1 = SystemClock.uptimeMillis();
119                FileInputStream fis = new FileInputStream(testFile[k]);
120                FileDescriptor fd = fis.getFD();
121                mp.setDataSource(fd);
122                fis.close();
123                t2 = SystemClock.uptimeMillis();
124                mp.prepare();
125                t3 = SystemClock.uptimeMillis();
126                mp.start();
127                t4 = SystemClock.uptimeMillis();
128            } catch (Exception e) {
129                Log.v(TAG, e.toString());
130            }
131            setDataSourceDuration = t2 - t1;
132            prepareDuration = t3 - t2;
133            startDuration = t4 - t3;
134            totalSetDataTime = totalSetDataTime + setDataSourceDuration;
135            totalPrepareTime = totalPrepareTime + prepareDuration;
136            totalStartDuration = totalStartDuration + startDuration;
137            mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," +
138                    " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' +
139                    setDataSourceDuration + ',' + prepareDuration +
140            		',' + startDuration + ");");
141            Log.v(TAG, "File name " + testFile[k]);
142            mp.stop();
143            mp.release();
144        }
145        Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles);
146        Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles);
147        Log.v(TAG, "start average " + totalStartDuration / numberOfFiles);
148
149    }
150
151    @Suppress
152    public void testStartUpTime() throws Exception {
153        createDB();
154        audioPlaybackStartupTime(MediaNames.MP3FILES);
155        audioPlaybackStartupTime(MediaNames.AACFILES);
156
157        //close the database after all transactions
158        if (mDB.isOpen()) {
159            mDB.close();
160        }
161    }
162
163    public void wmametadatautility(String[] testFile) {
164        long t1 = 0;
165        long t2 = 0;
166        long sum = 0;
167        long duration = 0;
168        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
169        String value;
170        for (int i = 0, n = testFile.length; i < n; ++i) {
171            try {
172                t1 = SystemClock.uptimeMillis();
173                retriever.setDataSource(testFile[i]);
174                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
175                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
176                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
177                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE);
178                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
179                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
180                value =
181                    retriever
182                    .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER);
183                t2 = SystemClock.uptimeMillis();
184                duration = t2 - t1;
185                Log.v(TAG, "Time taken = " + duration);
186                sum = sum + duration;
187            } catch (Exception e) {
188                Log.v(TAG, e.getMessage());
189            }
190
191        }
192        Log.v(TAG, "Average duration = " + sum / testFile.length);
193    }
194
195    private void initializeMessageLooper() {
196        final ConditionVariable startDone = new ConditionVariable();
197        new Thread() {
198            @Override
199            public void run() {
200                Looper.prepare();
201                Log.v(TAG, "start loopRun");
202                mLooper = Looper.myLooper();
203                mCamera = Camera.open();
204                startDone.open();
205                Looper.loop();
206                Log.v(TAG, "initializeMessageLooper: quit.");
207            }
208        }.start();
209
210        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
211            fail("initializeMessageLooper: start timeout");
212        }
213    }
214
215    private void terminateMessageLooper() throws Exception {
216        mLooper.quit();
217        // Looper.quit() is asynchronous. The looper may still has some
218        // preview callbacks in the queue after quit is called. The preview
219        // callback still uses the camera object (setHasPreviewCallback).
220        // After camera is released, RuntimeException will be thrown from
221        // the method. So we need to join the looper thread here.
222        mLooper.getThread().join();
223        mCamera.release();
224    }
225
226    private final class RawPreviewCallback implements PreviewCallback {
227        public void onPreviewFrame(byte[] rawData, Camera camera) {
228            mPreviewDone.open();
229        }
230    }
231
232    private void waitForPreviewDone() {
233        if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
234            Log.v(TAG, "waitForPreviewDone: timeout");
235        }
236        mPreviewDone.close();
237    }
238
239    public void stressCameraPreview() {
240        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
241            try {
242                initializeMessageLooper();
243                mCamera.setPreviewCallback(mRawPreviewCallback);
244                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
245                mCamera.setPreviewDisplay(mSurfaceHolder);
246                mCamera.startPreview();
247                waitForPreviewDone();
248                Thread.sleep(1000);
249                mCamera.stopPreview();
250                terminateMessageLooper();
251            } catch (Exception e) {
252                Log.v(TAG, e.toString());
253            }
254        }
255    }
256
257    // Note: This test is to assume the mediaserver's pid is 34
258    public void mediaStressPlayback(String testFilePath) {
259        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
260            MediaPlayer mp = new MediaPlayer();
261            try {
262                mp.setDataSource(testFilePath);
263                mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
264                mp.prepare();
265                mp.start();
266                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
267                mp.release();
268            } catch (Exception e) {
269                mp.release();
270                Log.v(TAG, e.toString());
271            }
272        }
273    }
274
275    // Note: This test is to assume the mediaserver's pid is 34
276    private void stressVideoRecord(int frameRate, int width, int height, int videoFormat,
277            int outFormat, String outFile, boolean videoOnly) {
278        // Video recording
279        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
280            MediaRecorder mRecorder = new MediaRecorder();
281            try {
282                if (!videoOnly) {
283                    Log.v(TAG, "setAudioSource");
284                    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
285                }
286                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
287                mRecorder.setOutputFormat(outFormat);
288                Log.v(TAG, "output format " + outFormat);
289                mRecorder.setOutputFile(outFile);
290                mRecorder.setVideoFrameRate(frameRate);
291                mRecorder.setVideoSize(width, height);
292                Log.v(TAG, "setEncoder");
293                mRecorder.setVideoEncoder(videoFormat);
294                if (!videoOnly) {
295                    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
296                }
297                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
298                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
299                mRecorder.prepare();
300                mRecorder.start();
301                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
302                mRecorder.stop();
303                mRecorder.release();
304            } catch (Exception e) {
305                Log.v("record video failed ", e.toString());
306                mRecorder.release();
307            }
308        }
309    }
310
311    public void stressAudioRecord(String filePath) {
312        // This test is only for the short media file
313        for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
314            MediaRecorder mRecorder = new MediaRecorder();
315            try {
316                mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
317                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
318                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
319                mRecorder.setOutputFile(filePath);
320                mRecorder.prepare();
321                mRecorder.start();
322                Thread.sleep(MEDIA_STRESS_WAIT_TIME);
323                mRecorder.stop();
324                mRecorder.release();
325            } catch (Exception e) {
326                Log.v(TAG, e.toString());
327                mRecorder.release();
328            }
329        }
330    }
331
332    //Write the ps output to the file
333    public void getMemoryWriteToLog(Writer output, int writeCount) {
334        String memusage = null;
335        try {
336            if (writeCount == 0) {
337                mStartMemory = getMediaserverVsize();
338                output.write("Start memory : " + mStartMemory + "\n");
339            }
340            memusage = captureMediaserverInfo();
341            output.write(memusage);
342            if (writeCount == NUM_STRESS_LOOP - 1) {
343                mEndMemory = getMediaserverVsize();
344                output.write("End Memory :" + mEndMemory + "\n");
345            }
346        } catch (Exception e) {
347            e.toString();
348        }
349    }
350
351    public String captureMediaserverInfo() {
352        String cm = "ps mediaserver";
353        String memoryUsage = null;
354
355        int ch;
356        try {
357            Process p = Runtime.getRuntime().exec(cm);
358            InputStream in = p.getInputStream();
359            StringBuffer sb = new StringBuffer(512);
360            while ((ch = in.read()) != -1) {
361                sb.append((char) ch);
362            }
363            memoryUsage = sb.toString();
364        } catch (IOException e) {
365            Log.v(TAG, e.toString());
366        }
367        String[] poList = memoryUsage.split("\r|\n|\r\n");
368        String memusage = poList[1].concat("\n");
369        return memusage;
370    }
371
372    public int getMediaserverPid(){
373        String memoryUsage = null;
374        int pidvalue = 0;
375        memoryUsage = captureMediaserverInfo();
376        String[] poList2 = memoryUsage.split("\t|\\s+");
377        String pid = poList2[1];
378        pidvalue = Integer.parseInt(pid);
379        Log.v(TAG, "PID = " + pidvalue);
380        return pidvalue;
381    }
382
383    public int getMediaserverVsize(){
384        String memoryUsage = captureMediaserverInfo();
385        String[] poList2 = memoryUsage.split("\t|\\s+");
386        String vsize = poList2[3];
387        int vsizevalue = Integer.parseInt(vsize);
388        Log.v(TAG, "VSIZE = " + vsizevalue);
389        return vsizevalue;
390    }
391
392    public boolean validateMemoryResult(int startPid, int startMemory, Writer output, int limit)
393            throws Exception {
394        // Wait for 10 seconds to make sure the memory settle.
395        Thread.sleep(10000);
396        mEndPid = getMediaserverPid();
397        int memDiff = mEndMemory - startMemory;
398        if (memDiff < 0) {
399            memDiff = 0;
400        }
401        output.write("The total diff = " + memDiff);
402        output.write("\n\n");
403        // mediaserver crash
404        if (startPid != mEndPid) {
405            output.write("mediaserver died. Test failed\n");
406            return false;
407        }
408        // memory leak greter than the tolerant
409        if (memDiff > limit) return false;
410        return true;
411    }
412
413    @Suppress
414    public void testWmaParseTime() throws Exception {
415        // createDB();
416        wmametadatautility(MediaNames.WMASUPPORTED);
417    }
418
419
420    // Test case 1: Capture the memory usage after every 20 h263 playback
421    @LargeTest
422    public void testH263VideoPlaybackMemoryUsage() throws Exception {
423        boolean memoryResult = false;
424        mStartPid = getMediaserverPid();
425
426        File h263MemoryOut = new File(MEDIA_MEMORY_OUTPUT);
427        Writer output = new BufferedWriter(new FileWriter(h263MemoryOut, true));
428        output.write("H263 Video Playback Only\n");
429        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
430            mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
431            getMemoryWriteToLog(output, i);
432        }
433        output.write("\n");
434        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
435        output.close();
436        assertTrue("H263 playback memory test", memoryResult);
437    }
438
439    // Test case 2: Capture the memory usage after every 20 h264 playback
440    @LargeTest
441    public void testH264VideoPlaybackMemoryUsage() throws Exception {
442        boolean memoryResult = false;
443        mStartPid = getMediaserverPid();
444
445        File h264MemoryOut = new File(MEDIA_MEMORY_OUTPUT);
446        Writer output = new BufferedWriter(new FileWriter(h264MemoryOut, true));
447        output.write("H264 Video Playback only\n");
448        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
449            mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
450            getMemoryWriteToLog(output, i);
451        }
452        output.write("\n");
453        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
454        output.close();
455        assertTrue("H264 playback memory test", memoryResult);
456    }
457
458    // Test case 3: Capture the memory usage after each 20 WMV playback
459    @LargeTest
460    public void testWMVVideoPlaybackMemoryUsage() throws Exception {
461        boolean memoryResult = false;
462        if (MediaProfileReader.getWMVEnable()){
463            mStartPid = getMediaserverPid();
464            File wmvMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
465            Writer output = new BufferedWriter(new FileWriter(wmvMemoryOut, true));
466            output.write("WMV video playback only\n");
467            for (int i = 0; i < NUM_STRESS_LOOP; i++) {
468                mediaStressPlayback(MediaNames.VIDEO_WMV);
469                getMemoryWriteToLog(output, i);
470            }
471            output.write("\n");
472            memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
473            output.close();
474            assertTrue("wmv playback memory test", memoryResult);
475        }
476    }
477
478    // Test case 4: Capture the memory usage after every 20 video only recorded
479    @LargeTest
480    public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
481        boolean memoryResult = false;
482        mStartPid = getMediaserverPid();
483
484        File videoH263RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
485        Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true));
486        output.write("H263 video record only\n");
487        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
488            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
489                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true);
490            getMemoryWriteToLog(output, i);
491        }
492        output.write("\n");
493        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
494        output.close();
495        assertTrue("H263 record only memory test", memoryResult);
496    }
497
498    // Test case 5: Capture the memory usage after every 20 video only recorded
499    @LargeTest
500    public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
501        boolean memoryResult = false;
502        mStartPid = getMediaserverPid();
503
504        File videoMp4RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
505        Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true));
506        output.write("MPEG4 video record only\n");
507        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
508            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP,
509                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true);
510            getMemoryWriteToLog(output, i);
511        }
512        output.write("\n");
513        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
514        output.close();
515        assertTrue("mpeg4 record only memory test", memoryResult);
516    }
517
518    // Test case 6: Capture the memory usage after every 20 video and audio
519    // recorded
520    @LargeTest
521    public void testRecordVidedAudioMemoryUsage() throws Exception {
522        boolean memoryResult = false;
523        mStartPid = getMediaserverPid();
524
525        File videoRecordAudioMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
526        Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true));
527        output.write("Audio and h263 video record\n");
528        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
529            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
530                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false);
531            getMemoryWriteToLog(output, i);
532        }
533        output.write("\n");
534        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
535        output.close();
536        assertTrue("H263 audio video record memory test", memoryResult);
537    }
538
539    // Test case 7: Capture the memory usage after every 20 audio only recorded
540    @LargeTest
541    public void testRecordAudioOnlyMemoryUsage() throws Exception {
542        boolean memoryResult = false;
543        mStartPid = getMediaserverPid();
544
545        File audioOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
546        Writer output = new BufferedWriter(new FileWriter(audioOnlyMemoryOut, true));
547        output.write("Audio record only\n");
548        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
549            stressAudioRecord(MediaNames.RECORDER_OUTPUT);
550            getMemoryWriteToLog(output, i);
551        }
552        output.write("\n");
553        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
554        output.close();
555        assertTrue("audio record only memory test", memoryResult);
556    }
557
558    // Test case 8: Capture the memory usage after every 20 camera preview
559    @LargeTest
560    public void testCameraPreviewMemoryUsage() throws Exception {
561        boolean memoryResult = false;
562        mStartPid = getMediaserverPid();
563
564        File cameraPreviewMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
565        Writer output = new BufferedWriter(new FileWriter(cameraPreviewMemoryOut, true));
566        output.write("Camera Preview Only\n");
567        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
568            stressCameraPreview();
569            getMemoryWriteToLog(output, i);
570        }
571        output.write("\n");
572        memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, CAMERA_LIMIT);
573        output.close();
574        assertTrue("camera preview memory test", memoryResult);
575    }
576}
577