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 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
19 
20 import android.content.pm.PackageManager;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Paint;
24 import android.hardware.Camera;
25 import android.media.AudioFormat;
26 import android.media.AudioRecordingConfiguration;
27 import android.media.CamcorderProfile;
28 import android.media.EncoderCapabilities;
29 import android.media.EncoderCapabilities.VideoEncoderCap;
30 import android.media.MediaCodec;
31 import android.media.MediaCodecInfo;
32 import android.media.MediaCodecInfo.CodecCapabilities;
33 import android.media.MediaCodecList;
34 import android.media.MediaExtractor;
35 import android.media.MediaFormat;
36 import android.media.MediaMetadataRetriever;
37 import android.media.MediaRecorder;
38 import android.media.MediaRecorder.OnErrorListener;
39 import android.media.MediaRecorder.OnInfoListener;
40 import android.media.MicrophoneInfo;
41 import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
42 import android.opengl.GLES20;
43 import android.os.ConditionVariable;
44 import android.os.Environment;
45 import android.os.ParcelFileDescriptor;
46 import android.os.PersistableBundle;
47 import android.platform.test.annotations.AppModeFull;
48 import android.platform.test.annotations.RequiresDevice;
49 import android.test.ActivityInstrumentationTestCase2;
50 import android.test.UiThreadTest;
51 import android.util.Log;
52 import android.view.Surface;
53 
54 import androidx.test.filters.SmallTest;
55 
56 import com.android.compatibility.common.util.CddTest;
57 import com.android.compatibility.common.util.MediaUtils;
58 
59 import java.io.File;
60 import java.io.FileDescriptor;
61 import java.io.FileOutputStream;
62 import java.io.IOException;
63 import java.io.RandomAccessFile;
64 import java.lang.Runnable;
65 import java.util.ArrayList;
66 import java.util.List;
67 import java.util.Set;
68 import java.util.concurrent.CountDownLatch;
69 import java.util.concurrent.Executor;
70 import java.util.concurrent.TimeUnit;
71 
72 @SmallTest
73 @RequiresDevice
74 @AppModeFull(reason = "TODO: evaluate and port to instant")
75 public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
76     private final String TAG = "MediaRecorderTest";
77     private final String OUTPUT_PATH;
78     private final String OUTPUT_PATH2;
79     private static final float TOLERANCE = 0.0002f;
80     private static final int RECORD_TIME_MS = 3000;
81     private static final int RECORD_TIME_LAPSE_MS = 6000;
82     private static final int RECORD_TIME_LONG_MS = 20000;
83     private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
84     private static final int TEST_TIMING_TOLERANCE_MS = 70;
85     // Tolerate 4 frames off at maximum
86     private static final float RECORDED_DUR_TOLERANCE_FRAMES = 4f;
87     private static final int VIDEO_WIDTH = 176;
88     private static final int VIDEO_HEIGHT = 144;
89     private static int mVideoWidth = VIDEO_WIDTH;
90     private static int mVideoHeight = VIDEO_HEIGHT;
91     private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
92     private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
93     private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
94     private static final int AUDIO_NUM_CHANNELS = 1;
95     private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
96     private static final long MAX_FILE_SIZE = 5000;
97     private static final int MAX_FILE_SIZE_TIMEOUT_MS = 5 * 60 * 1000;
98     private static final int MAX_DURATION_MSEC = 2000;
99     private static final float LATITUDE = 0.0000f;
100     private static final float LONGITUDE  = -180.0f;
101     private static final int NORMAL_FPS = 30;
102     private static final int TIME_LAPSE_FPS = 5;
103     private static final int SLOW_MOTION_FPS = 120;
104     private static final List<VideoEncoderCap> mVideoEncoders =
105             EncoderCapabilities.getVideoEncoders();
106 
107     private boolean mOnInfoCalled;
108     private boolean mOnErrorCalled;
109     private File mOutFile;
110     private File mOutFile2;
111     private Camera mCamera;
112     private MediaStubActivity mActivity = null;
113     private int mFileIndex;
114 
115     private MediaRecorder mMediaRecorder;
116     private ConditionVariable mMaxDurationCond;
117     private ConditionVariable mMaxFileSizeCond;
118     private ConditionVariable mMaxFileSizeApproachingCond;
119     private ConditionVariable mNextOutputFileStartedCond;
120     private boolean mExpectMaxFileCond;
121 
122     // movie length, in frames
123     private static final int NUM_FRAMES = 120;
124 
125     private static final int TEST_R0 = 0;                   // RGB equivalent of {0,0,0} (BT.601)
126     private static final int TEST_G0 = 136;
127     private static final int TEST_B0 = 0;
128     private static final int TEST_R1 = 236;                 // RGB equivalent of {120,160,200} (BT.601)
129     private static final int TEST_G1 = 50;
130     private static final int TEST_B1 = 186;
131 
132     private final static String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
133 
MediaRecorderTest()134     public MediaRecorderTest() {
135         super("android.media.cts", MediaStubActivity.class);
136         OUTPUT_PATH = new File(Environment.getExternalStorageDirectory(),
137                 "record.out").getAbsolutePath();
138         OUTPUT_PATH2 = new File(Environment.getExternalStorageDirectory(),
139                 "record2.out").getAbsolutePath();
140     }
141 
completeOnUiThread(final Runnable runnable)142     private void completeOnUiThread(final Runnable runnable) {
143         final CountDownLatch latch = new CountDownLatch(1);
144         getActivity().runOnUiThread(new Runnable() {
145             @Override
146             public void run() {
147                 runnable.run();
148                 latch.countDown();
149             }
150         });
151         try {
152             // if UI thread does not run, things will fail anyway
153             assertTrue(latch.await(10, TimeUnit.SECONDS));
154         } catch (java.lang.InterruptedException e) {
155             fail("should not be interrupted");
156         }
157     }
158 
159     @Override
setUp()160     protected void setUp() throws Exception {
161         mActivity = getActivity();
162         completeOnUiThread(new Runnable() {
163             @Override
164             public void run() {
165                 mMediaRecorder = new MediaRecorder();
166                 mOutFile = new File(OUTPUT_PATH);
167                 mOutFile2 = new File(OUTPUT_PATH2);
168                 mFileIndex = 0;
169 
170                 mMaxDurationCond = new ConditionVariable();
171                 mMaxFileSizeCond = new ConditionVariable();
172                 mMaxFileSizeApproachingCond = new ConditionVariable();
173                 mNextOutputFileStartedCond = new ConditionVariable();
174                 mExpectMaxFileCond = true;
175 
176                 mMediaRecorder.setOutputFile(OUTPUT_PATH);
177                 mMediaRecorder.setOnInfoListener(new OnInfoListener() {
178                     public void onInfo(MediaRecorder mr, int what, int extra) {
179                         mOnInfoCalled = true;
180                         if (what ==
181                             MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
182                             Log.v(TAG, "max duration reached");
183                             mMaxDurationCond.open();
184                         } else if (what ==
185                             MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
186                             Log.v(TAG, "max file size reached");
187                             mMaxFileSizeCond.open();
188                         }
189                     }
190                 });
191                 mMediaRecorder.setOnErrorListener(new OnErrorListener() {
192                     public void onError(MediaRecorder mr, int what, int extra) {
193                         mOnErrorCalled = true;
194                     }
195                 });
196             }
197         });
198         super.setUp();
199     }
200 
201     @Override
tearDown()202     protected void tearDown() throws Exception {
203         if (mMediaRecorder != null) {
204             mMediaRecorder.release();
205             mMediaRecorder = null;
206         }
207         if (mOutFile != null && mOutFile.exists()) {
208             mOutFile.delete();
209         }
210         if (mOutFile2 != null && mOutFile2.exists()) {
211             mOutFile2.delete();
212         }
213         if (mCamera != null)  {
214             mCamera.release();
215             mCamera = null;
216         }
217         mMaxDurationCond.close();
218         mMaxDurationCond = null;
219         mMaxFileSizeCond.close();
220         mMaxFileSizeCond = null;
221         mMaxFileSizeApproachingCond.close();
222         mMaxFileSizeApproachingCond = null;
223         mNextOutputFileStartedCond.close();
224         mNextOutputFileStartedCond = null;
225         mActivity = null;
226         super.tearDown();
227     }
228 
testRecorderCamera()229     public void testRecorderCamera() throws Exception {
230         int width;
231         int height;
232         Camera camera = null;
233         if (!hasCamera()) {
234             return;
235         }
236         // Try to get camera profile for QUALITY_LOW; if unavailable,
237         // set the video size to default value.
238         CamcorderProfile profile = CamcorderProfile.get(
239                 0 /* cameraId */, CamcorderProfile.QUALITY_LOW);
240         if (profile != null) {
241             width = profile.videoFrameWidth;
242             height = profile.videoFrameHeight;
243         } else {
244             width = VIDEO_WIDTH;
245             height = VIDEO_HEIGHT;
246         }
247         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
248         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
249         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
250         mMediaRecorder.setVideoSize(width, height);
251         mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
252         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
253         mMediaRecorder.prepare();
254         mMediaRecorder.start();
255         Thread.sleep(RECORD_TIME_MS);
256 
257 
258         // verify some getMetrics() behaviors while we're here.
259         PersistableBundle metrics = mMediaRecorder.getMetrics();
260         if (metrics == null) {
261             fail("MediaRecorder.getMetrics() returned null metrics");
262         } else if (metrics.isEmpty()) {
263             fail("MediaRecorder.getMetrics() returned empty metrics");
264         } else {
265             int size = metrics.size();
266             Set<String> keys = metrics.keySet();
267 
268             if (size == 0) {
269                 fail("MediaRecorder.getMetrics().size() reports empty record");
270             }
271 
272             if (keys == null) {
273                 fail("MediaMetricsSet returned no keys");
274             } else if (keys.size() != size) {
275                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
276             }
277 
278             // ensure existence of some known fields
279             int videoBitRate = metrics.getInt(MediaRecorder.MetricsConstants.VIDEO_BITRATE, -1);
280             if (videoBitRate != VIDEO_BIT_RATE_IN_BPS) {
281                 fail("getMetrics() videoEncodeBitrate set " +
282                      VIDEO_BIT_RATE_IN_BPS + " got " + videoBitRate);
283             }
284 
285             // valid values are -1.0 and >= 0;
286             // tolerate some floating point rounding variability
287             double captureFrameRate = metrics.getDouble(MediaRecorder.MetricsConstants.CAPTURE_FPS, -2);
288             if (captureFrameRate < 0.) {
289                 assertEquals("getMetrics() capture framerate=" + captureFrameRate, -1.0, captureFrameRate, 0.001);
290             }
291         }
292 
293 
294         mMediaRecorder.stop();
295         checkOutputExist();
296     }
297 
testRecorderMPEG2TS()298     public void testRecorderMPEG2TS() throws Exception {
299         int width;
300         int height;
301         Camera camera = null;
302         if (!hasCamera()) {
303             MediaUtils.skipTest("no camera");
304             return;
305         }
306         if (!hasMicrophone() || !hasAac()) {
307             MediaUtils.skipTest("no audio codecs or microphone");
308             return;
309         }
310         // Try to get camera profile for QUALITY_LOW; if unavailable,
311         // set the video size to default value.
312         CamcorderProfile profile = CamcorderProfile.get(
313                 0 /* cameraId */, CamcorderProfile.QUALITY_LOW);
314         if (profile != null) {
315             width = profile.videoFrameWidth;
316             height = profile.videoFrameHeight;
317         } else {
318             width = VIDEO_WIDTH;
319             height = VIDEO_HEIGHT;
320         }
321         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
322         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
323         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
324         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
325         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
326         mMediaRecorder.setVideoSize(width, height);
327         mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
328         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
329         mMediaRecorder.prepare();
330         mMediaRecorder.start();
331         Thread.sleep(RECORD_TIME_MS);
332 
333         // verify some getMetrics() behaviors while we're here.
334         PersistableBundle metrics = mMediaRecorder.getMetrics();
335         if (metrics == null) {
336             fail("MediaRecorder.getMetrics() returned null metrics");
337         } else if (metrics.isEmpty()) {
338             fail("MediaRecorder.getMetrics() returned empty metrics");
339         } else {
340             int size = metrics.size();
341             Set<String> keys = metrics.keySet();
342 
343             if (size == 0) {
344                 fail("MediaRecorder.getMetrics().size() reports empty record");
345             }
346 
347             if (keys == null) {
348                 fail("MediaMetricsSet returned no keys");
349             } else if (keys.size() != size) {
350                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
351             }
352 
353             // ensure existence of some known fields
354             int videoBitRate = metrics.getInt(MediaRecorder.MetricsConstants.VIDEO_BITRATE, -1);
355             if (videoBitRate != VIDEO_BIT_RATE_IN_BPS) {
356                 fail("getMetrics() videoEncodeBitrate set " +
357                      VIDEO_BIT_RATE_IN_BPS + " got " + videoBitRate);
358             }
359 
360             // valid values are -1.0 and >= 0;
361             // tolerate some floating point rounding variability
362             double captureFrameRate = metrics.getDouble(MediaRecorder.MetricsConstants.CAPTURE_FPS, -2);
363             if (captureFrameRate < 0.) {
364                 assertEquals("getMetrics() capture framerate=" + captureFrameRate, -1.0, captureFrameRate, 0.001);
365             }
366         }
367 
368         mMediaRecorder.stop();
369         checkOutputExist();
370     }
371 
372     @UiThreadTest
testSetCamera()373     public void testSetCamera() throws Exception {
374         recordVideoUsingCamera(false, false);
375     }
376 
testRecorderTimelapsedVideo()377     public void testRecorderTimelapsedVideo() throws Exception {
378         recordVideoUsingCamera(true, false);
379     }
380 
testRecorderPauseResume()381     public void testRecorderPauseResume() throws Exception {
382         recordVideoUsingCamera(false, true);
383     }
384 
testRecorderPauseResumeOnTimeLapse()385     public void testRecorderPauseResumeOnTimeLapse() throws Exception {
386         recordVideoUsingCamera(true, true);
387     }
388 
recordVideoUsingCamera(boolean timelapse, boolean pause)389     private void recordVideoUsingCamera(boolean timelapse, boolean pause) throws Exception {
390         int nCamera = Camera.getNumberOfCameras();
391         int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
392         for (int cameraId = 0; cameraId < nCamera; cameraId++) {
393             mCamera = Camera.open(cameraId);
394             setSupportedResolution(mCamera);
395             recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse, pause);
396             mCamera.release();
397             mCamera = null;
398             assertTrue(checkLocationInFile(OUTPUT_PATH));
399         }
400     }
401 
setSupportedResolution(Camera camera)402     private void setSupportedResolution(Camera camera) {
403         Camera.Parameters parameters = camera.getParameters();
404         List<Camera.Size> videoSizes = parameters.getSupportedVideoSizes();
405         // getSupportedVideoSizes returns null when separate video/preview size
406         // is not supported.
407         if (videoSizes == null) {
408             videoSizes = parameters.getSupportedPreviewSizes();
409         }
410         int minVideoWidth = Integer.MAX_VALUE;
411         int minVideoHeight = Integer.MAX_VALUE;
412         for (Camera.Size size : videoSizes)
413         {
414             if (size.width == VIDEO_WIDTH && size.height == VIDEO_HEIGHT) {
415                 mVideoWidth = VIDEO_WIDTH;
416                 mVideoHeight = VIDEO_HEIGHT;
417                 return;
418             }
419             if (size.width < minVideoWidth || size.height < minVideoHeight) {
420                 minVideoWidth = size.width;
421                 minVideoHeight = size.height;
422             }
423         }
424         // Use minimum resolution to avoid that one frame size exceeds file size limit.
425         mVideoWidth = minVideoWidth;
426         mVideoHeight = minVideoHeight;
427     }
428 
recordVideoUsingCamera( Camera camera, String fileName, int durMs, boolean timelapse, boolean pause)429     private void recordVideoUsingCamera(
430             Camera camera, String fileName, int durMs, boolean timelapse, boolean pause)
431         throws Exception {
432         // FIXME:
433         // We should add some test case to use Camera.Parameters.getPreviewFpsRange()
434         // to get the supported video frame rate range.
435         Camera.Parameters params = camera.getParameters();
436         int frameRate = params.getPreviewFrameRate();
437 
438         camera.unlock();
439         mMediaRecorder.setCamera(camera);
440         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
441         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
442         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
443         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
444         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
445         mMediaRecorder.setVideoFrameRate(frameRate);
446         mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
447         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
448         mMediaRecorder.setOutputFile(fileName);
449         mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
450         final double captureRate = VIDEO_TIMELAPSE_CAPTURE_RATE_FPS;
451         if (timelapse) {
452             mMediaRecorder.setCaptureRate(captureRate);
453         }
454 
455         mMediaRecorder.prepare();
456         mMediaRecorder.start();
457         if (pause) {
458             Thread.sleep(durMs / 2);
459             mMediaRecorder.pause();
460             Thread.sleep(durMs / 2);
461             mMediaRecorder.resume();
462             Thread.sleep(durMs / 2);
463         } else {
464             Thread.sleep(durMs);
465         }
466         mMediaRecorder.stop();
467         assertTrue(mOutFile.exists());
468 
469         int targetDurMs = timelapse? ((int) (durMs * (captureRate / frameRate))): durMs;
470         boolean hasVideo = true;
471         boolean hasAudio = timelapse? false: true;
472         checkTracksAndDuration(targetDurMs, hasVideo, hasAudio, fileName, frameRate);
473     }
474 
checkTracksAndDuration( int targetMs, boolean hasVideo, boolean hasAudio, String fileName, float frameRate)475     private void checkTracksAndDuration(
476             int targetMs, boolean hasVideo, boolean hasAudio, String fileName,
477             float frameRate) throws Exception {
478         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
479         retriever.setDataSource(fileName);
480         String hasVideoStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
481         String hasAudioStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
482         assertTrue(hasVideo? hasVideoStr != null : hasVideoStr == null);
483         assertTrue(hasAudio? hasAudioStr != null : hasAudioStr == null);
484         // FIXME:
485         // If we could use fixed frame rate for video recording, we could also do more accurate
486         // check on the duration.
487         String durStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
488         assertTrue(durStr != null);
489         int duration = Integer.parseInt(durStr);
490         assertTrue("duration is non-positive: dur = " + duration, duration > 0);
491         if (targetMs != 0) {
492             float toleranceMs = RECORDED_DUR_TOLERANCE_FRAMES * (1000f / frameRate);
493             assertTrue(String.format("duration is too large: dur = %d, target = %d, tolerance = %f",
494                         duration, targetMs, toleranceMs),
495                     duration <= targetMs + toleranceMs);
496         }
497 
498         retriever.release();
499         retriever = null;
500     }
501 
checkLocationInFile(String fileName)502     private boolean checkLocationInFile(String fileName) {
503         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
504         retriever.setDataSource(fileName);
505         String location = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
506         if (location == null) {
507             retriever.release();
508             Log.v(TAG, "No location information found in file " + fileName);
509             return false;
510         }
511 
512         // parsing String location and recover the location inforamtion in floats
513         // Make sure the tolerance is very small - due to rounding errors?.
514         Log.v(TAG, "location: " + location);
515 
516         // Trim the trailing slash, if any.
517         int lastIndex = location.lastIndexOf('/');
518         if (lastIndex != -1) {
519             location = location.substring(0, lastIndex);
520         }
521 
522         // Get the position of the -/+ sign in location String, which indicates
523         // the beginning of the longtitude.
524         int index = location.lastIndexOf('-');
525         if (index == -1) {
526             index = location.lastIndexOf('+');
527         }
528         assertTrue("+ or - is not found", index != -1);
529         assertTrue("+ or - is only found at the beginning", index != 0);
530         float latitude = Float.parseFloat(location.substring(0, index));
531         float longitude = Float.parseFloat(location.substring(index));
532         assertTrue("Incorrect latitude: " + latitude, Math.abs(latitude - LATITUDE) <= TOLERANCE);
533         assertTrue("Incorrect longitude: " + longitude, Math.abs(longitude - LONGITUDE) <= TOLERANCE);
534         retriever.release();
535         return true;
536     }
537 
checkOutputExist()538     private void checkOutputExist() {
539         assertTrue(mOutFile.exists());
540         assertTrue(mOutFile.length() > 0);
541         assertTrue(mOutFile.delete());
542     }
543 
testRecorderVideo()544     public void testRecorderVideo() throws Exception {
545         if (!hasCamera()) {
546             return;
547         }
548         mCamera = Camera.open(0);
549         setSupportedResolution(mCamera);
550         mCamera.unlock();
551 
552         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
553         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
554         mMediaRecorder.setOutputFile(OUTPUT_PATH2);
555         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
556         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
557         mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
558 
559         FileOutputStream fos = new FileOutputStream(OUTPUT_PATH2);
560         FileDescriptor fd = fos.getFD();
561         mMediaRecorder.setOutputFile(fd);
562         long maxFileSize = MAX_FILE_SIZE * 10;
563         recordMedia(maxFileSize, mOutFile2);
564         assertFalse(checkLocationInFile(OUTPUT_PATH2));
565         fos.close();
566     }
567 
testSetOutputFile()568     public void testSetOutputFile() throws Exception {
569         if (!hasCamera()) {
570             return;
571         }
572         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
573         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
574         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
575         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
576         mMediaRecorder.setOutputFile(mOutFile);
577         long maxFileSize = MAX_FILE_SIZE * 10;
578         recordMedia(maxFileSize, mOutFile);
579     }
580 
testRecordingAudioInRawFormats()581     public void testRecordingAudioInRawFormats() throws Exception {
582         int testsRun = 0;
583         if (hasAmrNb()) {
584             testsRun += testRecordAudioInRawFormat(
585                     MediaRecorder.OutputFormat.AMR_NB,
586                     MediaRecorder.AudioEncoder.AMR_NB);
587         }
588 
589         if (hasAmrWb()) {
590             testsRun += testRecordAudioInRawFormat(
591                     MediaRecorder.OutputFormat.AMR_WB,
592                     MediaRecorder.AudioEncoder.AMR_WB);
593         }
594 
595         if (hasAac()) {
596             testsRun += testRecordAudioInRawFormat(
597                     MediaRecorder.OutputFormat.AAC_ADTS,
598                     MediaRecorder.AudioEncoder.AAC);
599         }
600         if (testsRun == 0) {
601             MediaUtils.skipTest("no audio codecs or microphone");
602         }
603     }
604 
testRecordAudioInRawFormat( int fileFormat, int codec)605     private int testRecordAudioInRawFormat(
606             int fileFormat, int codec) throws Exception {
607         if (!hasMicrophone()) {
608             return 0; // skip
609         }
610         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
611         mMediaRecorder.setOutputFormat(fileFormat);
612         mMediaRecorder.setOutputFile(OUTPUT_PATH);
613         mMediaRecorder.setAudioEncoder(codec);
614         recordMedia(MAX_FILE_SIZE, mOutFile);
615         return 1;
616     }
617 
configureDefaultMediaRecorder()618     private void configureDefaultMediaRecorder() {
619         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
620         mMediaRecorder.setAudioChannels(AUDIO_NUM_CHANNELS);
621         mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
622         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
623         mMediaRecorder.setOutputFile(OUTPUT_PATH);
624         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
625         mMediaRecorder.setMaxFileSize(MAX_FILE_SIZE * 10);
626     }
627 
628     @CddTest(requirement="5.4.4/C-4-1")
testGetActiveMicrophones()629     public void testGetActiveMicrophones() throws Exception {
630         if (!hasMicrophone() || !hasAac()) {
631             MediaUtils.skipTest("no audio codecs or microphone");
632             return;
633         }
634         configureDefaultMediaRecorder();
635         mMediaRecorder.prepare();
636         mMediaRecorder.start();
637         Thread.sleep(1000);
638         List<MicrophoneInfo> activeMicrophones = mMediaRecorder.getActiveMicrophones();
639         assertTrue(activeMicrophones.size() > 0);
640         for (MicrophoneInfo activeMicrophone : activeMicrophones) {
641             printMicrophoneInfo(activeMicrophone);
642         }
643         mMediaRecorder.stop();
644     }
645 
printMicrophoneInfo(MicrophoneInfo microphone)646     private void printMicrophoneInfo(MicrophoneInfo microphone) {
647         Log.i(TAG, "deviceId:" + microphone.getDescription());
648         Log.i(TAG, "portId:" + microphone.getId());
649         Log.i(TAG, "type:" + microphone.getType());
650         Log.i(TAG, "address:" + microphone.getAddress());
651         Log.i(TAG, "deviceLocation:" + microphone.getLocation());
652         Log.i(TAG, "deviceGroup:" + microphone.getGroup()
653             + " index:" + microphone.getIndexInTheGroup());
654         MicrophoneInfo.Coordinate3F position = microphone.getPosition();
655         Log.i(TAG, "position:" + position.x + "," + position.y + "," + position.z);
656         MicrophoneInfo.Coordinate3F orientation = microphone.getOrientation();
657         Log.i(TAG, "orientation:" + orientation.x + "," + orientation.y + "," + orientation.z);
658         Log.i(TAG, "frequencyResponse:" + microphone.getFrequencyResponse());
659         Log.i(TAG, "channelMapping:" + microphone.getChannelMapping());
660         Log.i(TAG, "sensitivity:" + microphone.getSensitivity());
661         Log.i(TAG, "max spl:" + microphone.getMaxSpl());
662         Log.i(TAG, "min spl:" + microphone.getMinSpl());
663         Log.i(TAG, "directionality:" + microphone.getDirectionality());
664         Log.i(TAG, "******");
665     }
666 
testRecordAudioFromAudioSourceUnprocessed()667     public void testRecordAudioFromAudioSourceUnprocessed() throws Exception {
668         if (!hasMicrophone() || !hasAmrNb()) {
669             MediaUtils.skipTest("no audio codecs or microphone");
670             return;
671         }
672         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED);
673         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
674         mMediaRecorder.setOutputFile(OUTPUT_PATH);
675         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
676         recordMedia(MAX_FILE_SIZE, mOutFile);
677     }
678 
testGetAudioSourceMax()679     public void testGetAudioSourceMax() throws Exception {
680         final int max = MediaRecorder.getAudioSourceMax();
681         assertTrue(MediaRecorder.AudioSource.DEFAULT <= max);
682         assertTrue(MediaRecorder.AudioSource.MIC <= max);
683         assertTrue(MediaRecorder.AudioSource.CAMCORDER <= max);
684         assertTrue(MediaRecorder.AudioSource.VOICE_CALL <= max);
685         assertTrue(MediaRecorder.AudioSource.VOICE_COMMUNICATION <= max);
686         assertTrue(MediaRecorder.AudioSource.VOICE_DOWNLINK <= max);
687         assertTrue(MediaRecorder.AudioSource.VOICE_RECOGNITION <= max);
688         assertTrue(MediaRecorder.AudioSource.VOICE_UPLINK <= max);
689         assertTrue(MediaRecorder.AudioSource.UNPROCESSED <= max);
690         assertTrue(MediaRecorder.AudioSource.VOICE_PERFORMANCE <= max);
691     }
692 
testRecorderAudio()693     public void testRecorderAudio() throws Exception {
694         if (!hasMicrophone() || !hasAac()) {
695             MediaUtils.skipTest("no audio codecs or microphone");
696             return;
697         }
698         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
699         assertEquals(0, mMediaRecorder.getMaxAmplitude());
700         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
701         mMediaRecorder.setOutputFile(OUTPUT_PATH);
702         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
703         mMediaRecorder.setAudioChannels(AUDIO_NUM_CHANNELS);
704         mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
705         mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
706         recordMedia(MAX_FILE_SIZE, mOutFile);
707     }
708 
testOnInfoListener()709     public void testOnInfoListener() throws Exception {
710         if (!hasMicrophone() || !hasAac()) {
711             MediaUtils.skipTest("no audio codecs or microphone");
712             return;
713         }
714         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
715         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
716         mMediaRecorder.setMaxDuration(MAX_DURATION_MSEC);
717         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
718         mMediaRecorder.prepare();
719         mMediaRecorder.start();
720         Thread.sleep(RECORD_TIME_MS);
721         assertTrue(mOnInfoCalled);
722     }
723 
testSetMaxDuration()724     public void testSetMaxDuration() throws Exception {
725         if (!hasMicrophone() || !hasAac()) {
726             MediaUtils.skipTest("no audio codecs or microphone");
727             return;
728         }
729         testSetMaxDuration(RECORD_TIME_LONG_MS, RECORDED_DUR_TOLERANCE_MS);
730     }
731 
testSetMaxDuration(long durationMs, long toleranceMs)732     private void testSetMaxDuration(long durationMs, long toleranceMs) throws Exception {
733         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
734         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
735         mMediaRecorder.setMaxDuration((int)durationMs);
736         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
737         mMediaRecorder.prepare();
738         mMediaRecorder.start();
739         long startTimeMs = System.currentTimeMillis();
740         if (!mMaxDurationCond.block(durationMs + toleranceMs)) {
741             fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_DURATION_REACHED");
742         }
743         long endTimeMs = System.currentTimeMillis();
744         long actualDurationMs = endTimeMs - startTimeMs;
745         mMediaRecorder.stop();
746         checkRecordedTime(durationMs, actualDurationMs, toleranceMs);
747     }
748 
checkRecordedTime(long expectedMs, long actualMs, long tolerance)749     private void checkRecordedTime(long expectedMs, long actualMs, long tolerance) {
750         assertEquals(expectedMs, actualMs, tolerance);
751         long actualFileDurationMs = getRecordedFileDurationMs(OUTPUT_PATH);
752         assertEquals(actualFileDurationMs, actualMs, tolerance);
753     }
754 
getRecordedFileDurationMs(final String fileName)755     private int getRecordedFileDurationMs(final String fileName) {
756         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
757         retriever.setDataSource(fileName);
758         String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
759         assertNotNull(durationStr);
760         return Integer.parseInt(durationStr);
761     }
762 
testSetMaxFileSize()763     public void testSetMaxFileSize() throws Exception {
764         testSetMaxFileSize(512 * 1024, 50 * 1024);
765     }
766 
testSetMaxFileSize( long fileSize, long tolerance)767     private void testSetMaxFileSize(
768             long fileSize, long tolerance) throws Exception {
769         if (!hasMicrophone() || !hasCamera() || !hasAmrNb() || !hasH264()) {
770             MediaUtils.skipTest("no microphone, camera, or codecs");
771             return;
772         }
773         mCamera = Camera.open(0);
774         setSupportedResolution(mCamera);
775         mCamera.unlock();
776 
777         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
778         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
779         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
780         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
781         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
782         mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
783         mMediaRecorder.setVideoEncodingBitRate(256000);
784         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
785         mMediaRecorder.setMaxFileSize(fileSize);
786         mMediaRecorder.prepare();
787         mMediaRecorder.start();
788 
789         // Recording a scene with moving objects would greatly help reduce
790         // the time for waiting.
791         if (!mMaxFileSizeCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
792             fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
793         }
794         mMediaRecorder.stop();
795         checkOutputFileSize(OUTPUT_PATH, fileSize, tolerance);
796     }
797 
798     /**
799      * Returns the first codec capable of encoding the specified MIME type, or null if no
800      * match was found.
801      */
getCapsForPreferredCodecForMediaType(String mimeType)802     private static CodecCapabilities getCapsForPreferredCodecForMediaType(String mimeType) {
803         // FIXME: select codecs based on the complete use-case, not just the mime
804         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
805         for (MediaCodecInfo info : mcl.getCodecInfos()) {
806             if (!info.isEncoder()) {
807                 continue;
808             }
809 
810             String[] types = info.getSupportedTypes();
811             for (int j = 0; j < types.length; j++) {
812                 if (types[j].equalsIgnoreCase(mimeType)) {
813                     return info.getCapabilitiesForType(mimeType);
814                 }
815             }
816         }
817         return null;
818     }
819 
820     /**
821      * Generates a frame of data using GL commands.
822      */
generateSurfaceFrame(int frameIndex, int width, int height)823     private void generateSurfaceFrame(int frameIndex, int width, int height) {
824         frameIndex %= 8;
825 
826         int startX, startY;
827         if (frameIndex < 4) {
828             // (0,0) is bottom-left in GL
829             startX = frameIndex * (width / 4);
830             startY = height / 2;
831         } else {
832             startX = (7 - frameIndex) * (width / 4);
833             startY = 0;
834         }
835 
836         GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
837         GLES20.glClearColor(TEST_R0 / 255.0f, TEST_G0 / 255.0f, TEST_B0 / 255.0f, 1.0f);
838         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
839         GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
840         GLES20.glScissor(startX, startY, width / 4, height / 2);
841         GLES20.glClearColor(TEST_R1 / 255.0f, TEST_G1 / 255.0f, TEST_B1 / 255.0f, 1.0f);
842         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
843     }
844 
845     /**
846      * Generates the presentation time for frame N, in microseconds.
847      */
computePresentationTime( long startTimeOffset, int frameIndex, int frameRate)848     private static long computePresentationTime(
849             long startTimeOffset, int frameIndex, int frameRate) {
850         return startTimeOffset + frameIndex * 1000000 / frameRate;
851     }
852 
testLevel(String mediaType, int width, int height, int framerate, int bitrate, int profile, int requestedLevel, int... expectedLevels)853     private void testLevel(String mediaType, int width, int height, int framerate,
854             int bitrate, int profile, int requestedLevel, int... expectedLevels) throws Exception {
855         CodecCapabilities cap = getCapsForPreferredCodecForMediaType(mediaType);
856         if (cap == null) { // not supported
857             return;
858         }
859         MediaCodecInfo.VideoCapabilities vCap = cap.getVideoCapabilities();
860         if (!vCap.areSizeAndRateSupported(width, height, framerate)
861             || !vCap.getBitrateRange().contains(bitrate * 1000)) {
862             Log.i(TAG, "Skip the test");
863             return;
864         }
865 
866         Surface surface = MediaCodec.createPersistentInputSurface();
867         if (surface == null) {
868             return;
869         }
870         InputSurface encSurface = new InputSurface(surface);
871         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
872         mMediaRecorder.setInputSurface(encSurface.getSurface());
873         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
874         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
875         mMediaRecorder.setOutputFile(mOutFile);
876 
877         try {
878             mMediaRecorder.setVideoEncodingProfileLevel(-1, requestedLevel);
879             fail("Expect IllegalArgumentException.");
880         } catch (IllegalArgumentException e) {
881             // Expect exception.
882         }
883         try {
884             mMediaRecorder.setVideoEncodingProfileLevel(profile, -1);
885             fail("Expect IllegalArgumentException.");
886         } catch (IllegalArgumentException e) {
887             // Expect exception.
888         }
889 
890         mMediaRecorder.setVideoEncodingProfileLevel(profile, requestedLevel);
891         mMediaRecorder.setVideoSize(width, height);
892         mMediaRecorder.setVideoEncodingBitRate(bitrate * 1000);
893         mMediaRecorder.setVideoFrameRate(framerate);
894         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
895         mMediaRecorder.prepare();
896         encSurface.updateSize(width, height);
897         mMediaRecorder.start();
898 
899 
900         long startNsec = System.nanoTime();
901         long startTimeOffset =  3000 / framerate;
902         for (int i = 0; i < NUM_FRAMES; i++) {
903             encSurface.makeCurrent();
904             generateSurfaceFrame(i, width, height);
905             long time = startNsec +
906                     computePresentationTime(startTimeOffset, i, framerate) * 1000;
907             encSurface.setPresentationTime(time);
908             encSurface.swapBuffers();
909         }
910 
911         mMediaRecorder.stop();
912 
913         assertTrue(mOutFile.exists());
914         assertTrue(mOutFile.length() > 0);
915 
916         // Verify the recorded file profile/level,
917         MediaExtractor ex = new MediaExtractor();
918         ex.setDataSource(OUTPUT_PATH);
919         for (int i = 0; i < ex.getTrackCount(); i++) {
920             MediaFormat format = ex.getTrackFormat(i);
921             String mime = format.getString(MediaFormat.KEY_MIME);
922             if (mime.startsWith("video/")) {
923                 int finalProfile = format.getInteger(MediaFormat.KEY_PROFILE);
924                 if (!(finalProfile == profile ||
925                         (mediaType.equals(AVC)
926                                 && profile == AVCProfileBaseline
927                                 && finalProfile == AVCProfileConstrainedBaseline) ||
928                         (mediaType.equals(AVC)
929                                 && profile == AVCProfileHigh
930                                 && finalProfile == AVCProfileConstrainedHigh))) {
931                     fail("Incorrect profile: " + finalProfile + " Expected: " + profile);
932                 }
933                 int finalLevel = format.getInteger(MediaFormat.KEY_LEVEL);
934                 boolean match = false;
935                 String expectLvls = new String();
936                 for (int level : expectedLevels) {
937                     expectLvls += level;
938                     if (finalLevel == level) {
939                         match = true;
940                         break;
941                     }
942                 }
943                 if (!match) {
944                     fail("Incorrect Level: " + finalLevel + " Expected: " + expectLvls);
945                 }
946             }
947         }
948         mOutFile.delete();
949         if (encSurface != null) {
950             encSurface.release();
951             encSurface = null;
952         }
953     }
954 
testProfileAvcBaselineLevel1()955     public void testProfileAvcBaselineLevel1() throws Exception {
956         int profile = AVCProfileBaseline;
957 
958         if (!hasH264()) {
959             MediaUtils.skipTest("no Avc codecs");
960             return;
961         }
962 
963         /*              W    H   fps kbps  profile  request level   expected levels */
964         testLevel(AVC, 176, 144, 15, 64,   profile,  AVCLevel1, AVCLevel1);
965         // Enable them when vendor fixes the failure
966         //testLevel(AVC, 178, 144, 15, 64,   profile,  AVCLevel1, AVCLevel11);
967         //testLevel(AVC, 178, 146, 15, 64,   profile,  AVCLevel1, AVCLevel11);
968         //testLevel(AVC, 176, 144, 16, 64,   profile,  AVCLevel1, AVCLevel11);
969         //testLevel(AVC, 176, 144, 15, 65,   profile,  AVCLevel1, AVCLevel1b);
970         testLevel(AVC, 176, 144, 15, 64,   profile,  AVCLevel1b, AVCLevel1,
971                 AVCLevel1b);
972         // testLevel(AVC, 176, 144, 15, 65,   profile,  AVCLevel2, AVCLevel1b,
973         //        AVCLevel11, AVCLevel12, AVCLevel13, AVCLevel2);
974     }
975 
976 
testRecordExceedFileSizeLimit()977     public void testRecordExceedFileSizeLimit() throws Exception {
978         if (!hasMicrophone() || !hasCamera() || !hasAmrNb() || !hasH264()) {
979             MediaUtils.skipTest("no microphone, camera, or codecs");
980             return;
981         }
982         long fileSize = 128 * 1024;
983         long tolerance = 50 * 1024;
984         List<String> recordFileList = new ArrayList<String>();
985         mFileIndex = 0;
986 
987         // Override the handler in setup for test.
988         mMediaRecorder.setOnInfoListener(new OnInfoListener() {
989             public void onInfo(MediaRecorder mr, int what, int extra) {
990                 mOnInfoCalled = true;
991                 if (what ==
992                     MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
993                     Log.v(TAG, "max duration reached");
994                     mMaxDurationCond.open();
995                 } else if (what ==
996                     MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
997                     if (!mExpectMaxFileCond) {
998                         fail("Do not expect MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
999                     } else {
1000                         Log.v(TAG, "max file size reached");
1001                         mMaxFileSizeCond.open();
1002                     }
1003                 } else if (what ==
1004                     MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING) {
1005                     Log.v(TAG, "max file size is approaching");
1006                     mMaxFileSizeApproachingCond.open();
1007 
1008                     // Test passing a read-only FileDescriptor and expect IOException.
1009                     if (mFileIndex == 1) {
1010                         RandomAccessFile file = null;
1011                         try {
1012                             String path = OUTPUT_PATH + '0';
1013                             file = new RandomAccessFile(path, "r");
1014                             mMediaRecorder.setNextOutputFile(file.getFD());
1015                             fail("Expect IOException.");
1016                         } catch (IOException e) {
1017                             // Expected.
1018                         } finally {
1019                             try {
1020                                 file.close();
1021                             } catch (IOException e) {
1022                                 fail("Fail to close file");
1023                             }
1024                         }
1025                     }
1026 
1027                     // Test passing a read-only FileDescriptor and expect IOException.
1028                     if (mFileIndex == 2) {
1029                         ParcelFileDescriptor out = null;
1030                         String path = null;
1031                         try {
1032                             path = OUTPUT_PATH + '0';
1033                             out = ParcelFileDescriptor.open(new File(path),
1034                                     ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_CREATE);
1035                             mMediaRecorder.setNextOutputFile(out.getFileDescriptor());
1036                             fail("Expect IOException.");
1037                         } catch (IOException e) {
1038                             // Expected.
1039                         } finally {
1040                             try {
1041                                 out.close();
1042                             } catch (IOException e) {
1043                                 fail("Fail to close file");
1044                             }
1045                         }
1046                     }
1047 
1048                     // Test passing a write-only FileDescriptor and expect NO IOException.
1049                     if (mFileIndex == 3) {
1050                         try {
1051                             ParcelFileDescriptor out = null;
1052                             String path = OUTPUT_PATH + mFileIndex;
1053                             out = ParcelFileDescriptor.open(new File(path),
1054                                     ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
1055                             mMediaRecorder.setNextOutputFile(out.getFileDescriptor());
1056                             out.close();
1057                             recordFileList.add(path);
1058                             mFileIndex++;
1059                         } catch (IOException e) {
1060                             fail("Fail to set next output file error: " + e);
1061                         }
1062                     } else if (mFileIndex < 6) {
1063                         try {
1064                             String path = OUTPUT_PATH + mFileIndex;
1065                             File nextFile = new File(path);
1066                             mMediaRecorder.setNextOutputFile(nextFile);
1067                             recordFileList.add(path);
1068                             mFileIndex++;
1069                         } catch (IOException e) {
1070                             fail("Fail to set next output file error: " + e);
1071                         }
1072                     }
1073                 } else if (what ==
1074                     MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED) {
1075                     Log.v(TAG, "Next output file started");
1076                     mNextOutputFileStartedCond.open();
1077                 }
1078             }
1079         });
1080         mExpectMaxFileCond = false;
1081         mMediaRecorder.setOutputFile(OUTPUT_PATH + mFileIndex);
1082         recordFileList.add(OUTPUT_PATH + mFileIndex);
1083         mFileIndex++;
1084         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
1085         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1086         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
1087         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
1088         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
1089         mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
1090         mMediaRecorder.setVideoEncodingBitRate(256000);
1091         mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
1092         mMediaRecorder.setMaxFileSize(fileSize);
1093         mMediaRecorder.prepare();
1094         mMediaRecorder.start();
1095 
1096         // Record total 5 files including previous one.
1097         int fileCount = 0;
1098         while (fileCount < 5) {
1099             if (!mMaxFileSizeApproachingCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
1100                 fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING");
1101             }
1102             if (!mNextOutputFileStartedCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
1103                 fail("timed out waiting for MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED");
1104             }
1105             fileCount++;
1106             mMaxFileSizeApproachingCond.close();
1107             mNextOutputFileStartedCond.close();
1108         }
1109 
1110         mExpectMaxFileCond = true;
1111         if (!mMaxFileSizeCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
1112             fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
1113         }
1114         mMediaRecorder.stop();
1115 
1116         for (String filePath : recordFileList) {
1117             checkOutputFileSize(filePath, fileSize, tolerance);
1118         }
1119     }
1120 
checkOutputFileSize(final String fileName, long fileSize, long tolerance)1121     private void checkOutputFileSize(final String fileName, long fileSize, long tolerance) {
1122         File file = new File(fileName);
1123         assertTrue(file.exists());
1124         assertEquals(fileSize, file.length(), tolerance);
1125         assertTrue(file.delete());
1126     }
1127 
testOnErrorListener()1128     public void testOnErrorListener() throws Exception {
1129         if (!hasMicrophone() || !hasAac()) {
1130             MediaUtils.skipTest("no audio codecs or microphone");
1131             return;
1132         }
1133         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
1134         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
1135         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
1136 
1137         recordMedia(MAX_FILE_SIZE, mOutFile);
1138         // TODO: how can we trigger a recording error?
1139         assertFalse(mOnErrorCalled);
1140     }
1141 
setupRecorder(String filename, boolean useSurface, boolean hasAudio)1142     private void setupRecorder(String filename, boolean useSurface, boolean hasAudio)
1143             throws Exception {
1144         int codec = MediaRecorder.VideoEncoder.H264;
1145         int frameRate = getMaxFrameRateForCodec(codec);
1146         if (mMediaRecorder == null) {
1147             mMediaRecorder = new MediaRecorder();
1148         }
1149 
1150         if (!useSurface) {
1151             mCamera = Camera.open(0);
1152             Camera.Parameters params = mCamera.getParameters();
1153             frameRate = params.getPreviewFrameRate();
1154             setSupportedResolution(mCamera);
1155             mCamera.unlock();
1156             mMediaRecorder.setCamera(mCamera);
1157             mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
1158         }
1159 
1160         mMediaRecorder.setVideoSource(useSurface ?
1161                 MediaRecorder.VideoSource.SURFACE : MediaRecorder.VideoSource.CAMERA);
1162 
1163         if (hasAudio) {
1164             mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
1165         }
1166 
1167         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
1168         mMediaRecorder.setOutputFile(filename);
1169 
1170         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
1171         mMediaRecorder.setVideoFrameRate(frameRate);
1172         mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
1173 
1174         if (hasAudio) {
1175             mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
1176         }
1177     }
1178 
tryGetSurface(boolean shouldThrow)1179     private Surface tryGetSurface(boolean shouldThrow) throws Exception {
1180         Surface surface = null;
1181         try {
1182             surface = mMediaRecorder.getSurface();
1183             assertFalse("failed to throw IllegalStateException", shouldThrow);
1184         } catch (IllegalStateException e) {
1185             assertTrue("threw unexpected exception: " + e, shouldThrow);
1186         }
1187         return surface;
1188     }
1189 
validateGetSurface(boolean useSurface)1190     private boolean validateGetSurface(boolean useSurface) {
1191         Log.v(TAG,"validateGetSurface, useSurface=" + useSurface);
1192         if (!useSurface && !hasCamera()) {
1193             // pass if testing camera source but no hardware
1194             return true;
1195         }
1196         Surface surface = null;
1197         boolean success = true;
1198         try {
1199             setupRecorder(OUTPUT_PATH, useSurface, false /* hasAudio */);
1200 
1201             /* Test: getSurface() before prepare()
1202              * should throw IllegalStateException
1203              */
1204             surface = tryGetSurface(true /* shouldThow */);
1205 
1206             mMediaRecorder.prepare();
1207 
1208             /* Test: getSurface() after prepare()
1209              * should succeed for surface source
1210              * should fail for camera source
1211              */
1212             surface = tryGetSurface(!useSurface);
1213 
1214             mMediaRecorder.start();
1215 
1216             /* Test: getSurface() after start()
1217              * should succeed for surface source
1218              * should fail for camera source
1219              */
1220             surface = tryGetSurface(!useSurface);
1221 
1222             try {
1223                 mMediaRecorder.stop();
1224             } catch (Exception e) {
1225                 // stop() could fail if the recording is empty, as we didn't render anything.
1226                 // ignore any failure in stop, we just want it stopped.
1227             }
1228 
1229             /* Test: getSurface() after stop()
1230              * should throw IllegalStateException
1231              */
1232             surface = tryGetSurface(true /* shouldThow */);
1233         } catch (Exception e) {
1234             Log.d(TAG, e.toString());
1235             success = false;
1236         } finally {
1237             // reset to clear states, as stop() might have failed
1238             mMediaRecorder.reset();
1239 
1240             if (mCamera != null) {
1241                 mCamera.release();
1242                 mCamera = null;
1243             }
1244             if (surface != null) {
1245                 surface.release();
1246                 surface = null;
1247             }
1248         }
1249 
1250         return success;
1251     }
1252 
trySetInputSurface(Surface surface)1253     private void trySetInputSurface(Surface surface) throws Exception {
1254         boolean testBadArgument = (surface == null);
1255         try {
1256             mMediaRecorder.setInputSurface(testBadArgument ? new Surface() : surface);
1257             fail("failed to throw exception");
1258         } catch (IllegalArgumentException e) {
1259             // OK only if testing bad arg
1260             assertTrue("threw unexpected exception: " + e, testBadArgument);
1261         } catch (IllegalStateException e) {
1262             // OK only if testing error case other than bad arg
1263             assertFalse("threw unexpected exception: " + e, testBadArgument);
1264         }
1265     }
1266 
validatePersistentSurface(boolean errorCase)1267     private boolean validatePersistentSurface(boolean errorCase) {
1268         Log.v(TAG, "validatePersistentSurface, errorCase=" + errorCase);
1269 
1270         Surface surface = MediaCodec.createPersistentInputSurface();
1271         if (surface == null) {
1272             return false;
1273         }
1274         Surface placeholder = null;
1275 
1276         boolean success = true;
1277         try {
1278             setupRecorder(OUTPUT_PATH, true /* useSurface */, false /* hasAudio */);
1279 
1280             if (errorCase) {
1281                 /*
1282                  * Test: should throw if called with non-persistent surface
1283                  */
1284                 trySetInputSurface(null);
1285             } else {
1286                 /*
1287                  * Test: should succeed if called with a persistent surface before prepare()
1288                  */
1289                 mMediaRecorder.setInputSurface(surface);
1290             }
1291 
1292             /*
1293              * Test: getSurface() should fail before prepare
1294              */
1295             placeholder = tryGetSurface(true /* shouldThow */);
1296 
1297             mMediaRecorder.prepare();
1298 
1299             /*
1300              * Test: setInputSurface() should fail after prepare
1301              */
1302             trySetInputSurface(surface);
1303 
1304             /*
1305              * Test: getSurface() should fail if setInputSurface() succeeded
1306              */
1307             placeholder = tryGetSurface(!errorCase /* shouldThow */);
1308 
1309             mMediaRecorder.start();
1310 
1311             /*
1312              * Test: setInputSurface() should fail after start
1313              */
1314             trySetInputSurface(surface);
1315 
1316             /*
1317              * Test: getSurface() should fail if setInputSurface() succeeded
1318              */
1319             placeholder = tryGetSurface(!errorCase /* shouldThow */);
1320 
1321             try {
1322                 mMediaRecorder.stop();
1323             } catch (Exception e) {
1324                 // stop() could fail if the recording is empty, as we didn't render anything.
1325                 // ignore any failure in stop, we just want it stopped.
1326             }
1327 
1328             /*
1329              * Test: getSurface() should fail after stop
1330              */
1331             placeholder = tryGetSurface(true /* shouldThow */);
1332         } catch (Exception e) {
1333             Log.d(TAG, e.toString());
1334             success = false;
1335         } finally {
1336             // reset to clear states, as stop() might have failed
1337             mMediaRecorder.reset();
1338 
1339             if (mCamera != null) {
1340                 mCamera.release();
1341                 mCamera = null;
1342             }
1343             if (surface != null) {
1344                 surface.release();
1345                 surface = null;
1346             }
1347             if (placeholder != null) {
1348                 placeholder.release();
1349                 placeholder = null;
1350             }
1351         }
1352 
1353         return success;
1354     }
1355 
testGetSurfaceApi()1356     public void testGetSurfaceApi() {
1357         if (!hasH264()) {
1358             MediaUtils.skipTest("no codecs");
1359             return;
1360         }
1361 
1362         if (hasCamera()) {
1363             // validate getSurface() with CAMERA source
1364             assertTrue(validateGetSurface(false /* useSurface */));
1365         }
1366 
1367         // validate getSurface() with SURFACE source
1368         assertTrue(validateGetSurface(true /* useSurface */));
1369     }
1370 
testPersistentSurfaceApi()1371     public void testPersistentSurfaceApi() {
1372         if (!hasH264()) {
1373             MediaUtils.skipTest("no codecs");
1374             return;
1375         }
1376 
1377         // test valid use case
1378         assertTrue(validatePersistentSurface(false /* errorCase */));
1379 
1380         // test invalid use case
1381         assertTrue(validatePersistentSurface(true /* errorCase */));
1382     }
1383 
getMaxFrameRateForCodec(int codec)1384     private static int getMaxFrameRateForCodec(int codec) {
1385         for (VideoEncoderCap cap : mVideoEncoders) {
1386             if (cap.mCodec == codec) {
1387                 return cap.mMaxFrameRate < NORMAL_FPS ? cap.mMaxFrameRate : NORMAL_FPS;
1388             }
1389         }
1390         fail("didn't find max FPS for codec");
1391         return -1;
1392     }
1393 
recordFromSurface( String filename, int captureRate, boolean hasAudio, Surface persistentSurface)1394     private boolean recordFromSurface(
1395             String filename,
1396             int captureRate,
1397             boolean hasAudio,
1398             Surface persistentSurface) {
1399         Log.v(TAG, "recordFromSurface");
1400         Surface surface = null;
1401         try {
1402             setupRecorder(filename, true /* useSurface */, hasAudio);
1403 
1404             int sleepTimeMs;
1405             if (captureRate > 0) {
1406                 mMediaRecorder.setCaptureRate(captureRate);
1407                 sleepTimeMs = 1000 / captureRate;
1408             } else {
1409                 sleepTimeMs = 1000 / getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H264);
1410             }
1411 
1412             if (persistentSurface != null) {
1413                 Log.v(TAG, "using persistent surface");
1414                 surface = persistentSurface;
1415                 mMediaRecorder.setInputSurface(surface);
1416             }
1417 
1418             mMediaRecorder.prepare();
1419 
1420             if (persistentSurface == null) {
1421                 surface = mMediaRecorder.getSurface();
1422             }
1423 
1424             Paint paint = new Paint();
1425             paint.setTextSize(16);
1426             paint.setColor(Color.RED);
1427             int i;
1428 
1429             /* Test: draw 10 frames at 30fps before start
1430              * these should be dropped and not causing malformed stream.
1431              */
1432             for(i = 0; i < 10; i++) {
1433                 Canvas canvas = surface.lockCanvas(null);
1434                 int background = (i * 255 / 99);
1435                 canvas.drawARGB(255, background, background, background);
1436                 String text = "Frame #" + i;
1437                 canvas.drawText(text, 50, 50, paint);
1438                 surface.unlockCanvasAndPost(canvas);
1439                 Thread.sleep(sleepTimeMs);
1440             }
1441 
1442             Log.v(TAG, "start");
1443             mMediaRecorder.start();
1444 
1445             /* Test: draw another 90 frames at 30fps after start */
1446             for(i = 10; i < 100; i++) {
1447                 Canvas canvas = surface.lockCanvas(null);
1448                 int background = (i * 255 / 99);
1449                 canvas.drawARGB(255, background, background, background);
1450                 String text = "Frame #" + i;
1451                 canvas.drawText(text, 50, 50, paint);
1452                 surface.unlockCanvasAndPost(canvas);
1453                 Thread.sleep(sleepTimeMs);
1454             }
1455 
1456             Log.v(TAG, "stop");
1457             mMediaRecorder.stop();
1458         } catch (Exception e) {
1459             Log.v(TAG, "record video failed: " + e.toString());
1460             return false;
1461         } finally {
1462             // We need to test persistent surface across multiple MediaRecorder
1463             // instances, so must destroy mMediaRecorder here.
1464             if (mMediaRecorder != null) {
1465                 mMediaRecorder.release();
1466                 mMediaRecorder = null;
1467             }
1468 
1469             // release surface if not using persistent surface
1470             if (persistentSurface == null && surface != null) {
1471                 surface.release();
1472                 surface = null;
1473             }
1474         }
1475         return true;
1476     }
1477 
checkCaptureFps(String filename, int captureRate)1478     private boolean checkCaptureFps(String filename, int captureRate) {
1479         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
1480 
1481         retriever.setDataSource(filename);
1482 
1483         // verify capture rate meta key is present and correct
1484         String captureFps = retriever.extractMetadata(
1485                 MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
1486 
1487         if (captureFps == null) {
1488             Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is missing");
1489             return false;
1490         }
1491 
1492         if (Math.abs(Float.parseFloat(captureFps) - captureRate) > 0.001) {
1493             Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is incorrect: "
1494                     + captureFps + "vs. " + captureRate);
1495             return false;
1496         }
1497 
1498         // verify other meta keys here if necessary
1499         return true;
1500     }
1501 
testRecordFromSurface(boolean persistent, boolean timelapse)1502     private boolean testRecordFromSurface(boolean persistent, boolean timelapse) {
1503         Log.v(TAG, "testRecordFromSurface: " +
1504                    "persistent=" + persistent + ", timelapse=" + timelapse);
1505         boolean success = false;
1506         Surface surface = null;
1507         int noOfFailure = 0;
1508 
1509         if (!hasH264()) {
1510             MediaUtils.skipTest("no codecs");
1511             return true;
1512         }
1513 
1514         final float frameRate = getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H264);
1515 
1516         try {
1517             if (persistent) {
1518                 surface = MediaCodec.createPersistentInputSurface();
1519             }
1520 
1521             for (int k = 0; k < 2; k++) {
1522                 String filename = (k == 0) ? OUTPUT_PATH : OUTPUT_PATH2;
1523                 boolean hasAudio = false;
1524                 int captureRate = 0;
1525 
1526                 if (timelapse) {
1527                     // if timelapse/slow-mo, k chooses between low/high capture fps
1528                     captureRate = (k == 0) ? TIME_LAPSE_FPS : SLOW_MOTION_FPS;
1529                 } else {
1530                     // otherwise k chooses between no-audio and audio
1531                     hasAudio = (k == 0) ? false : true;
1532                 }
1533 
1534                 if (hasAudio && (!hasMicrophone() || !hasAmrNb())) {
1535                     // audio test waived if no audio support
1536                     continue;
1537                 }
1538 
1539                 Log.v(TAG, "testRecordFromSurface - round " + k);
1540                 success = recordFromSurface(filename, captureRate, hasAudio, surface);
1541                 if (success) {
1542                     checkTracksAndDuration(0, true /* hasVideo */, hasAudio, filename, frameRate);
1543 
1544                     // verify capture fps meta key
1545                     if (timelapse && !checkCaptureFps(filename, captureRate)) {
1546                         noOfFailure++;
1547                     }
1548                 }
1549                 if (!success) {
1550                     noOfFailure++;
1551                 }
1552             }
1553         } catch (Exception e) {
1554             Log.v(TAG, e.toString());
1555             noOfFailure++;
1556         } finally {
1557             if (surface != null) {
1558                 Log.v(TAG, "releasing persistent surface");
1559                 surface.release();
1560                 surface = null;
1561             }
1562         }
1563         return (noOfFailure == 0);
1564     }
1565 
1566     // Test recording from surface source with/without audio)
testSurfaceRecording()1567     public void testSurfaceRecording() {
1568         assertTrue(testRecordFromSurface(false /* persistent */, false /* timelapse */));
1569     }
1570 
1571     // Test recording from persistent surface source with/without audio
testPersistentSurfaceRecording()1572     public void testPersistentSurfaceRecording() {
1573         assertTrue(testRecordFromSurface(true /* persistent */, false /* timelapse */));
1574     }
1575 
1576     // Test timelapse recording from surface without audio
testSurfaceRecordingTimeLapse()1577     public void testSurfaceRecordingTimeLapse() {
1578         assertTrue(testRecordFromSurface(false /* persistent */, true /* timelapse */));
1579     }
1580 
1581     // Test timelapse recording from persisent surface without audio
testPersistentSurfaceRecordingTimeLapse()1582     public void testPersistentSurfaceRecordingTimeLapse() {
1583         assertTrue(testRecordFromSurface(true /* persistent */, true /* timelapse */));
1584     }
1585 
recordMedia(long maxFileSize, File outFile)1586     private void recordMedia(long maxFileSize, File outFile) throws Exception {
1587         mMediaRecorder.setMaxFileSize(maxFileSize);
1588         mMediaRecorder.prepare();
1589         mMediaRecorder.start();
1590         Thread.sleep(RECORD_TIME_MS);
1591         mMediaRecorder.stop();
1592 
1593         assertTrue(outFile.exists());
1594 
1595         // The max file size is always guaranteed.
1596         // We just make sure that the margin is not too big
1597         assertTrue(outFile.length() < 1.1 * maxFileSize);
1598         assertTrue(outFile.length() > 0);
1599     }
1600 
hasCamera()1601     private boolean hasCamera() {
1602         return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
1603     }
1604 
hasMicrophone()1605     private boolean hasMicrophone() {
1606         return mActivity.getPackageManager().hasSystemFeature(
1607                 PackageManager.FEATURE_MICROPHONE);
1608     }
1609 
hasAmrNb()1610     private static boolean hasAmrNb() {
1611         return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
1612     }
1613 
hasAmrWb()1614     private static boolean hasAmrWb() {
1615         return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
1616     }
1617 
hasAac()1618     private static boolean hasAac() {
1619         return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC);
1620     }
1621 
hasH264()1622     private static boolean hasH264() {
1623         return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_VIDEO_AVC);
1624     }
1625 
testSetCaptureRate()1626     public void testSetCaptureRate() throws Exception {
1627         // No exception expected for 30fps
1628         mMediaRecorder.setCaptureRate(30.0);
1629         try {
1630             mMediaRecorder.setCaptureRate(-1.0);
1631             fail("Should fail setting negative fps");
1632         } catch (Exception ex) {
1633             // expected
1634         }
1635         // No exception expected for 1/24hr
1636         mMediaRecorder.setCaptureRate(1.0 / 86400.0);
1637         try {
1638             mMediaRecorder.setCaptureRate(1.0 / 90000.0);
1639             fail("Should fail setting smaller fps than one frame per day");
1640         } catch (Exception ex) {
1641             // expected
1642         }
1643         try {
1644             mMediaRecorder.setCaptureRate(0);
1645             fail("Should fail setting zero fps");
1646         } catch (Exception ex) {
1647             // expected
1648         }
1649     }
1650 
testAudioRecordInfoCallback()1651     public void testAudioRecordInfoCallback() throws Exception {
1652         if (!hasMicrophone() || !hasAac()) {
1653             MediaUtils.skipTest("no audio codecs or microphone");
1654             return;
1655         }
1656         AudioRecordingConfigurationTest.MyAudioRecordingCallback callback =
1657                 new AudioRecordingConfigurationTest.MyAudioRecordingCallback(
1658                         0 /*unused*/, MediaRecorder.AudioSource.DEFAULT);
1659         mMediaRecorder.registerAudioRecordingCallback(mExec, callback);
1660         configureDefaultMediaRecorder();
1661         mMediaRecorder.prepare();
1662         mMediaRecorder.start();
1663         callback.await(TEST_TIMING_TOLERANCE_MS);
1664         assertTrue(callback.mCalled);
1665         assertTrue(callback.mConfigs.size() <= 1);
1666         if (callback.mConfigs.size() == 1) {
1667             checkRecordingConfig(callback.mConfigs.get(0));
1668         }
1669         Thread.sleep(RECORD_TIME_MS);
1670         mMediaRecorder.stop();
1671         mMediaRecorder.unregisterAudioRecordingCallback(callback);
1672     }
1673 
testGetActiveRecordingConfiguration()1674     public void testGetActiveRecordingConfiguration() throws Exception {
1675         if (!hasMicrophone() || !hasAac()) {
1676             MediaUtils.skipTest("no audio codecs or microphone");
1677             return;
1678         }
1679         configureDefaultMediaRecorder();
1680         mMediaRecorder.prepare();
1681         mMediaRecorder.start();
1682         Thread.sleep(1000);
1683         AudioRecordingConfiguration config = mMediaRecorder.getActiveRecordingConfiguration();
1684         checkRecordingConfig(config);
1685         mMediaRecorder.stop();
1686     }
1687 
1688     private Executor mExec = new Executor() {
1689         @Override
1690         public void execute(Runnable command) {
1691             command.run();
1692         }
1693     };
1694 
checkRecordingConfig(AudioRecordingConfiguration config)1695     private static void checkRecordingConfig(AudioRecordingConfiguration config) {
1696         assertNotNull(config);
1697         AudioFormat format = config.getClientFormat();
1698         assertEquals(AUDIO_NUM_CHANNELS, format.getChannelCount());
1699         assertEquals(AUDIO_SAMPLE_RATE_HZ, format.getSampleRate());
1700         assertEquals(MediaRecorder.AudioSource.MIC, config.getAudioSource());
1701         assertNotNull(config.getAudioDevice());
1702         assertNotNull(config.getClientEffects());
1703         assertNotNull(config.getEffects());
1704         // no requirement here, just testing the API
1705         config.isClientSilenced();
1706     }
1707 
1708 
1709 }
1710