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