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 139 @TargetApi(Build.VERSION_CODES.HONEYCOMB) prepareVideoRecorder()140 private boolean prepareVideoRecorder(){ 141 142 // BEGIN_INCLUDE (configure_preview) 143 mCamera = CameraHelper.getDefaultCameraInstance(); 144 145 // We need to make sure that our preview and recording video size are supported by the 146 // camera. Query camera to find all the sizes and choose the optimal size given the 147 // dimensions of our preview surface. 148 Camera.Parameters parameters = mCamera.getParameters(); 149 List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); 150 List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes(); 151 Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes, 152 mSupportedPreviewSizes, mPreview.getWidth(), mPreview.getHeight()); 153 154 // Use the same size for recording profile. 155 CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); 156 profile.videoFrameWidth = optimalSize.width; 157 profile.videoFrameHeight = optimalSize.height; 158 159 // likewise for the camera object itself. 160 parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight); 161 mCamera.setParameters(parameters); 162 try { 163 // Requires API level 11+, For backward compatibility use {@link setPreviewDisplay} 164 // with {@link SurfaceView} 165 mCamera.setPreviewTexture(mPreview.getSurfaceTexture()); 166 } catch (IOException e) { 167 Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage()); 168 return false; 169 } 170 // END_INCLUDE (configure_preview) 171 172 173 // BEGIN_INCLUDE (configure_media_recorder) 174 mMediaRecorder = new MediaRecorder(); 175 176 // Step 1: Unlock and set camera to MediaRecorder 177 mCamera.unlock(); 178 mMediaRecorder.setCamera(mCamera); 179 180 // Step 2: Set sources 181 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT ); 182 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 183 184 // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) 185 mMediaRecorder.setProfile(profile); 186 187 // Step 4: Set output file 188 mOutputFile = CameraHelper.getOutputMediaFile(CameraHelper.MEDIA_TYPE_VIDEO); 189 if (mOutputFile == null) { 190 return false; 191 } 192 mMediaRecorder.setOutputFile(mOutputFile.getPath()); 193 // END_INCLUDE (configure_media_recorder) 194 195 // Step 5: Prepare configured MediaRecorder 196 try { 197 mMediaRecorder.prepare(); 198 } catch (IllegalStateException e) { 199 Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); 200 releaseMediaRecorder(); 201 return false; 202 } catch (IOException e) { 203 Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); 204 releaseMediaRecorder(); 205 return false; 206 } 207 return true; 208 } 209 210 /** 211 * Asynchronous task for preparing the {@link android.media.MediaRecorder} since it's a long blocking 212 * operation. 213 */ 214 class MediaPrepareTask extends AsyncTask<Void, Void, Boolean> { 215 216 @Override doInBackground(Void... voids)217 protected Boolean doInBackground(Void... voids) { 218 // initialize video camera 219 if (prepareVideoRecorder()) { 220 // Camera is available and unlocked, MediaRecorder is prepared, 221 // now you can start recording 222 mMediaRecorder.start(); 223 224 isRecording = true; 225 } else { 226 // prepare didn't work, release the camera 227 releaseMediaRecorder(); 228 return false; 229 } 230 return true; 231 } 232 233 @Override onPostExecute(Boolean result)234 protected void onPostExecute(Boolean result) { 235 if (!result) { 236 MainActivity.this.finish(); 237 } 238 // inform the user that recording has started 239 setCaptureButtonText("Stop"); 240 241 } 242 } 243 244 } 245