• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 package android.media.cts;
17 
18 
19 import android.content.pm.PackageManager;
20 import android.hardware.Camera;
21 import android.media.MediaMetadataRetriever;
22 import android.media.MediaRecorder;
23 import android.media.MediaRecorder.OnErrorListener;
24 import android.media.MediaRecorder.OnInfoListener;
25 import android.media.MediaMetadataRetriever;
26 import android.os.Environment;
27 import android.os.ConditionVariable;
28 import android.test.ActivityInstrumentationTestCase2;
29 import android.test.UiThreadTest;
30 import android.view.Surface;
31 
32 import android.util.Log;
33 
34 import java.io.File;
35 import java.io.FileDescriptor;
36 import java.io.FileOutputStream;
37 import java.lang.InterruptedException;
38 import java.lang.Runnable;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
43     private final String TAG = "MediaRecorderTest";
44     private final String OUTPUT_PATH;
45     private final String OUTPUT_PATH2;
46     private static final float TOLERANCE = 0.0002f;
47     private static final int RECORD_TIME_MS = 3000;
48     private static final int RECORD_TIME_LAPSE_MS = 4000;
49     private static final int RECORD_TIME_LONG_MS = 20000;
50     private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
51     private static final int VIDEO_WIDTH = 176;
52     private static final int VIDEO_HEIGHT = 144;
53     private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
54     private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
55     private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
56     private static final int AUDIO_NUM_CHANNELS = 1;
57     private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
58     private static final long MAX_FILE_SIZE = 5000;
59     private static final int MAX_FILE_SIZE_TIMEOUT_MS = 5 * 60 * 1000;
60     private static final int MAX_DURATION_MSEC = 2000;
61     private static final float LATITUDE = 0.0000f;
62     private static final float LONGITUDE  = -180.0f;
63     private boolean mOnInfoCalled;
64     private boolean mOnErrorCalled;
65     private File mOutFile;
66     private File mOutFile2;
67     private Camera mCamera;
68     private MediaStubActivity mActivity = null;
69 
70     private MediaRecorder mMediaRecorder;
71     private ConditionVariable mMaxDurationCond;
72     private ConditionVariable mMaxFileSizeCond;
73 
MediaRecorderTest()74     public MediaRecorderTest() {
75         super("com.android.cts.media", MediaStubActivity.class);
76         OUTPUT_PATH = new File(Environment.getExternalStorageDirectory(),
77                 "record.out").getAbsolutePath();
78         OUTPUT_PATH2 = new File(Environment.getExternalStorageDirectory(),
79                 "record2.out").getAbsolutePath();
80     }
81 
completeOnUiThread(final Runnable runnable)82     private void completeOnUiThread(final Runnable runnable) {
83         final CountDownLatch latch = new CountDownLatch(1);
84         getActivity().runOnUiThread(new Runnable() {
85             @Override
86             public void run() {
87                 runnable.run();
88                 latch.countDown();
89             }
90         });
91         try {
92             // if UI thread does not run, things will fail anyway
93             assertTrue(latch.await(10, TimeUnit.SECONDS));
94         } catch (java.lang.InterruptedException e) {
95             fail("should not be interrupted");
96         }
97     }
98 
99     @Override
setUp()100     protected void setUp() throws Exception {
101         mActivity = getActivity();
102         completeOnUiThread(new Runnable() {
103             @Override
104             public void run() {
105                 mMediaRecorder = new MediaRecorder();
106                 mOutFile = new File(OUTPUT_PATH);
107                 mOutFile2 = new File(OUTPUT_PATH2);
108 
109                 mMaxDurationCond = new ConditionVariable();
110                 mMaxFileSizeCond = new ConditionVariable();
111 
112                 mMediaRecorder.setOutputFile(OUTPUT_PATH);
113                 mMediaRecorder.setOnInfoListener(new OnInfoListener() {
114                     public void onInfo(MediaRecorder mr, int what, int extra) {
115                         mOnInfoCalled = true;
116                         if (what ==
117                             MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
118                             Log.v(TAG, "max duration reached");
119                             mMaxDurationCond.open();
120                         } else if (what ==
121                             MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
122                             Log.v(TAG, "max file size reached");
123                             mMaxFileSizeCond.open();
124                         }
125                     }
126                 });
127                 mMediaRecorder.setOnErrorListener(new OnErrorListener() {
128                     public void onError(MediaRecorder mr, int what, int extra) {
129                         mOnErrorCalled = true;
130                     }
131                 });
132             }
133         });
134         super.setUp();
135     }
136 
137     @Override
tearDown()138     protected void tearDown() throws Exception {
139         mMediaRecorder.release();
140         mMediaRecorder = null;
141         if (mOutFile != null && mOutFile.exists()) {
142             mOutFile.delete();
143         }
144         if (mOutFile2 != null && mOutFile2.exists()) {
145             mOutFile2.delete();
146         }
147         if (mCamera != null)  {
148             mCamera.release();
149             mCamera = null;
150         }
151         mMaxDurationCond.close();
152         mMaxDurationCond = null;
153         mMaxFileSizeCond.close();
154         mMaxFileSizeCond = null;
155         mActivity = null;
156         super.tearDown();
157     }
158 
testRecorderCamera()159     public void testRecorderCamera() throws Exception {
160         if (!hasCamera()) {
161             return;
162         }
163         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
164         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
165         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
166         mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
167         mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
168         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
169         mMediaRecorder.prepare();
170         mMediaRecorder.start();
171         Thread.sleep(RECORD_TIME_MS);
172         mMediaRecorder.stop();
173         checkOutputExist();
174     }
175 
176     @UiThreadTest
testSetCamera()177     public void testSetCamera() throws Exception {
178         recordVideoUsingCamera(false);
179     }
180 
testRecorderTimelapsedVideo()181     public void testRecorderTimelapsedVideo() throws Exception {
182         recordVideoUsingCamera(true);
183     }
184 
recordVideoUsingCamera(boolean timelapse)185     private void recordVideoUsingCamera(boolean timelapse) throws Exception {
186         int nCamera = Camera.getNumberOfCameras();
187         int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
188         for (int cameraId = 0; cameraId < nCamera; cameraId++) {
189             mCamera = Camera.open(cameraId);
190             recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
191             mCamera.release();
192             mCamera = null;
193             assertTrue(checkLocationInFile(OUTPUT_PATH));
194         }
195     }
196 
recordVideoUsingCamera( Camera camera, String fileName, int durMs, boolean timelapse)197     private void recordVideoUsingCamera(
198             Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
199         // FIXME:
200         // We should add some test case to use Camera.Parameters.getPreviewFpsRange()
201         // to get the supported video frame rate range.
202         Camera.Parameters params = camera.getParameters();
203         int frameRate = params.getPreviewFrameRate();
204 
205         camera.unlock();
206         mMediaRecorder.setCamera(camera);
207         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
208         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
209         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
210         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
211         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
212         mMediaRecorder.setVideoFrameRate(frameRate);
213         mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
214         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
215         mMediaRecorder.setOutputFile(fileName);
216         mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
217         final double captureRate = VIDEO_TIMELAPSE_CAPTURE_RATE_FPS;
218         if (timelapse) {
219             mMediaRecorder.setCaptureRate(captureRate);
220         }
221 
222         mMediaRecorder.prepare();
223         mMediaRecorder.start();
224         Thread.sleep(durMs);
225         mMediaRecorder.stop();
226         assertTrue(mOutFile.exists());
227 
228         int targetDurMs = timelapse? ((int) (durMs * (captureRate / frameRate))): durMs;
229         boolean hasVideo = true;
230         boolean hasAudio = timelapse? false: true;
231         checkTracksAndDuration(targetDurMs, hasVideo, hasAudio, fileName);
232     }
233 
checkTracksAndDuration( int targetMs, boolean hasVideo, boolean hasAudio, String fileName)234     private void checkTracksAndDuration(
235             int targetMs, boolean hasVideo, boolean hasAudio, String fileName) throws Exception {
236         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
237         retriever.setDataSource(fileName);
238         String hasVideoStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
239         String hasAudioStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
240         assertTrue(hasVideo? hasVideoStr != null : hasVideoStr == null);
241         assertTrue(hasAudio? hasAudioStr != null : hasAudioStr == null);
242         // FIXME:
243         // If we could use fixed frame rate for video recording, we could also do more accurate
244         // check on the duration.
245         String durStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
246         assertTrue(durStr != null);
247         assertTrue(Integer.parseInt(durStr) > 0);
248         retriever.release();
249         retriever = null;
250     }
251 
checkLocationInFile(String fileName)252     private boolean checkLocationInFile(String fileName) {
253         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
254         retriever.setDataSource(fileName);
255         String location = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
256         if (location == null) {
257             retriever.release();
258             Log.v(TAG, "No location information found in file " + fileName);
259             return false;
260         }
261 
262         // parsing String location and recover the location inforamtion in floats
263         // Make sure the tolerance is very small - due to rounding errors?.
264         Log.v(TAG, "location: " + location);
265 
266         // Get the position of the -/+ sign in location String, which indicates
267         // the beginning of the longtitude.
268         int index = location.lastIndexOf('-');
269         if (index == -1) {
270             index = location.lastIndexOf('+');
271         }
272         assertTrue("+ or - is not found", index != -1);
273         assertTrue("+ or - is only found at the beginning", index != 0);
274         float latitude = Float.parseFloat(location.substring(0, index - 1));
275         float longitude = Float.parseFloat(location.substring(index));
276         assertTrue("Incorrect latitude: " + latitude, Math.abs(latitude - LATITUDE) <= TOLERANCE);
277         assertTrue("Incorrect longitude: " + longitude, Math.abs(longitude - LONGITUDE) <= TOLERANCE);
278         retriever.release();
279         return true;
280     }
281 
checkOutputExist()282     private void checkOutputExist() {
283         assertTrue(mOutFile.exists());
284         assertTrue(mOutFile.length() > 0);
285         assertTrue(mOutFile.delete());
286     }
287 
testRecorderVideo()288     public void testRecorderVideo() throws Exception {
289         if (!hasCamera()) {
290             return;
291         }
292         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
293         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
294         mMediaRecorder.setOutputFile(OUTPUT_PATH2);
295         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
296         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
297         mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
298 
299         FileOutputStream fos = new FileOutputStream(OUTPUT_PATH2);
300         FileDescriptor fd = fos.getFD();
301         mMediaRecorder.setOutputFile(fd);
302         long maxFileSize = MAX_FILE_SIZE * 10;
303         recordMedia(maxFileSize, mOutFile2);
304         assertFalse(checkLocationInFile(OUTPUT_PATH2));
305         fos.close();
306     }
307 
testRecordingAudioInRawFormats()308     public void testRecordingAudioInRawFormats() throws Exception {
309         testRecordAudioInRawFormat(
310                 MediaRecorder.OutputFormat.AMR_NB,
311                 MediaRecorder.AudioEncoder.AMR_NB);
312 
313         testRecordAudioInRawFormat(
314                 MediaRecorder.OutputFormat.AMR_WB,
315                 MediaRecorder.AudioEncoder.AMR_WB);
316 
317         testRecordAudioInRawFormat(
318                 MediaRecorder.OutputFormat.AAC_ADTS,
319                 MediaRecorder.AudioEncoder.AAC);
320     }
321 
testRecordAudioInRawFormat( int fileFormat, int codec)322     private void testRecordAudioInRawFormat(
323             int fileFormat, int codec) throws Exception {
324 
325         if (!hasMicrophone()) {
326             return;
327         }
328         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
329         mMediaRecorder.setOutputFormat(fileFormat);
330         mMediaRecorder.setOutputFile(OUTPUT_PATH);
331         mMediaRecorder.setAudioEncoder(codec);
332         recordMedia(MAX_FILE_SIZE, mOutFile);
333     }
334 
testGetAudioSourceMax()335     public void testGetAudioSourceMax() throws Exception {
336         final int max = MediaRecorder.getAudioSourceMax();
337         assertTrue(MediaRecorder.AudioSource.DEFAULT <= max);
338         assertTrue(MediaRecorder.AudioSource.MIC <= max);
339         assertTrue(MediaRecorder.AudioSource.CAMCORDER <= max);
340         assertTrue(MediaRecorder.AudioSource.VOICE_CALL <= max);
341         assertTrue(MediaRecorder.AudioSource.VOICE_COMMUNICATION <= max);
342         assertTrue(MediaRecorder.AudioSource.VOICE_DOWNLINK <= max);
343         assertTrue(MediaRecorder.AudioSource.VOICE_RECOGNITION <= max);
344         assertTrue(MediaRecorder.AudioSource.VOICE_UPLINK <= max);
345     }
346 
testRecorderAudio()347     public void testRecorderAudio() throws Exception {
348         if (!hasMicrophone()) {
349             return;
350         }
351         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
352         assertEquals(0, mMediaRecorder.getMaxAmplitude());
353         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
354         mMediaRecorder.setOutputFile(OUTPUT_PATH);
355         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
356         mMediaRecorder.setAudioChannels(AUDIO_NUM_CHANNELS);
357         mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
358         mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
359         recordMedia(MAX_FILE_SIZE, mOutFile);
360     }
361 
testOnInfoListener()362     public void testOnInfoListener() throws Exception {
363         if (!hasMicrophone()) {
364             return;
365         }
366         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
367         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
368         mMediaRecorder.setMaxDuration(MAX_DURATION_MSEC);
369         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
370         mMediaRecorder.prepare();
371         mMediaRecorder.start();
372         Thread.sleep(RECORD_TIME_MS);
373         assertTrue(mOnInfoCalled);
374     }
375 
testSetMaxDuration()376     public void testSetMaxDuration() throws Exception {
377         if (!hasMicrophone()) {
378             return;
379         }
380         testSetMaxDuration(RECORD_TIME_LONG_MS, RECORDED_DUR_TOLERANCE_MS);
381     }
382 
testSetMaxDuration(long durationMs, long toleranceMs)383     private void testSetMaxDuration(long durationMs, long toleranceMs) throws Exception {
384         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
385         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
386         mMediaRecorder.setMaxDuration((int)durationMs);
387         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
388         mMediaRecorder.prepare();
389         mMediaRecorder.start();
390         long startTimeMs = System.currentTimeMillis();
391         if (!mMaxDurationCond.block(durationMs + toleranceMs)) {
392             fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_DURATION_REACHED");
393         }
394         long endTimeMs = System.currentTimeMillis();
395         long actualDurationMs = endTimeMs - startTimeMs;
396         mMediaRecorder.stop();
397         checkRecordedTime(durationMs, actualDurationMs, toleranceMs);
398     }
399 
checkRecordedTime(long expectedMs, long actualMs, long tolerance)400     private void checkRecordedTime(long expectedMs, long actualMs, long tolerance) {
401         assertEquals(expectedMs, actualMs, tolerance);
402         long actualFileDurationMs = getRecordedFileDurationMs(OUTPUT_PATH);
403         assertEquals(actualFileDurationMs, actualMs, tolerance);
404     }
405 
getRecordedFileDurationMs(final String fileName)406     private int getRecordedFileDurationMs(final String fileName) {
407         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
408         retriever.setDataSource(fileName);
409         String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
410         assertNotNull(durationStr);
411         return Integer.parseInt(durationStr);
412     }
413 
testSetMaxFileSize()414     public void testSetMaxFileSize() throws Exception {
415         if (!hasMicrophone() || !hasCamera()) {
416             return;
417         }
418         testSetMaxFileSize(512 * 1024, 50 * 1024);
419     }
420 
testSetMaxFileSize( long fileSize, long tolerance)421     private void testSetMaxFileSize(
422             long fileSize, long tolerance) throws Exception {
423         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
424         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
425         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
426         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
427         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
428         mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
429         mMediaRecorder.setVideoEncodingBitRate(256000);
430         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
431         mMediaRecorder.setMaxFileSize(fileSize);
432         mMediaRecorder.prepare();
433         mMediaRecorder.start();
434 
435         // Recording a scene with moving objects would greatly help reduce
436         // the time for waiting.
437         if (!mMaxFileSizeCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
438             fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
439         }
440         mMediaRecorder.stop();
441         checkOutputFileSize(OUTPUT_PATH, fileSize, tolerance);
442     }
443 
checkOutputFileSize(final String fileName, long fileSize, long tolerance)444     private void checkOutputFileSize(final String fileName, long fileSize, long tolerance) {
445         assertTrue(mOutFile.exists());
446         assertEquals(fileSize, mOutFile.length(), tolerance);
447         assertTrue(mOutFile.delete());
448     }
449 
testOnErrorListener()450     public void testOnErrorListener() throws Exception {
451         if (!hasMicrophone()) {
452             return;
453         }
454         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
455         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
456         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
457 
458         recordMedia(MAX_FILE_SIZE, mOutFile);
459         // TODO: how can we trigger a recording error?
460         assertFalse(mOnErrorCalled);
461     }
462 
recordMedia(long maxFileSize, File outFile)463     private void recordMedia(long maxFileSize, File outFile) throws Exception {
464         mMediaRecorder.setMaxFileSize(maxFileSize);
465         mMediaRecorder.prepare();
466         mMediaRecorder.start();
467         Thread.sleep(RECORD_TIME_MS);
468         mMediaRecorder.stop();
469 
470         assertTrue(outFile.exists());
471 
472         // The max file size is always guaranteed.
473         // We just make sure that the margin is not too big
474         assertTrue(outFile.length() < 1.1 * maxFileSize);
475         assertTrue(outFile.length() > 0);
476     }
477 
hasCamera()478     private boolean hasCamera() {
479         return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
480     }
481 
hasMicrophone()482     private boolean hasMicrophone() {
483         return mActivity.getPackageManager().hasSystemFeature(
484                 PackageManager.FEATURE_MICROPHONE);
485     }
486 }
487