/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mediaframeworktest.functional.mediarecorder; import com.android.mediaframeworktest.MediaFrameworkTest; import com.android.mediaframeworktest.MediaNames; import java.io.*; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.media.EncoderCapabilities; import android.media.EncoderCapabilities.VideoEncoderCap; import android.media.EncoderCapabilities.AudioEncoderCap; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import com.android.mediaframeworktest.MediaProfileReader; import com.android.mediaframeworktest.MediaFrameworkTestRunner; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.Suppress; import java.util.List; /** * Junit / Instrumentation test case for the media recorder api */ public class MediaRecorderTest extends ActivityInstrumentationTestCase2 { private String TAG = "MediaRecorderTest"; private int mOutputDuration =0; private int mOutputVideoWidth = 0; private int mOutputVideoHeight= 0 ; private SurfaceHolder mSurfaceHolder = null; private MediaRecorder mRecorder; private int MIN_VIDEO_FPS = 5; private int HIGH_SPEED_FPS = 120; private static final int CAMERA_ID = 0; Context mContext; Camera mCamera; public MediaRecorderTest() { super(MediaFrameworkTest.class); } protected void setUp() throws Exception { getActivity(); mRecorder = new MediaRecorder(); super.setUp(); } private void recordVideo(int frameRate, int width, int height, int videoFormat, int outFormat, String outFile, boolean videoOnly) { Log.v(TAG,"startPreviewAndPrepareRecording"); try { if (!videoOnly) { Log.v(TAG, "setAudioSource"); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); } mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(outFormat); Log.v(TAG, "output format " + outFormat); mRecorder.setOutputFile(outFile); mRecorder.setVideoFrameRate(frameRate); mRecorder.setVideoSize(width, height); Log.v(TAG, "setEncoder"); mRecorder.setVideoEncoder(videoFormat); if (!videoOnly) { mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); } mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); Log.v(TAG, "setPreview"); mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); Log.v(TAG, "prepare"); mRecorder.prepare(); Log.v(TAG, "start"); mRecorder.start(); Thread.sleep(MediaNames.RECORDED_TIME); Log.v(TAG, "stop"); mRecorder.stop(); mRecorder.release(); } catch (Exception e) { Log.v("record video failed ", e.toString()); mRecorder.release(); } } private boolean validateGetSurface(boolean useSurface) { Log.v(TAG,"validateGetSurface, useSurface=" + useSurface); MediaRecorder recorder = new MediaRecorder(); Surface surface; boolean success = true; try { /* initialization */ if (!useSurface) { mCamera = Camera.open(CAMERA_ID); Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(352, 288); parameters.set("orientation", "portrait"); mCamera.setParameters(parameters); mCamera.unlock(); recorder.setCamera(mCamera); mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); recorder.setPreviewDisplay(mSurfaceHolder.getSurface()); } recorder.setAudioSource(MediaRecorder.AudioSource.MIC); int videoSource = useSurface ? MediaRecorder.VideoSource.SURFACE : MediaRecorder.VideoSource.CAMERA; recorder.setVideoSource(videoSource); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(MediaNames.RECORDED_SURFACE_3GP); recorder.setVideoFrameRate(30); recorder.setVideoSize(352, 288); recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); /* Test: getSurface() before prepare() * should throw IllegalStateException */ try { surface = recorder.getSurface(); throw new Exception("getSurface failed to throw IllegalStateException"); } catch (IllegalStateException e) { // OK } recorder.prepare(); /* Test: getSurface() after prepare() * should succeed for surface source * should fail for camera source */ try { surface = recorder.getSurface(); if (!useSurface) { throw new Exception("getSurface failed to throw IllegalStateException"); } } catch (IllegalStateException e) { if (useSurface) { throw new Exception("getSurface failed to throw IllegalStateException"); } } recorder.start(); /* Test: getSurface() after start() * should succeed for surface source * should fail for camera source */ try { surface = recorder.getSurface(); if (!useSurface) { throw new Exception("getSurface failed to throw IllegalStateException"); } } catch (IllegalStateException e) { if (useSurface) { throw new Exception("getSurface failed to throw IllegalStateException"); } } try { recorder.stop(); } catch (Exception e) { // stop() could fail if the recording is empty, as we didn't render anything. // ignore any failure in stop, we just want it stopped. } /* Test: getSurface() after stop() * should throw IllegalStateException */ try { surface = recorder.getSurface(); throw new Exception("getSurface failed to throw IllegalStateException"); } catch (IllegalStateException e) { // OK } } catch (Exception e) { // fail success = false; } try { if (mCamera != null) { mCamera.lock(); mCamera.release(); mCamera = null; } recorder.release(); } catch (Exception e) { success = false; } return success; } private boolean recordVideoFromSurface( int frameRate, int captureRate, int width, int height, int videoFormat, int outFormat, String outFile, boolean videoOnly) { Log.v(TAG,"recordVideoFromSurface"); MediaRecorder recorder = new MediaRecorder(); int sleepTime = 33; // normal capture at 33ms / frame try { if (!videoOnly) { recorder.setAudioSource(MediaRecorder.AudioSource.MIC); } recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); recorder.setOutputFormat(outFormat); recorder.setOutputFile(outFile); recorder.setVideoFrameRate(frameRate); if (captureRate > 0) { recorder.setCaptureRate(captureRate); sleepTime = 1000 / captureRate; } recorder.setVideoSize(width, height); recorder.setVideoEncoder(videoFormat); if (!videoOnly) { recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); } recorder.prepare(); Surface surface = recorder.getSurface(); Paint paint = new Paint(); paint.setTextSize(16); paint.setColor(Color.RED); int i; /* Test: draw 10 frames at 30fps before start * these should be dropped and not causing malformed stream. */ for(i = 0; i < 10; i++) { Canvas canvas = surface.lockCanvas(null); int background = (i * 255 / 99); canvas.drawARGB(255, background, background, background); String text = "Frame #" + i; canvas.drawText(text, 100, 100, paint); surface.unlockCanvasAndPost(canvas); Thread.sleep(sleepTime); } Log.v(TAG, "start"); recorder.start(); /* Test: draw another 90 frames at 30fps after start */ for(i = 10; i < 100; i++) { Canvas canvas = surface.lockCanvas(null); int background = (i * 255 / 99); canvas.drawARGB(255, background, background, background); String text = "Frame #" + i; canvas.drawText(text, 100, 100, paint); surface.unlockCanvasAndPost(canvas); Thread.sleep(sleepTime); } Log.v(TAG, "stop"); recorder.stop(); recorder.release(); } catch (Exception e) { Log.v("record video failed ", e.toString()); recorder.release(); return false; } return true; } private boolean recordVideoWithPara(VideoEncoderCap videoCap, AudioEncoderCap audioCap, boolean highQuality){ boolean recordSuccess = false; int videoEncoder = videoCap.mCodec; int audioEncoder = audioCap.mCodec; int videoWidth = highQuality? videoCap.mMaxFrameWidth: videoCap.mMinFrameWidth; int videoHeight = highQuality? videoCap.mMaxFrameHeight: videoCap.mMinFrameHeight; int videoFps = highQuality? videoCap.mMaxFrameRate: videoCap.mMinFrameRate; int videoBitrate = highQuality? videoCap.mMaxBitRate: videoCap.mMinBitRate; int audioBitrate = highQuality? audioCap.mMaxBitRate: audioCap.mMinBitRate; int audioChannels = highQuality? audioCap.mMaxChannels: audioCap.mMinChannels ; int audioSamplingRate = highQuality? audioCap.mMaxSampleRate: audioCap.mMinSampleRate; //Overide the fps if the min_camera_fps is set if (MediaFrameworkTestRunner.mMinCameraFps != 0 && MediaFrameworkTestRunner.mMinCameraFps > videoFps){ videoFps = MediaFrameworkTestRunner.mMinCameraFps; } if (videoFps < MIN_VIDEO_FPS) { videoFps = MIN_VIDEO_FPS; } mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp"); try { Log.v(TAG, "video encoder : " + videoEncoder); Log.v(TAG, "audio encoder : " + audioEncoder); Log.v(TAG, "quality : " + (highQuality?"high": "low")); Log.v(TAG, "encoder : " + MediaProfileReader.getVideoCodecName(videoEncoder)); Log.v(TAG, "audio : " + MediaProfileReader.getAudioCodecName(audioEncoder)); Log.v(TAG, "videoWidth : " + videoWidth); Log.v(TAG, "videoHeight : " + videoHeight); Log.v(TAG, "videoFPS : " + videoFps); Log.v(TAG, "videobitrate : " + videoBitrate); Log.v(TAG, "audioBitrate : " + audioBitrate); Log.v(TAG, "audioChannel : " + audioChannels); Log.v(TAG, "AudioSampleRate : " + audioSamplingRate); MediaRecorder mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mMediaRecorder.setOutputFile(filename); mMediaRecorder.setVideoFrameRate(videoFps); mMediaRecorder.setVideoSize(videoWidth, videoHeight); mMediaRecorder.setVideoEncodingBitRate(videoBitrate); mMediaRecorder.setAudioEncodingBitRate(audioBitrate); mMediaRecorder.setAudioChannels(audioChannels); mMediaRecorder.setAudioSamplingRate(audioSamplingRate); mMediaRecorder.setVideoEncoder(videoEncoder); mMediaRecorder.setAudioEncoder(audioEncoder); mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); mMediaRecorder.prepare(); mMediaRecorder.start(); Thread.sleep(MediaNames.RECORDED_TIME); mMediaRecorder.stop(); mMediaRecorder.release(); recordSuccess = validateVideo(filename, videoWidth, videoHeight); } catch (Exception e) { Log.v(TAG, e.toString()); return false; } return recordSuccess; } private boolean invalidRecordSetting(int frameRate, int width, int height, int videoFormat, int outFormat, String outFile, boolean videoOnly) { try { if (!videoOnly) { Log.v(TAG, "setAudioSource"); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); } mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setOutputFormat(outFormat); Log.v(TAG, "output format " + outFormat); mRecorder.setOutputFile(outFile); mRecorder.setVideoFrameRate(frameRate); mRecorder.setVideoSize(width, height); Log.v(TAG, "setEncoder"); mRecorder.setVideoEncoder(videoFormat); if (!videoOnly) { mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); } mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); Log.v(TAG, "setPreview"); mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); Log.v(TAG, "prepare"); mRecorder.prepare(); Log.v(TAG, "start"); mRecorder.start(); Thread.sleep(MediaNames.RECORDED_TIME); Log.v(TAG, "stop"); mRecorder.stop(); mRecorder.release(); } catch (Exception e) { Log.v("record video failed ", e.toString()); mRecorder.release(); Log.v(TAG, "reset and release"); return true; } return false; } private void getOutputVideoProperty(String outputFilePath) { MediaPlayer mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(outputFilePath); Log.v(TAG, "file Path = " + outputFilePath); mediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); Log.v(TAG, "before player prepare"); mediaPlayer.prepare(); Log.v(TAG, "before getduration"); mOutputDuration = mediaPlayer.getDuration(); Log.v(TAG, "get video dimension"); Thread.sleep(1000); mOutputVideoHeight = mediaPlayer.getVideoHeight(); mOutputVideoWidth = mediaPlayer.getVideoWidth(); mediaPlayer.release(); } catch (Exception e) { Log.v(TAG, e.toString()); mediaPlayer.release(); } } private boolean validateVideo(String filePath, int width, int height) { boolean validVideo = false; getOutputVideoProperty(filePath); if (mOutputVideoWidth == width && mOutputVideoHeight == height && mOutputDuration > MediaNames.VALID_VIDEO_DURATION ) { validVideo = true; } Log.v(TAG, "width = " + mOutputVideoWidth + " height = " + mOutputVideoHeight + " Duration = " + mOutputDuration); return validVideo; } @LargeTest /* * This test case set the camera in portrait mode. * Verification: validate the video dimension and the duration. */ public void testPortraitH263() throws Exception { boolean videoRecordedResult = false; try { mCamera = Camera.open(CAMERA_ID); Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(352, 288); parameters.set("orientation", "portrait"); mCamera.setParameters(parameters); mCamera.unlock(); mRecorder.setCamera(mCamera); Thread.sleep(1000); int codec = MediaRecorder.VideoEncoder.H263; int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); recordVideo(frameRate, 352, 288, codec, MediaRecorder.OutputFormat.THREE_GPP, MediaNames.RECORDED_PORTRAIT_H263, true); mCamera.lock(); mCamera.release(); videoRecordedResult = validateVideo(MediaNames.RECORDED_PORTRAIT_H263, 352, 288); } catch (Exception e) { Log.v(TAG, e.toString()); } assertTrue("PortraitH263", videoRecordedResult); } @LargeTest public void testInvalidVideoPath() throws Exception { boolean isTestInvalidVideoPathSuccessful = false; isTestInvalidVideoPathSuccessful = invalidRecordSetting(15, 176, 144, MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.THREE_GPP, MediaNames.INVALD_VIDEO_PATH, false); assertTrue("Invalid outputFile Path", isTestInvalidVideoPathSuccessful); } @LargeTest //test cases for the new codec public void testDeviceSpecificCodec() throws Exception { int noOfFailure = 0; boolean recordSuccess = false; String deviceType = MediaProfileReader.getDeviceType(); Log.v(TAG, "deviceType = " + deviceType); List videoEncoders = MediaProfileReader.getVideoEncoders(); List audioEncoders = MediaProfileReader.getAudioEncoders(); for (int k = 0; k < 2; k++) { for (VideoEncoderCap videoEncoder: videoEncoders) { for (AudioEncoderCap audioEncoder: audioEncoders) { if (k == 0) { recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, true); } else { recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, false); } if (!recordSuccess) { Log.v(TAG, "testDeviceSpecificCodec failed"); Log.v(TAG, "Encoder = " + videoEncoder.mCodec + "Audio Encoder = " + audioEncoder.mCodec); noOfFailure++; } } } } if (noOfFailure != 0) { assertTrue("testDeviceSpecificCodec", false); } } // Test MediaRecorder.getSurface() api with surface or camera source public void testGetSurfaceApi() { boolean success = false; int noOfFailure = 0; try { for (int k = 0; k < 2; k++) { success = validateGetSurface( k == 0 ? true : false /* useSurface */); if (!success) { noOfFailure++; } } } catch (Exception e) { Log.v(TAG, e.toString()); } assertTrue("testGetSurfaceApi", noOfFailure == 0); } // Test recording from surface source with/without audio public void testSurfaceRecording() { boolean success = false; int noOfFailure = 0; try { int codec = MediaRecorder.VideoEncoder.H264; int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); for (int k = 0; k < 2; k++) { String filename = "/sdcard/surface_" + (k==0?"video_only":"with_audio") + ".3gp"; success = recordVideoFromSurface(frameRate, 0, 352, 288, codec, MediaRecorder.OutputFormat.THREE_GPP, filename, k == 0 ? true : false /* videoOnly */); if (success) { success = validateVideo(filename, 352, 288); } if (!success) { noOfFailure++; } } } catch (Exception e) { Log.v(TAG, e.toString()); } assertTrue("testSurfaceRecording", noOfFailure == 0); } // Test recording from surface source with/without audio public void testSurfaceRecordingTimeLapse() { boolean success = false; int noOfFailure = 0; try { int codec = MediaRecorder.VideoEncoder.H264; int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec); for (int k = 0; k < 2; k++) { // k==0: time lapse test, set capture rate to MIN_VIDEO_FPS // k==1: slow motion test, set capture rate to HIGH_SPEED_FPS String filename = "/sdcard/surface_" + (k==0 ? "time_lapse" : "slow_motion") + ".3gp"; // always set videoOnly=false, MediaRecorder should disable // audio automatically with time lapse/slow motion success = recordVideoFromSurface(frameRate, k==0 ? MIN_VIDEO_FPS : HIGH_SPEED_FPS, 352, 288, codec, MediaRecorder.OutputFormat.THREE_GPP, filename, false /* videoOnly */); if (success) { success = validateVideo(filename, 352, 288); } if (!success) { noOfFailure++; } } } catch (Exception e) { Log.v(TAG, e.toString()); } assertTrue("testSurfaceRecordingTimeLapse", noOfFailure == 0); } }