MediaRecorderTest.java revision 699780f54b7ddd7b00f891218140df663b3ab84e
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mediaframeworktest.functional.mediarecorder;
18
19import com.android.mediaframeworktest.MediaFrameworkTest;
20import com.android.mediaframeworktest.MediaNames;
21
22import java.io.*;
23
24import android.content.Context;
25import android.graphics.Canvas;
26import android.graphics.Color;
27import android.graphics.Paint;
28import android.graphics.Typeface;
29import android.hardware.Camera;
30import android.media.MediaMetadataRetriever;
31import android.media.MediaPlayer;
32import android.media.MediaRecorder;
33import android.media.EncoderCapabilities;
34import android.media.EncoderCapabilities.VideoEncoderCap;
35import android.media.EncoderCapabilities.AudioEncoderCap;
36import android.test.ActivityInstrumentationTestCase2;
37import android.util.Log;
38import android.view.Surface;
39import android.view.SurfaceHolder;
40import android.view.SurfaceView;
41import com.android.mediaframeworktest.MediaProfileReader;
42import com.android.mediaframeworktest.MediaFrameworkTestRunner;
43
44import android.test.suitebuilder.annotation.LargeTest;
45import android.test.suitebuilder.annotation.Suppress;
46import java.util.List;
47
48
49/**
50 * Junit / Instrumentation test case for the media recorder api
51 */
52public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
53    private String TAG = "MediaRecorderTest";
54    private int mOutputDuration =0;
55    private int mOutputVideoWidth = 0;
56    private int mOutputVideoHeight= 0 ;
57
58    private SurfaceHolder mSurfaceHolder = null;
59    private MediaRecorder mRecorder;
60
61    private int MIN_VIDEO_FPS = 5;
62    private int HIGH_SPEED_FPS = 120;
63
64    private static final int CAMERA_ID = 0;
65
66    Context mContext;
67    Camera mCamera;
68
69    public MediaRecorderTest() {
70        super(MediaFrameworkTest.class);
71
72    }
73
74    protected void setUp() throws Exception {
75        getActivity();
76        mRecorder = new MediaRecorder();
77        super.setUp();
78    }
79
80    private void recordVideo(int frameRate, int width, int height,
81            int videoFormat, int outFormat, String outFile, boolean videoOnly) {
82        Log.v(TAG,"startPreviewAndPrepareRecording");
83        try {
84            if (!videoOnly) {
85                Log.v(TAG, "setAudioSource");
86                mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
87            }
88            mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
89            mRecorder.setOutputFormat(outFormat);
90            Log.v(TAG, "output format " + outFormat);
91            mRecorder.setOutputFile(outFile);
92            mRecorder.setVideoFrameRate(frameRate);
93            mRecorder.setVideoSize(width, height);
94            Log.v(TAG, "setEncoder");
95            mRecorder.setVideoEncoder(videoFormat);
96            if (!videoOnly) {
97                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
98            }
99            mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
100            Log.v(TAG, "setPreview");
101            mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
102            Log.v(TAG, "prepare");
103            mRecorder.prepare();
104            Log.v(TAG, "start");
105            mRecorder.start();
106            Thread.sleep(MediaNames.RECORDED_TIME);
107            Log.v(TAG, "stop");
108            mRecorder.stop();
109            mRecorder.release();
110        } catch (Exception e) {
111            Log.v("record video failed ", e.toString());
112            mRecorder.release();
113        }
114    }
115
116    private boolean validateGetSurface(boolean useSurface) {
117        Log.v(TAG,"validateGetSurface, useSurface=" + useSurface);
118        MediaRecorder recorder = new MediaRecorder();
119        Surface surface;
120        boolean success = true;
121        try {
122            /* initialization */
123            if (!useSurface) {
124                mCamera = Camera.open(CAMERA_ID);
125                Camera.Parameters parameters = mCamera.getParameters();
126                parameters.setPreviewSize(352, 288);
127                parameters.set("orientation", "portrait");
128                mCamera.setParameters(parameters);
129                mCamera.unlock();
130                recorder.setCamera(mCamera);
131                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
132                recorder.setPreviewDisplay(mSurfaceHolder.getSurface());
133            }
134
135            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
136            int videoSource = useSurface ?
137                    MediaRecorder.VideoSource.SURFACE :
138                    MediaRecorder.VideoSource.CAMERA;
139            recorder.setVideoSource(videoSource);
140            recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
141            recorder.setOutputFile(MediaNames.RECORDED_SURFACE_3GP);
142            recorder.setVideoFrameRate(30);
143            recorder.setVideoSize(352, 288);
144            recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
145            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
146
147            /* Test: getSurface() before prepare()
148             * should throw IllegalStateException
149             */
150            try {
151                surface = recorder.getSurface();
152                throw new Exception("getSurface failed to throw IllegalStateException");
153            } catch (IllegalStateException e) {
154                // OK
155            }
156
157            recorder.prepare();
158
159            /* Test: getSurface() after prepare()
160             * should succeed for surface source
161             * should fail for camera source
162             */
163            try {
164                surface = recorder.getSurface();
165                if (!useSurface) {
166                    throw new Exception("getSurface failed to throw IllegalStateException");
167                }
168            } catch (IllegalStateException e) {
169                if (useSurface) {
170                    throw new Exception("getSurface failed to throw IllegalStateException");
171                }
172            }
173
174            recorder.start();
175
176            /* Test: getSurface() after start()
177             * should succeed for surface source
178             * should fail for camera source
179             */
180            try {
181                surface = recorder.getSurface();
182                if (!useSurface) {
183                    throw new Exception("getSurface failed to throw IllegalStateException");
184                }
185            } catch (IllegalStateException e) {
186                if (useSurface) {
187                    throw new Exception("getSurface failed to throw IllegalStateException");
188                }
189            }
190
191            try {
192                recorder.stop();
193            } catch (Exception e) {
194                // stop() could fail if the recording is empty, as we didn't render anything.
195                // ignore any failure in stop, we just want it stopped.
196            }
197
198            /* Test: getSurface() after stop()
199             * should throw IllegalStateException
200             */
201            try {
202                surface = recorder.getSurface();
203                throw new Exception("getSurface failed to throw IllegalStateException");
204            } catch (IllegalStateException e) {
205                // OK
206            }
207        } catch (Exception e) {
208            // fail
209            success = false;
210        }
211
212        try {
213            if (mCamera != null) {
214                mCamera.lock();
215                mCamera.release();
216                mCamera = null;
217            }
218            recorder.release();
219        } catch (Exception e) {
220            success = false;
221        }
222
223        return success;
224    }
225
226    private boolean recordVideoFromSurface(
227            int frameRate, int captureRate, int width, int height,
228            int videoFormat, int outFormat, String outFile, boolean videoOnly) {
229        Log.v(TAG,"recordVideoFromSurface");
230        MediaRecorder recorder = new MediaRecorder();
231        int sleepTime = 33; // normal capture at 33ms / frame
232        try {
233            if (!videoOnly) {
234                recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
235            }
236            recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
237            recorder.setOutputFormat(outFormat);
238            recorder.setOutputFile(outFile);
239            recorder.setVideoFrameRate(frameRate);
240            if (captureRate > 0) {
241                recorder.setCaptureRate(captureRate);
242                sleepTime = 1000 / captureRate;
243            }
244            recorder.setVideoSize(width, height);
245            recorder.setVideoEncoder(videoFormat);
246            if (!videoOnly) {
247                recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
248            }
249            recorder.prepare();
250            Surface surface = recorder.getSurface();
251
252            Paint paint = new Paint();
253            paint.setTextSize(16);
254            paint.setColor(Color.RED);
255            int i;
256
257            /* Test: draw 10 frames at 30fps before start
258             * these should be dropped and not causing malformed stream.
259             */
260            for(i = 0; i < 10; i++) {
261                Canvas canvas = surface.lockCanvas(null);
262                int background = (i * 255 / 99);
263                canvas.drawARGB(255, background, background, background);
264                String text = "Frame #" + i;
265                canvas.drawText(text, 100, 100, paint);
266                surface.unlockCanvasAndPost(canvas);
267                Thread.sleep(sleepTime);
268            }
269
270            Log.v(TAG, "start");
271            recorder.start();
272
273            /* Test: draw another 90 frames at 30fps after start */
274            for(i = 10; i < 100; i++) {
275                Canvas canvas = surface.lockCanvas(null);
276                int background = (i * 255 / 99);
277                canvas.drawARGB(255, background, background, background);
278                String text = "Frame #" + i;
279                canvas.drawText(text, 100, 100, paint);
280                surface.unlockCanvasAndPost(canvas);
281                Thread.sleep(sleepTime);
282            }
283
284            Log.v(TAG, "stop");
285            recorder.stop();
286            recorder.release();
287        } catch (Exception e) {
288            Log.v("record video failed ", e.toString());
289            recorder.release();
290            return false;
291        }
292        return true;
293    }
294
295    private boolean recordVideoWithPara(VideoEncoderCap videoCap, AudioEncoderCap audioCap, boolean highQuality){
296        boolean recordSuccess = false;
297        int videoEncoder = videoCap.mCodec;
298        int audioEncoder = audioCap.mCodec;
299        int videoWidth = highQuality? videoCap.mMaxFrameWidth: videoCap.mMinFrameWidth;
300        int videoHeight = highQuality? videoCap.mMaxFrameHeight: videoCap.mMinFrameHeight;
301        int videoFps = highQuality? videoCap.mMaxFrameRate: videoCap.mMinFrameRate;
302        int videoBitrate = highQuality? videoCap.mMaxBitRate: videoCap.mMinBitRate;
303        int audioBitrate = highQuality? audioCap.mMaxBitRate: audioCap.mMinBitRate;
304        int audioChannels = highQuality? audioCap.mMaxChannels: audioCap.mMinChannels ;
305        int audioSamplingRate = highQuality? audioCap.mMaxSampleRate: audioCap.mMinSampleRate;
306
307        //Overide the fps if the min_camera_fps is set
308        if (MediaFrameworkTestRunner.mMinCameraFps != 0 &&
309            MediaFrameworkTestRunner.mMinCameraFps > videoFps){
310            videoFps = MediaFrameworkTestRunner.mMinCameraFps;
311        }
312
313        if (videoFps < MIN_VIDEO_FPS) {
314            videoFps = MIN_VIDEO_FPS;
315        }
316
317        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
318        String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp");
319        try {
320            Log.v(TAG, "video encoder : " + videoEncoder);
321            Log.v(TAG, "audio encoder : " + audioEncoder);
322            Log.v(TAG, "quality : " + (highQuality?"high": "low"));
323            Log.v(TAG, "encoder : " + MediaProfileReader.getVideoCodecName(videoEncoder));
324            Log.v(TAG, "audio : " + MediaProfileReader.getAudioCodecName(audioEncoder));
325            Log.v(TAG, "videoWidth : " + videoWidth);
326            Log.v(TAG, "videoHeight : " + videoHeight);
327            Log.v(TAG, "videoFPS : " + videoFps);
328            Log.v(TAG, "videobitrate : " + videoBitrate);
329            Log.v(TAG, "audioBitrate : " + audioBitrate);
330            Log.v(TAG, "audioChannel : " + audioChannels);
331            Log.v(TAG, "AudioSampleRate : " + audioSamplingRate);
332
333            MediaRecorder mMediaRecorder = new MediaRecorder();
334            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
335            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
336            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
337            mMediaRecorder.setOutputFile(filename);
338            mMediaRecorder.setVideoFrameRate(videoFps);
339            mMediaRecorder.setVideoSize(videoWidth, videoHeight);
340            mMediaRecorder.setVideoEncodingBitRate(videoBitrate);
341            mMediaRecorder.setAudioEncodingBitRate(audioBitrate);
342            mMediaRecorder.setAudioChannels(audioChannels);
343            mMediaRecorder.setAudioSamplingRate(audioSamplingRate);
344            mMediaRecorder.setVideoEncoder(videoEncoder);
345            mMediaRecorder.setAudioEncoder(audioEncoder);
346            mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
347            mMediaRecorder.prepare();
348            mMediaRecorder.start();
349            Thread.sleep(MediaNames.RECORDED_TIME);
350            mMediaRecorder.stop();
351            mMediaRecorder.release();
352            recordSuccess = validateVideo(filename, videoWidth, videoHeight);
353        } catch (Exception e) {
354            Log.v(TAG, e.toString());
355            return false;
356        }
357        return recordSuccess;
358    }
359
360    private boolean invalidRecordSetting(int frameRate, int width, int height,
361            int videoFormat, int outFormat, String outFile, boolean videoOnly) {
362        try {
363            if (!videoOnly) {
364                Log.v(TAG, "setAudioSource");
365                mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
366            }
367            mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
368            mRecorder.setOutputFormat(outFormat);
369            Log.v(TAG, "output format " + outFormat);
370            mRecorder.setOutputFile(outFile);
371            mRecorder.setVideoFrameRate(frameRate);
372            mRecorder.setVideoSize(width, height);
373            Log.v(TAG, "setEncoder");
374            mRecorder.setVideoEncoder(videoFormat);
375            if (!videoOnly) {
376                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
377            }
378            mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
379            Log.v(TAG, "setPreview");
380            mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
381            Log.v(TAG, "prepare");
382            mRecorder.prepare();
383            Log.v(TAG, "start");
384            mRecorder.start();
385            Thread.sleep(MediaNames.RECORDED_TIME);
386            Log.v(TAG, "stop");
387            mRecorder.stop();
388            mRecorder.release();
389        } catch (Exception e) {
390            Log.v("record video failed ", e.toString());
391            mRecorder.release();
392            Log.v(TAG, "reset and release");
393            return true;
394        }
395        return false;
396    }
397
398    private void getOutputVideoProperty(String outputFilePath) {
399        MediaPlayer mediaPlayer = new MediaPlayer();
400        try {
401            mediaPlayer.setDataSource(outputFilePath);
402            Log.v(TAG, "file Path = " + outputFilePath);
403            mediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
404            Log.v(TAG, "before player prepare");
405            mediaPlayer.prepare();
406            Log.v(TAG, "before getduration");
407            mOutputDuration = mediaPlayer.getDuration();
408            Log.v(TAG, "get video dimension");
409            Thread.sleep(1000);
410            mOutputVideoHeight = mediaPlayer.getVideoHeight();
411            mOutputVideoWidth = mediaPlayer.getVideoWidth();
412            mediaPlayer.release();
413        } catch (Exception e) {
414            Log.v(TAG, e.toString());
415            mediaPlayer.release();
416        }
417    }
418
419    private boolean validateVideo(String filePath, int width, int height) {
420        boolean validVideo = false;
421        getOutputVideoProperty(filePath);
422        if (mOutputVideoWidth == width && mOutputVideoHeight == height &&
423                mOutputDuration > MediaNames.VALID_VIDEO_DURATION ) {
424            validVideo = true;
425        }
426        Log.v(TAG, "width = " + mOutputVideoWidth + " height = " + mOutputVideoHeight + " Duration = " + mOutputDuration);
427        return validVideo;
428    }
429
430    private boolean validateMetadata(String filePath, int captureRate) {
431        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
432
433        retriever.setDataSource(filePath);
434
435        // verify capture rate meta key is present and correct
436        String captureFps = retriever.extractMetadata(
437                MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
438
439        if (captureFps == null) {
440            Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is missing");
441            return false;
442        }
443
444        if (Math.abs(Float.parseFloat(captureFps) - captureRate) > 0.001) {
445            Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is incorrect: "
446                    + captureFps + "vs. " + captureRate);
447            return false;
448        }
449
450        // verify other meta keys here if necessary
451        return true;
452    }
453    @LargeTest
454    /*
455     * This test case set the camera in portrait mode.
456     * Verification: validate the video dimension and the duration.
457     */
458    public void testPortraitH263() throws Exception {
459        boolean videoRecordedResult = false;
460        try {
461            mCamera = Camera.open(CAMERA_ID);
462            Camera.Parameters parameters = mCamera.getParameters();
463            parameters.setPreviewSize(352, 288);
464            parameters.set("orientation", "portrait");
465            mCamera.setParameters(parameters);
466            mCamera.unlock();
467            mRecorder.setCamera(mCamera);
468            Thread.sleep(1000);
469            int codec = MediaRecorder.VideoEncoder.H263;
470            int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
471            recordVideo(frameRate, 352, 288, codec,
472                    MediaRecorder.OutputFormat.THREE_GPP,
473                    MediaNames.RECORDED_PORTRAIT_H263, true);
474            mCamera.lock();
475            mCamera.release();
476            videoRecordedResult =
477                validateVideo(MediaNames.RECORDED_PORTRAIT_H263, 352, 288);
478        } catch (Exception e) {
479            Log.v(TAG, e.toString());
480        }
481        assertTrue("PortraitH263", videoRecordedResult);
482    }
483
484    @LargeTest
485    public void testInvalidVideoPath() throws Exception {
486        boolean isTestInvalidVideoPathSuccessful = false;
487        isTestInvalidVideoPathSuccessful = invalidRecordSetting(15, 176, 144, MediaRecorder.VideoEncoder.H263,
488               MediaRecorder.OutputFormat.THREE_GPP, MediaNames.INVALD_VIDEO_PATH, false);
489        assertTrue("Invalid outputFile Path", isTestInvalidVideoPathSuccessful);
490    }
491
492    @LargeTest
493    //test cases for the new codec
494    public void testDeviceSpecificCodec() throws Exception {
495        int noOfFailure = 0;
496        boolean recordSuccess = false;
497        String deviceType = MediaProfileReader.getDeviceType();
498        Log.v(TAG, "deviceType = " + deviceType);
499        List<VideoEncoderCap> videoEncoders = MediaProfileReader.getVideoEncoders();
500        List<AudioEncoderCap> audioEncoders = MediaProfileReader.getAudioEncoders();
501        for (int k = 0; k < 2; k++) {
502            for (VideoEncoderCap videoEncoder: videoEncoders) {
503                for (AudioEncoderCap audioEncoder: audioEncoders) {
504                    if (k == 0) {
505                        recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, true);
506                    } else {
507                        recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, false);
508                    }
509                    if (!recordSuccess) {
510                        Log.v(TAG, "testDeviceSpecificCodec failed");
511                        Log.v(TAG, "Encoder = " + videoEncoder.mCodec + "Audio Encoder = " + audioEncoder.mCodec);
512                        noOfFailure++;
513                    }
514                }
515            }
516        }
517        if (noOfFailure != 0) {
518            assertTrue("testDeviceSpecificCodec", false);
519        }
520    }
521
522    // Test MediaRecorder.getSurface() api with surface or camera source
523    public void testGetSurfaceApi() {
524        boolean success = false;
525        int noOfFailure = 0;
526        try {
527            for (int k = 0; k < 2; k++) {
528                success = validateGetSurface(
529                        k == 0 ? true : false /* useSurface */);
530                if (!success) {
531                    noOfFailure++;
532                }
533            }
534        } catch (Exception e) {
535            Log.v(TAG, e.toString());
536        }
537        assertTrue("testGetSurfaceApi", noOfFailure == 0);
538    }
539
540    // Test recording from surface source with/without audio
541    public void testSurfaceRecording() {
542        boolean success = false;
543        int noOfFailure = 0;
544        try {
545            int codec = MediaRecorder.VideoEncoder.H264;
546            int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
547            for (int k = 0; k < 2; k++) {
548                String filename = "/sdcard/surface_" +
549                            (k==0?"video_only":"with_audio") + ".3gp";
550
551                success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
552                        MediaRecorder.OutputFormat.THREE_GPP, filename,
553                        k == 0 ? true : false /* videoOnly */);
554                if (success) {
555                    success = validateVideo(filename, 352, 288);
556                }
557                if (!success) {
558                    noOfFailure++;
559                }
560            }
561        } catch (Exception e) {
562            Log.v(TAG, e.toString());
563        }
564        assertTrue("testSurfaceRecording", noOfFailure == 0);
565    }
566
567    // Test recording from surface source with/without audio
568    public void testSurfaceRecordingTimeLapse() {
569        boolean success = false;
570        int noOfFailure = 0;
571        try {
572            int codec = MediaRecorder.VideoEncoder.H264;
573            int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
574            for (int k = 0; k < 2; k++) {
575                // k==0: time lapse test, set capture rate to MIN_VIDEO_FPS
576                // k==1: slow motion test, set capture rate to HIGH_SPEED_FPS
577                String filename = "/sdcard/surface_" +
578                            (k==0 ? "time_lapse" : "slow_motion") + ".3gp";
579
580                // always set videoOnly=false, MediaRecorder should disable
581                // audio automatically with time lapse/slow motion
582                int captureRate = k==0 ? MIN_VIDEO_FPS : HIGH_SPEED_FPS;
583                success = recordVideoFromSurface(
584                        frameRate, captureRate, 352, 288, codec,
585                        MediaRecorder.OutputFormat.THREE_GPP,
586                        filename, false /* videoOnly */);
587                if (success) {
588                    success = validateVideo(filename, 352, 288);
589                    if (success) {
590                        success = validateMetadata(filename, captureRate);
591                    }
592                }
593                if (!success) {
594                    noOfFailure++;
595                }
596            }
597        } catch (Exception e) {
598            Log.v(TAG, e.toString());
599            noOfFailure++;
600        }
601        assertTrue("testSurfaceRecordingTimeLapse", noOfFailure == 0);
602    }
603
604}
605