Android APP Camera2应用(04)录像&保存视频流程

时间:2025-01-31 10:28:52

说明:camera子系统 系列文章针对Android10.0系统,主要针对 camera API2 + HAL3 框架进行解读。

1 录像&保存视频流程简要解读

@1 当预览创建之后,点击录像 button,触发录像事件,首先是停止预览,准备切换到录制视频模式,该部分关键代码如下所示:

private void closeCameraCaptureSession() {
    if (mCaptureSession != null) {
        (TAG, "close Session:" + ());
        ();
        mCaptureSession = null;
    }
}
//...
//camera record process,step1 停止预览,准备切换到录制视频
try {
    ();
    closeCameraCaptureSession();
} catch (CameraAccessException e) {
    ();
}

@2 创建 MediaRecorder 实例,并初始化相关设置,,关键代码如下所示:

//camera record process,step2 mMediaRecorder相关设置
mVideoFile = new File((),new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) +"demo.mp4");
();//设置音频来源
();//设置视频来源
(.MPEG_4);//设置输出格式
();//设置音频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择AAC
();//设置视频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择H264
(8*1024*1920);//设置比特率 一般是 1*分辨率 到 10*分辨率之间波动。比特率越大视频越清晰但是视频文件也越大。
(30);//设置帧数.
((),());
(90);
Surface surface = new Surface(());
(surface);
(());
try {
    ();
} catch (IOException e) {
    ();
}

@3 调用 (CameraDevice.TEMPLATE_RECORED)方法, 为新的捕获请求创建一个  对象,并用CameraDevice.TEMPLATE_RECORED 参数初始化,关键代码如下所示:

//camera record process,step3 创建 对象,并用CameraDevice.TEMPLATE_RECORED 参数初始化
mPreviewRequestBuilder = (CameraDevice.TEMPLATE_RECORD);
(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

@4 调用 ()方法,将MediaRecorder和预览用的Surface实例添加到该请求的目标列表中,关键代码如下所示:

SurfaceTexture surfaceTexture = ();
((),());
Surface previewSurface = new Surface(surfaceTexture);
Surface recorderSurface = ();//从获取录制视频需要的Surface

//camera record process,step4 将MediaRecorder和预览用的Surface实例添加到该请求的目标列表中
(previewSurface);
(recorderSurface);

@5 执行 方法,通过提供目标输出集来创建新 的捕获会话,该方法传入三个参数:

  • List<Surface>:新的用于捕获图像信息的 Surface 集合,此处为显示预览 信息的 surface 实例,以及记录图像信息用的 MediaRecorder 的实例
  • :用于通知新捕获 session 的callback
  • Handler:为一个句柄,代表执行 callback 的 handler,如果程序希望直 接在当前线程中执行 callback,则可以将 handler 参数设为 null

同时在这里重写onConfigured()方法,调用CameraCaptureSession . setRepeatingReqest()方法,通过此捕获session,持续重复捕获图像(也可以调用 ()方法,设置捕获的参数,可在此处设置 3A算法相)。最后调用 ()方法,开始捕获数据并将数据编码到指定文件。关键代码如下所示:

//camera record process,step5 执行 方法
((previewSurface,recorderSurface),new () {
    @Override
    public void onConfigured(CameraCaptureSession cameraCaptureSession) {
        mCaptureSession = cameraCaptureSession;
        (TAG, "Video Session:" + ());
        (CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        (new Runnable() {
            @Override
            public void run() {
                try {
                    ((), null, mCameraHandler);
                } catch (CameraAccessException e) {
                    throw new RuntimeException("Can't visit camera");
                }
            }
        });
        ();
    }
    @Override
    public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
    }
},mCameraHandler);

@6 录像后 当点击停止录像时,执行CameraCaptureSession的stopRepeating()方法,取消持续捕获,同时调用 CameraCaptureSession的abortCapture()方法,尽可能快地丢弃当前待处理和正在进行的所有捕获(若不做这两步处理,停止录像时会闪退)关键代码如下所示:

(TAG,"stopRecorder");
try {
    //camera record process,step6 取消持续捕获&
    ();
    ();
} catch (CameraAccessException e) {
    ();
}

@7 调用 ,停止图像捕获 并且重启预览模式(根据需要可以此时处理视频,使得系统Gallery可以直接查看到该视频),关键代码如下所示:

//camera record process,step7 停止图像捕获 并且重启预览模式
();
();
//可根据需要将视频设置为系统gallery可见。
(new (mContext, mVideoFile));
startCaptureSession();

关于视频对Gallery可见,以下为关键参考代码:

public static class VideoSaver implements Runnable {
        private final File mFile;
        Context mContext;

        VideoSaver(Context context,File file) {
            mContext = context;
            mFile = file;
        }

        private ContentValues getVideoContentValues(File paramFile,long paramLong) {
            ContentValues values = new ContentValues();
            (, ());
            (.DISPLAY_NAME, ());
            (.MIME_TYPE, "video/mp4");
            (.DATE_TAKEN, (paramLong));
            (.DATE_MODIFIED, (paramLong));
            (.DATE_ADDED, (paramLong));
            (, ());
            (, (()));
            return values;
        }

        @Override
        public void run() {
            (TAG, "recorder video Run");
            ContentResolver localContentResolver = ();
            ContentValues localContentValues = getVideoContentValues(mFile, ());
            Uri localUri = (.EXTERNAL_CONTENT_URI, localContentValues);
            OutputStream os = null;
            FileInputStream fis = null;
            byte[] buf = new byte[1024];
            int len;
            try {
                if (localUri != null) {
                    fis = new FileInputStream(mFile);
                    os = (localUri);
                }
                if (os != null) {
                    while ((len = (buf)) >= 0) {
                        (buf, 0, len);
                    }
                }
            } catch (FileNotFoundException e) {
                ();
            } catch (IOException e) {
                ();
            } finally {
                try {
                    if(os!=null) {
                        ();
                    }
                    if(fis!=null){
                        ();
                    }
                } catch (IOException e) {
                    ();
                }
            }
        }
    }

2 camera录像&保存视频流程代码完整解读

2.1 java源码部分(草稿)

Camera流程相关代码如下所示:

class CameraCoreManager {
    private static final String TAG = "CameraDemo";

    private Context mContext;
    private CameraManager mCameraManager;
    private String mCameraId;
    private HandlerThread mCameraThread;
    private Handler mCameraHandler;
    private ImageReader mImageReader;
    private CameraDevice mCameraDevice;
    private CameraCharacteristics mCameraCharacteristics;
    private MediaRecorder mMediaRecorder;

    //Max preview width&height that is guaranteed by Camera2 API
    private static final int MAX_PREVIEW_WIDTH = 1080;
    private static final int MAX_PREVIEW_HEIGHT = 720;

    //A Semaphore to prevent the app from exiting before closing the camera.
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);
    private Size mPreviewSize = new Size(1920, 1080);
    private  mPreviewRequestBuilder;
    private  mCaptureRequestBuilder;
    private  mRecordRequestBuilder;
    private CameraCaptureSession mCaptureSession;
    private int mFacing = CameraCharacteristics.LENS_FACING_BACK;
    private  mFrameCallback;
    private SurfaceTexture mSurfaceTexture;
    private File mCameraFile;
    private File mVideoFile;
    private TextureView mTextureView;

    private enum State{
        STATE_PREVIEW,
        STATE_CAPTURE,
    }
    State mState = State.STATE_PREVIEW;

    //camera capture process,step3 创建ImageReader并设置mImageAvailableListener,实现如下:
    private  mImageAvailableListener = new () {
        @Override
        public void onImageAvailable(ImageReader reader) {
            if(mState == State.STATE_PREVIEW){
                //(TAG, "##### onFrame: Preview");
                Image image = ();
                ();
            }else if(mState == State.STATE_CAPTURE) {
                (TAG,"capture one picture to gallery");
                mCameraFile = new File("aa_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".jpg");
                (new (mContext, (), mCameraFile));
                mState = State.STATE_PREVIEW;
            }else{
                (TAG, "##### onFrame: default/nothing");
            }
        }
    };

    //camera preview process,step2 mStateCallback 实例化
    private  mStateCallback = new () {
        @Override
        public void onOpened(CameraDevice camera) {
            //重写onOpened方法,最为关键
            ();
            mCameraDevice = camera;
            startCaptureSession();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            ();
            ();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            ("DEBUG", "onError: " + error);
            ();
            ();
            mCameraDevice = null;
            ("DEBUG", "onError:  restart camera");
            stopPreview();
            startPreview();
        }
    };

     mCaptureCallback = new () {
        @Override
        public void onCaptureCompleted(CameraCaptureSession session,CaptureRequest request, TotalCaptureResult result) {
            (session, request, result);
        }

        @Override
        public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
            (session, request, failure);
        }
    };

    public CameraCoreManager(Context context, TextureView textureView) {
        mContext = context;
        mCameraManager = (CameraManager) (Context.CAMERA_SERVICE);
        mMediaRecorder = new MediaRecorder();
        mState = State.STATE_PREVIEW;
        mTextureView = textureView;
    }

    public void startPreview() {
        (TAG,"startPreview");
        if (!chooseCameraIdByFacing()) {
            (TAG, "Choose camera failed.");
            return;
        }

        mCameraThread = new HandlerThread("CameraThread");
        ();
        mCameraHandler = new Handler(());

        if (mImageReader == null) {
            mImageReader = ((), (), , 2);
            (mImageAvailableListener, mCameraHandler);
        }else{
            ();
        }
        openCamera();
    }

    public void stopPreview() {
        (TAG,"stopPreview");
        closeCamera();
        if (mCameraThread != null) {
            ();
            mCameraThread = null;
        }
        mCameraHandler = null;
    }

    private boolean chooseCameraIdByFacing() {
        try {
            String ids[] = ();
            if ( == 0) {
                (TAG, "No available camera.");
                return false;
            }

            for (String cameraId : ()) {
                CameraCharacteristics characteristics = (cameraId);

                StreamConfigurationMap map = (
                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null) {
                    continue;
                }

                Integer level = (CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                    continue;
                }

                Integer internal = (CameraCharacteristics.LENS_FACING);
                if (internal == null) {
                    continue;
                }
                if (internal == mFacing) {
                    mCameraId = cameraId;
                    mCameraCharacteristics = characteristics;
                    return true;
                }
            }

            mCameraId = ids[1];
            mCameraCharacteristics = (mCameraId);
            Integer level = (CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
            if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                return false;
            }

            Integer internal = (CameraCharacteristics.LENS_FACING);
            if (internal == null) {
                return false;
            }
            mFacing = CameraCharacteristics.LENS_FACING_BACK;
        } catch (CameraAccessException e) {
            ();
        }
        return true;
    }

    @SuppressLint("MissingPermission")
    public void openCamera() {
        if ((mCameraId)) {
            (TAG, "Open camera failed. No camera available");
            return;
        }

        try {
            if (!(2500, )) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            //camera preview process,step1 打开camera
            (mCameraId, mStateCallback, mCameraHandler);
        } catch (InterruptedException | CameraAccessException e) {
            (TAG, ());
        }
    }

    private void closeCamera() {
        try {
            ();
            if (mCaptureSession != null) {
                ();
                mCaptureSession = null;
            }
            if (mCameraDevice != null) {
                ();
                mCameraDevice = null;
            }
            if (mImageReader != null) {
                ();
                mImageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            ();
        }
    }

    private void startCaptureSession() {
        mState = State.STATE_PREVIEW;
        if (mCameraDevice == null) {
            return;
        }

        if ((mImageReader != null || mSurfaceTexture != null)) {
            try {
                closeCameraCaptureSession();
                //camera preview process,step3 创建一个 ,templateType来区分是拍照还是预览
                mPreviewRequestBuilder = (CameraDevice.TEMPLATE_PREVIEW);
                //camera preview process,step4 将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中
                (());
                List<Surface> surfaceList = (());
                if (mSurfaceTexture != null) {
                    ((), ());
                    Surface surface = new Surface(mSurfaceTexture);
                    (());
                    (surface);
                    //camera preview process,step5 将显示预览用的surface的实例传入,即将显示预览用的surface的实例,作为一个显示层添加到该请求的目标列表中
                    surfaceList = (surface, ());
                }

                Range<Integer>[] fpsRanges = (CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
                ("DEBUG", "##### fpsRange: " + (fpsRanges));
                //camera preview process,step6 & 7
                // 6 执行createCaptureSession方法
                // 7 参数中实例化 ,并重写 onConfigured 方法
                (surfaceList, new () {
                    @Override
                    public void onConfigured(CameraCaptureSession session) {
                        if (mCameraDevice == null) return;
                        (CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
                        mCaptureSession = session;
                        try {
                            if (mCaptureSession != null)
                                //camera preview process,step8 用 ()方法创建预览
                                ((), mCaptureCallback, mCameraHandler);
                        } catch (CameraAccessException | IllegalArgumentException | IllegalStateException | NullPointerException e) {
                            ();
                        }
                    }

                    @Override
                    public void onConfigureFailed(CameraCaptureSession session) {
                        (TAG, "Failed to configure capture session");
                    }

                    @Override
                    public void onClosed(CameraCaptureSession session) {
                        if (mCaptureSession != null && (session)) {
                            mCaptureSession = null;
                        }
                    }
                }, mCameraHandler);
            } catch (CameraAccessException e) {
                ();
                (TAG, ());
            } catch (IllegalStateException e) {
                stopPreview();
                startPreview();
            } catch (UnsupportedOperationException e) {
                ();
                (TAG, ());
            }
        }
    }

    public void captureStillPicture() {
        try {
            (TAG,"captureStillPicture");
            mState = State.STATE_CAPTURE;
            if (mCameraDevice == null) {
                return;
            }
            // camera capture process,step1 创建作为拍照的
            mCaptureRequestBuilder = (CameraDevice.TEMPLATE_STILL_CAPTURE);
            // camera capture process,step2 将imageReader的surface作为的目标
            (());
            // 设置自动对焦模式
            (CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 设置自动曝光模式
            (CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            // 设置为自动模式
            (CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
            // 设置摄像头旋转角度
            (CaptureRequest.JPEG_ORIENTATION, Surface.ROTATION_0);
            // 停止连续取景
            ();
            // camera capture process,step5 &6 捕获静态图像,结束后执行onCaptureCompleted
            ((), new () {
                @Override// 拍照完成时激发该方法
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                    (TAG,"onCaptureCompleted");
                    startCaptureSession();
                }
            }, mCameraHandler);
        } catch (CameraAccessException e) {
            ();
        }
    }

    private void setupMediaRecorder(){
        //camera record process,step1 停止预览,准备切换到录制视频
        try {
            ();
            closeCameraCaptureSession();
        } catch (CameraAccessException e) {
            ();
        }
        //camera record process,step2 mMediaRecorder相关设置
        mVideoFile = new File((),new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) +"demo.mp4");
        ();//设置音频来源
        ();//设置视频来源
        (.MPEG_4);//设置输出格式
        ();//设置音频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择AAC
        ();//设置视频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择H264
        (8*1024*1920);//设置比特率 一般是 1*分辨率 到 10*分辨率 之间波动。比特率越大视频越清晰但是视频文件也越大。
        (30);//设置帧数 选择 30即可, 过大帧数也会让视频文件更大当然也会更流畅,但是没有多少实际提升。人眼极限也就30帧了。
        ((),());
        (90);
        Surface surface = new Surface(());
        (surface);
        (());
        try {
            ();
        } catch (IOException e) {
            ();
        }

        SurfaceTexture surfaceTexture = ();
        ((),());
        Surface previewSurface = new Surface(surfaceTexture);
        Surface recorderSurface = ();//从获取录制视频需要的Surface

        try {
            //camera record process,step3 创建 对象,并用CameraDevice.TEMPLATE_RECORED 参数初始化
            mPreviewRequestBuilder = (CameraDevice.TEMPLATE_RECORD);
            (CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            //camera record process,step4 将MediaRecorder和预览用的Surface实例添加到该请求的目标列表中
            (previewSurface);
            (recorderSurface);
            //请注意这里设置了(previewSurface,recorderSurface) 2个Surface,很好理解录制视频也需要有画面预览,第一个是预览的Surface,第二个是录制视频使用的Surface
            //camera record process,step5 执行 方法
            ((previewSurface,recorderSurface),new () {
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    mCaptureSession = cameraCaptureSession;
                    (TAG, "Video Session:" + ());
                    (CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
                    (new Runnable() {
                        @Override
                        public void run() {
                            try {
                                ((), null, mCameraHandler);
                            } catch (CameraAccessException e) {
                                throw new RuntimeException("Can't visit camera");
                            }
                        }
                    });
                    ();
                }
                @Override
                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                }
            },mCameraHandler);
        } catch (CameraAccessException e) {
            ();
        }
    }

    private void closeCameraCaptureSession() {
        if (mCaptureSession != null) {
            (TAG, "close Session:" + ());
            ();
            mCaptureSession = null;
        }
    }
    public void startRecorder(){
        (TAG,"startRecorder");
        setupMediaRecorder();
    }

    public void stopRecorder(){
        (TAG,"stopRecorder");
        try {
            //camera record process,step6 取消持续捕获
            ();
            //丢弃当前待处理和正在进行的所有捕获
            ();
        } catch (CameraAccessException e) {
            ();
        }
        //camera record process,step7 停止图像捕获 并且重启预览模式
        ();
        ();
        //可根据需要将视频设置为系统gallery可见。
        (new (mContext, mVideoFile));
        startCaptureSession();
    }

    public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
        mSurfaceTexture = surfaceTexture;
    }
}

保存照片和保存视频相关操作在FileUtils类中代码如下所示:

class FileUtils {
    private static final String TAG = "FileUtils";

    //camera capture progress,step4 保存图片相关操作
    public static class ImageSaver implements Runnable {
        private final Image mImage;
        private final File mFile;
        Context mContext;

        ImageSaver(Context context,Image image, File file) {
            mContext = context;
            mImage = image;
            mFile = file;
        }

        @Override
        public void run() {
            (TAG,"take picture Image Run");
            ByteBuffer buffer = ()[0].getBuffer();
            byte[] bytes = new byte[()];
            (bytes);
            ContentValues values = new ContentValues();
            (, "This is an qr image");
            (.DISPLAY_NAME, ());
            (.MIME_TYPE, "image/jpeg");
            (, "");
            (.RELATIVE_PATH, "Pictures/");
            Uri external = .EXTERNAL_CONTENT_URI;
            ContentResolver resolver = ();
            Uri insertUri = (external, values);
            OutputStream os = null;
            try {
                if (insertUri != null) {
                    os = (insertUri);
                }
                if (os != null) {
                    (bytes);
                }
            } catch (IOException e) {
                ();
            } finally {
                ();
                try {
                    if(os!=null) {
                        ();
                    }
                } catch (IOException e) {
                    ();
                }
            }
        }
    }

    public static class VideoSaver implements Runnable {
        private final File mFile;
        Context mContext;

        VideoSaver(Context context,File file) {
            mContext = context;
            mFile = file;
        }

        private ContentValues getVideoContentValues(File paramFile,long paramLong) {
            ContentValues values = new ContentValues();
            (, ());
            (.DISPLAY_NAME, ());
            (.MIME_TYPE, "video/mp4");
            (.DATE_TAKEN, (paramLong));
            (.DATE_MODIFIED, (paramLong));
            (.DATE_ADDED, (paramLong));
            (, ());
            (, (()));
            return values;
        }

        @Override
        public void run() {
            (TAG, "recorder video Run");
            ContentResolver localContentResolver = ();
            ContentValues localContentValues = getVideoContentValues(mFile, ());
            Uri localUri = (.EXTERNAL_CONTENT_URI, localContentValues);
            OutputStream os = null;
            FileInputStream fis = null;
            byte[] buf = new byte[1024];
            int len;
            try {
                if (localUri != null) {
                    fis = new FileInputStream(mFile);
                    os = (localUri);
                }
                if (os != null) {
                    while ((len = (buf)) >= 0) {
                        (buf, 0, len);
                    }
                }
            } catch (FileNotFoundException e) {
                ();
            } catch (IOException e) {
                ();
            } finally {
                try {
                    if(os!=null) {
                        ();
                    }
                    if(fis!=null){
                        ();
                    }
                } catch (IOException e) {
                    ();
                }
            }
        }
    }
}

Activity UI相关代码如下所示:

public class MainActivity extends AppCompatActivity implements {
    private static final String TAG = "CameraDemo";
    private ImageButton mTakePictureBtn;
    private Button mBtnVideoStart;
    private Button mBtnVideoStop;
    private CameraCoreManager manager;
    private TextureView mTextureView;
    Lock Mutex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        (savedInstanceState);
        setContentView(.activity_camera);
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        mTakePictureBtn = findViewById(.camera_take_picture);
        (this);
        mBtnVideoStart = findViewById(.btn_video_start);
        (this);
        mBtnVideoStop = findViewById(.btn_video_stop);
        (this);
        mTextureView = findViewById(.texture_view);
        (new () {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                (surface);
                ();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            }
        });
        manager = new CameraCoreManager(this,mTextureView);
        (TAG,"onCreate:init");
    }

    @Override
    public void onResume() {
        ();

    }

    @Override
    public void onPause() {
        ();
    }

    @Override
    protected void onDestroy() {
        ();
        ();
    }

    @Override
    public void onClick(View v) {
        switch (()) {
            case .camera_take_picture:
                (TAG,"takepicture");
                ();
                break;
            case .btn_video_start:
                ();
                break;
            case .btn_video_stop:
                ();
                break;
            default:

                break;
        }
    }
}

2.2 layout文件

这里涉及布局文件主要为activity_camera.xml,xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
< xmlns:andro
xmlns:app="/apk/res-auto"

android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/gray"
    android:gravity="center"
    android:orientation="horizontal"
    android:padding="20dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent">

    <ImageButton
        android:
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="@drawable/ic_camera" />
</LinearLayout>

<TextureView
    android:
    android:layout_width="420dp"
    android:layout_height="360dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="录相开始"
        app:layout_constraintTop_toBottomOf="@id/texture_view"
        app:layout_constraintLeft_toLeftOf="parent"/>
    <Button
        android:
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="录相结束"
        app:layout_constraintTop_toBottomOf="@id/texture_view"
        app:layout_constraintRight_toRightOf="parent"/>


</>