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