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