1 /* 2 * Copyright (C) 2013 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 17 package com.example.android.mediarecorder; 18 19 import android.annotation.TargetApi; 20 import android.app.Activity; 21 import android.hardware.Camera; 22 import android.media.CamcorderProfile; 23 import android.media.MediaRecorder; 24 import android.os.AsyncTask; 25 import android.os.Build; 26 import android.os.Bundle; 27 import android.util.Log; 28 import android.view.Menu; 29 import android.view.TextureView; 30 import android.view.View; 31 import android.widget.Button; 32 33 import com.example.android.common.media.CameraHelper; 34 35 import java.io.File; 36 import java.io.IOException; 37 import java.util.List; 38 39 /** 40 * This activity uses the camera/camcorder as the A/V source for the {@link android.media.MediaRecorder} API. 41 * A {@link android.view.TextureView} is used as the camera preview which limits the code to API 14+. This 42 * can be easily replaced with a {@link android.view.SurfaceView} to run on older devices. 43 */ 44 public class MainActivity extends Activity { 45 46 private Camera mCamera; 47 private TextureView mPreview; 48 private MediaRecorder mMediaRecorder; 49 private File mOutputFile; 50 51 private boolean isRecording = false; 52 private static final String TAG = "Recorder"; 53 private Button captureButton; 54 55 @Override onCreate(Bundle savedInstanceState)56 protected void onCreate(Bundle savedInstanceState) { 57 super.onCreate(savedInstanceState); 58 setContentView(R.layout.sample_main); 59 60 mPreview = (TextureView) findViewById(R.id.surface_view); 61 captureButton = (Button) findViewById(R.id.button_capture); 62 } 63 64 /** 65 * The capture button controls all user interaction. When recording, the button click 66 * stops recording, releases {@link android.media.MediaRecorder} and {@link android.hardware.Camera}. When not recording, 67 * it prepares the {@link android.media.MediaRecorder} and starts recording. 68 * 69 * @param view the view generating the event. 70 */ onCaptureClick(View view)71 public void onCaptureClick(View view) { 72 if (isRecording) { 73 // BEGIN_INCLUDE(stop_release_media_recorder) 74 75 // stop recording and release camera 76 try { 77 mMediaRecorder.stop(); // stop the recording 78 } catch (RuntimeException e) { 79 // RuntimeException is thrown when stop() is called immediately after start(). 80 // In this case the output file is not properly constructed ans should be deleted. 81 Log.d(TAG, "RuntimeException: stop() is called immediately after start()"); 82 //noinspection ResultOfMethodCallIgnored 83 mOutputFile.delete(); 84 } 85 releaseMediaRecorder(); // release the MediaRecorder object 86 mCamera.lock(); // take camera access back from MediaRecorder 87 88 // inform the user that recording has stopped 89 setCaptureButtonText("Capture"); 90 isRecording = false; 91 releaseCamera(); 92 // END_INCLUDE(stop_release_media_recorder) 93 94 } else { 95 96 // BEGIN_INCLUDE(prepare_start_media_recorder) 97 98 new MediaPrepareTask().execute(null, null, null); 99 100 // END_INCLUDE(prepare_start_media_recorder) 101 102 } 103 } 104 setCaptureButtonText(String title)105 private void setCaptureButtonText(String title) { 106 captureButton.setText(title); 107 } 108 109 @Override onPause()110 protected void onPause() { 111 super.onPause(); 112 // if we are using MediaRecorder, release it first 113 releaseMediaRecorder(); 114 // release the camera immediately on pause event 115 releaseCamera(); 116 } 117 releaseMediaRecorder()118 private void releaseMediaRecorder(){ 119 if (mMediaRecorder != null) { 120 // clear recorder configuration 121 mMediaRecorder.reset(); 122 // release the recorder object 123 mMediaRecorder.release(); 124 mMediaRecorder = null; 125 // Lock camera for later use i.e taking it back from MediaRecorder. 126 // MediaRecorder doesn't need it anymore and we will release it if the activity pauses. 127 mCamera.lock(); 128 } 129 } 130 releaseCamera()131 private void releaseCamera(){ 132 if (mCamera != null){ 133 // release the camera for other applications 134 mCamera.release(); 135 mCamera = null; 136 } 137 } 138 prepareVideoRecorder()139 private boolean prepareVideoRecorder(){ 140 141 // BEGIN_INCLUDE (configure_preview) 142 mCamera = CameraHelper.getDefaultCameraInstance(); 143 144 // We need to make sure that our preview and recording video size are supported by the 145 // camera. Query camera to find all the sizes and choose the optimal size given the 146 // dimensions of our preview surface. 147 Camera.Parameters parameters = mCamera.getParameters(); 148 List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); 149 List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes(); 150 Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes, 151 mSupportedPreviewSizes, mPreview.getWidth(), mPreview.getHeight()); 152 153 // Use the same size for recording profile. 154 CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); 155 profile.videoFrameWidth = optimalSize.width; 156 profile.videoFrameHeight = optimalSize.height; 157 158 // likewise for the camera object itself. 159 parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight); 160 mCamera.setParameters(parameters); 161 try { 162 // Requires API level 11+, For backward compatibility use {@link setPreviewDisplay} 163 // with {@link SurfaceView} 164 mCamera.setPreviewTexture(mPreview.getSurfaceTexture()); 165 } catch (IOException e) { 166 Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage()); 167 return false; 168 } 169 // END_INCLUDE (configure_preview) 170 171 172 // BEGIN_INCLUDE (configure_media_recorder) 173 mMediaRecorder = new MediaRecorder(); 174 175 // Step 1: Unlock and set camera to MediaRecorder 176 mCamera.unlock(); 177 mMediaRecorder.setCamera(mCamera); 178 179 // Step 2: Set sources 180 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT ); 181 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 182 183 // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) 184 mMediaRecorder.setProfile(profile); 185 186 // Step 4: Set output file 187 mOutputFile = CameraHelper.getOutputMediaFile(CameraHelper.MEDIA_TYPE_VIDEO); 188 if (mOutputFile == null) { 189 return false; 190 } 191 mMediaRecorder.setOutputFile(mOutputFile.getPath()); 192 // END_INCLUDE (configure_media_recorder) 193 194 // Step 5: Prepare configured MediaRecorder 195 try { 196 mMediaRecorder.prepare(); 197 } catch (IllegalStateException e) { 198 Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); 199 releaseMediaRecorder(); 200 return false; 201 } catch (IOException e) { 202 Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); 203 releaseMediaRecorder(); 204 return false; 205 } 206 return true; 207 } 208 209 /** 210 * Asynchronous task for preparing the {@link android.media.MediaRecorder} since it's a long blocking 211 * operation. 212 */ 213 class MediaPrepareTask extends AsyncTask<Void, Void, Boolean> { 214 215 @Override doInBackground(Void... voids)216 protected Boolean doInBackground(Void... voids) { 217 // initialize video camera 218 if (prepareVideoRecorder()) { 219 // Camera is available and unlocked, MediaRecorder is prepared, 220 // now you can start recording 221 mMediaRecorder.start(); 222 223 isRecording = true; 224 } else { 225 // prepare didn't work, release the camera 226 releaseMediaRecorder(); 227 return false; 228 } 229 return true; 230 } 231 232 @Override onPostExecute(Boolean result)233 protected void onPostExecute(Boolean result) { 234 if (!result) { 235 MainActivity.this.finish(); 236 } 237 // inform the user that recording has started 238 setCaptureButtonText("Stop"); 239 240 } 241 } 242 243 } 244