Skip to content

Most visited

Recently visited

navigation
Camera2Video / src / com.example.android.camera2video /

Camera2VideoFragment.java

1
/*
2
 * Copyright 2014 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.camera2video;
18
 
19
import android.Manifest;
20
import android.app.Activity;
21
import android.app.AlertDialog;
22
import android.app.Dialog;
23
import android.app.DialogFragment;
24
import android.app.Fragment;
25
import android.content.Context;
26
import android.content.DialogInterface;
27
import android.content.pm.PackageManager;
28
import android.content.res.Configuration;
29
import android.graphics.Matrix;
30
import android.graphics.RectF;
31
import android.graphics.SurfaceTexture;
32
import android.hardware.camera2.CameraAccessException;
33
import android.hardware.camera2.CameraCaptureSession;
34
import android.hardware.camera2.CameraCharacteristics;
35
import android.hardware.camera2.CameraDevice;
36
import android.hardware.camera2.CameraManager;
37
import android.hardware.camera2.CameraMetadata;
38
import android.hardware.camera2.CaptureRequest;
39
import android.hardware.camera2.params.StreamConfigurationMap;
40
import android.media.MediaRecorder;
41
import android.os.Bundle;
42
import android.os.Handler;
43
import android.os.HandlerThread;
44
import android.support.annotation.NonNull;
45
import android.support.v13.app.FragmentCompat;
46
import android.support.v4.app.ActivityCompat;
47
import android.util.Log;
48
import android.util.Size;
49
import android.util.SparseIntArray;
50
import android.view.LayoutInflater;
51
import android.view.Surface;
52
import android.view.TextureView;
53
import android.view.View;
54
import android.view.ViewGroup;
55
import android.widget.Button;
56
import android.widget.Toast;
57
 
58
import java.io.IOException;
59
import java.util.ArrayList;
60
import java.util.Arrays;
61
import java.util.Collections;
62
import java.util.Comparator;
63
import java.util.List;
64
import java.util.concurrent.Semaphore;
65
import java.util.concurrent.TimeUnit;
66
 
67
public class Camera2VideoFragment extends Fragment
68
        implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback {
69
 
70
    private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
71
    private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
72
    private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
73
    private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
74
 
75
    private static final String TAG = "Camera2VideoFragment";
76
    private static final int REQUEST_VIDEO_PERMISSIONS = 1;
77
    private static final String FRAGMENT_DIALOG = "dialog";
78
 
79
    private static final String[] VIDEO_PERMISSIONS = {
80
            Manifest.permission.CAMERA,
81
            Manifest.permission.RECORD_AUDIO,
82
    };
83
 
84
    static {
85
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
86
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
87
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
88
        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
89
    }
90
 
91
    static {
92
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
93
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
94
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
95
        INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
96
    }
97
 
98
    /**
99
     * An {@link AutoFitTextureView} for camera preview.
100
     */
101
    private AutoFitTextureView mTextureView;
102
 
103
    /**
104
     * Button to record video
105
     */
106
    private Button mButtonVideo;
107
 
108
    /**
109
     * A refernce to the opened {@link android.hardware.camera2.CameraDevice}.
110
     */
111
    private CameraDevice mCameraDevice;
112
 
113
    /**
114
     * A reference to the current {@link android.hardware.camera2.CameraCaptureSession} for
115
     * preview.
116
     */
117
    private CameraCaptureSession mPreviewSession;
118
 
119
    /**
120
     * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
121
     * {@link TextureView}.
122
     */
123
    private TextureView.SurfaceTextureListener mSurfaceTextureListener
124
            = new TextureView.SurfaceTextureListener() {
125
 
126
        @Override
127
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
128
                                              int width, int height) {
129
            openCamera(width, height);
130
        }
131
 
132
        @Override
133
        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
134
                                                int width, int height) {
135
            configureTransform(width, height);
136
        }
137
 
138
        @Override
139
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
140
            return true;
141
        }
142
 
143
        @Override
144
        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
145
        }
146
 
147
    };
148
 
149
    /**
150
     * The {@link android.util.Size} of camera preview.
151
     */
152
    private Size mPreviewSize;
153
 
154
    /**
155
     * The {@link android.util.Size} of video recording.
156
     */
157
    private Size mVideoSize;
158
 
159
    /**
160
     * MediaRecorder
161
     */
162
    private MediaRecorder mMediaRecorder;
163
 
164
    /**
165
     * Whether the app is recording video now
166
     */
167
    private boolean mIsRecordingVideo;
168
 
169
    /**
170
     * An additional thread for running tasks that shouldn't block the UI.
171
     */
172
    private HandlerThread mBackgroundThread;
173
 
174
    /**
175
     * A {@link Handler} for running tasks in the background.
176
     */
177
    private Handler mBackgroundHandler;
178
 
179
    /**
180
     * A {@link Semaphore} to prevent the app from exiting before closing the camera.
181
     */
182
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);
183
 
184
    /**
185
     * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its status.
186
     */
187
    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
188
 
189
        @Override
190
        public void onOpened(CameraDevice cameraDevice) {
191
            mCameraDevice = cameraDevice;
192
            startPreview();
193
            mCameraOpenCloseLock.release();
194
            if (null != mTextureView) {
195
                configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
196
            }
197
        }
198
 
199
        @Override
200
        public void onDisconnected(CameraDevice cameraDevice) {
201
            mCameraOpenCloseLock.release();
202
            cameraDevice.close();
203
            mCameraDevice = null;
204
        }
205
 
206
        @Override
207
        public void onError(CameraDevice cameraDevice, int error) {
208
            mCameraOpenCloseLock.release();
209
            cameraDevice.close();
210
            mCameraDevice = null;
211
            Activity activity = getActivity();
212
            if (null != activity) {
213
                activity.finish();
214
            }
215
        }
216
 
217
    };
218
    private Integer mSensorOrientation;
219
    private String mNextVideoAbsolutePath;
220
    private CaptureRequest.Builder mPreviewBuilder;
221
    private Surface mRecorderSurface;
222
 
223
    public static Camera2VideoFragment newInstance() {
224
        return new Camera2VideoFragment();
225
    }
226
 
227
    /**
228
     * In this sample, we choose a video size with 3x4 aspect ratio. Also, we don't use sizes
229
     * larger than 1080p, since MediaRecorder cannot handle such a high-resolution video.
230
     *
231
     * @param choices The list of available sizes
232
     * @return The video size
233
     */
234
    private static Size chooseVideoSize(Size[] choices) {
235
        for (Size size : choices) {
236
            if (size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080) {
237
                return size;
238
            }
239
        }
240
        Log.e(TAG, "Couldn't find any suitable video size");
241
        return choices[choices.length - 1];
242
    }
243
 
244
    /**
245
     * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
246
     * width and height are at least as large as the respective requested values, and whose aspect
247
     * ratio matches with the specified value.
248
     *
249
     * @param choices     The list of sizes that the camera supports for the intended output class
250
     * @param width       The minimum desired width
251
     * @param height      The minimum desired height
252
     * @param aspectRatio The aspect ratio
253
     * @return The optimal {@code Size}, or an arbitrary one if none were big enough
254
     */
255
    private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
256
        // Collect the supported resolutions that are at least as big as the preview Surface
257
        List<Size> bigEnough = new ArrayList<Size>();
258
        int w = aspectRatio.getWidth();
259
        int h = aspectRatio.getHeight();
260
        for (Size option : choices) {
261
            if (option.getHeight() == option.getWidth() * h / w &&
262
                    option.getWidth() >= width && option.getHeight() >= height) {
263
                bigEnough.add(option);
264
            }
265
        }
266
 
267
        // Pick the smallest of those, assuming we found any
268
        if (bigEnough.size() > 0) {
269
            return Collections.min(bigEnough, new CompareSizesByArea());
270
        } else {
271
            Log.e(TAG, "Couldn't find any suitable preview size");
272
            return choices[0];
273
        }
274
    }
275
 
276
    @Override
277
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
278
                             Bundle savedInstanceState) {
279
        return inflater.inflate(R.layout.fragment_camera2_video, container, false);
280
    }
281
 
282
    @Override
283
    public void onViewCreated(final View view, Bundle savedInstanceState) {
284
        mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);
285
        mButtonVideo = (Button) view.findViewById(R.id.video);
286
        mButtonVideo.setOnClickListener(this);
287
        view.findViewById(R.id.info).setOnClickListener(this);
288
    }
289
 
290
    @Override
291
    public void onResume() {
292
        super.onResume();
293
        startBackgroundThread();
294
        if (mTextureView.isAvailable()) {
295
            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
296
        } else {
297
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
298
        }
299
    }
300
 
301
    @Override
302
    public void onPause() {
303
        closeCamera();
304
        stopBackgroundThread();
305
        super.onPause();
306
    }
307
 
308
    @Override
309
    public void onClick(View view) {
310
        switch (view.getId()) {
311
            case R.id.video: {
312
                if (mIsRecordingVideo) {
313
                    stopRecordingVideo();
314
                } else {
315
                    startRecordingVideo();
316
                }
317
                break;
318
            }
319
            case R.id.info: {
320
                Activity activity = getActivity();
321