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