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