Vídeo de la cámara 2

package com.example.camera;


import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Chronometer;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class RecorderVideoFragment extends Fragment implements View.OnClickListener {
    private String TAG = "RecorderVideoFragment";
    private ImageButton videoButton; //用来重新设置录像按钮
    private TextureView mTextureView; //预览框
    private CaptureRequest.Builder mPreviewCaptureRequest; //获取请求创建者
    private CameraDevice mCameraDevice; //camera设备
    private MediaRecorder mMediaRecorder; //音视频录制
    //摄像头ID 默认置为后置BACK FRONT值为0 == BACK
    private String mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);
    private Chronometer timer; //计时器
    private ArrayList<String> imageList = new ArrayList<>();   // 路径集合
    private static CameraCaptureSession mCameraCaptureSession;   //获取会话
    private Handler mChildHandler;   //子线程
    private CameraManager mCameraManager; //摄像头管理者
    private boolean isVisible = false;
    private boolean isRecording = false;
    private HandlerThread mHandlerThread;  //线程处理者
    private ImageView mImageView;     //缩略图按钮

    //Fragment 中 onCreateView返回的就是fragment要显示的view.
    @Nullable
    @Override
    /**
     * 第一个参数LayoutInflater inflater第二个参数ViewGroup container第三个参数 Bundle savedInstanceState
     * LayoutInflater inflater:作用类似于findViewById()用来寻找xml布局下的具体的控件Button、TextView等,
     * LayoutInflater inflater()用来找res/layout/下的xml布局文件
     * ViewGroup container:表示容器,View放在里面
     * Bundle savedInstanceState:保存当前的状态,在活动的生命周期中,只要离开了可见阶段,活动很可能就会被进程终止,
     * 这种机制能保存当时的状态
     */
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: success");
        View view = inflater.inflate(R.layout.fragment_recorder, container, false);
        mTextureView = view.findViewById(R.id.textureView);
        timer = view.findViewById(R.id.timer);
        videoButton = view.findViewById(R.id.recording);
        videoButton.setOnClickListener(this);
        mImageView = view.findViewById(R.id.image_show);
        mImageView.setOnClickListener(this);
        return view;
    }
    //判断Fragment是否可见
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            //如果Fragment可见 把isVisible置为true
            isVisible = true;
            Log.d(TAG, "setUserVisibleHint: true");
            //设置显示最后一张图片(视频第一帧)
            setLastImagePath();
            initChildHandler();
            if (mTextureView.isAvailable()) {
                openCamera();
            } else {
                initTextureViewStateListener();
            }
        }else {
            closeCamera();
            return;
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (isVisible) {
            initChildHandler();
            if (mTextureView.isAvailable()) {
                openCamera();
            } else {
                initTextureViewStateListener();
            }
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.recording:
                if (isRecording) {
                    //再次按下将停止录制
                    stopRecorder();
                    isRecording = false;
                } else {
                    //第一次按下将isRecording置为ture
                    //配置并开始录制
                    isRecording = true;
                    config();
                    startRecorder();
                }
                break;
            case R.id.image_show:
                openAlbum();
                break;
        }
    }
    //找到最后一张的路径
    private void setLastImagePath() {
        Log.d(TAG, "setLastImagePath: success");
        imageList = GetImageFilePath.getFilePath();
        String string = imageList.get(imageList.size() - 1);
        if(string.contains(".jpg")){
            setImageBitmap(string);
        }else {
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            retriever.setDataSource(string);
            //获取第1帧
            Bitmap bitmap = retriever.getFrameAtTime(1);
            mImageView.setImageBitmap(bitmap);
        }
    }

    // 设置缩略图显示
    private void setImageBitmap(String path) {
        Log.d(TAG, "setImageBitmap: success");
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        //通过ImageView显示缩略图
        mImageView.setImageBitmap(bitmap);
    }
    //1.摄像头状态回调
    private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            //摄像头被打开
            mCameraDevice = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            //摄像头断开
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            //异常
        }
    };
    //2.消息捕获回调
    private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request,
                                     long timestamp, long frameNumber) {
            super.onCaptureStarted(session, request, timestamp, frameNumber);
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request,
                                    @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
        }
    };
    //3.会话状态回调
    private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            mCameraCaptureSession = session;
            updatePreview();
            try {
                //执行重复获取数据请求,等于一直获取数据呈现预览画面,mSessionCaptureCallback会返回此次操作的信息回调
                mCameraCaptureSession.setRepeatingRequest(mPreviewCaptureRequest.build(),
                        mSessionCaptureCallback, mChildHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
        }
    };
    //
    private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
        builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    }
    

    /**
     * 初始化TextureView的纹理生成监听,只有纹理生成准备好了。才能去进行摄像头的初始化工作让TextureView接收摄像头预览画面
     */
    private void initTextureViewStateListener() {
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                //可以使用纹理
                initCameraManager();
                selectCamera();
                openCamera();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                //纹理尺寸变化

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                //纹理被销毁
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
                //纹理更新

            }
        });
    }
    /**
     * 计算需要的使用的摄像头分辨率
     *
     * @return
     */
    private Size getMatchingSize() {
        Size selectSize = null;
        try {
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            //因为我这里是将预览铺满屏幕,所以直接获取屏幕分辨率
            DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
            //屏幕分辨率宽
            int deviceWidth = displayMetrics.widthPixels;
            //屏幕分辨率高
            int deviceHeigh = displayMetrics.heightPixels;
            Log.e(TAG, "getMatchingSize: 屏幕密度宽度=" + deviceWidth);
            Log.e(TAG, "getMatchingSize: 屏幕密度高度=" + deviceHeigh);
            /**
             * 循环40次,让宽度范围从最小逐步增加,找到最符合屏幕宽度的分辨率,
             * 你要是不放心那就增加循环,肯定会找到一个分辨率,不会出现此方法返回一个null的Size的情况
             * ,但是循环越大后获取的分辨率就越不匹配
             */
            for (int j = 1; j < 41; j++) {
                for (int i = 0; i < sizes.length; i++) { //遍历所有Size
                    Size itemSize = sizes[i];
                    Log.e(TAG, "当前itemSize 宽=" + itemSize.getWidth() + "高=" + itemSize.getHeight());
                    //判断当前Size高度小于屏幕宽度+j*5  &&  判断当前Size高度大于屏幕宽度-j*5  &&  判断当前Size宽度小于当前屏幕高度
                    if (itemSize.getHeight() < (deviceWidth + j * 5) && itemSize.getHeight() > (deviceWidth - j * 5)) {
                        if (selectSize != null) { //如果之前已经找到一个匹配的宽度
                            if (Math.abs(deviceHeigh - itemSize.getWidth()) < Math.abs(deviceHeigh - selectSize.getWidth())) { //求绝对值算出最接近设备高度的尺寸
                                selectSize = itemSize;
                                continue;
                            }
                        } else {
                            selectSize = itemSize;
                        }
                    }
                }
                if (selectSize != null) { //如果不等于null 说明已经找到了 跳出循环
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "getMatchingSize: 选择的分辨率宽度=" + selectSize.getWidth());
        Log.e(TAG, "getMatchingSize: 选择的分辨率高度=" + selectSize.getHeight());
        return selectSize;
    }
    /**
     * 初始化Camera2的相机管理,CameraManager用于获取摄像头分辨率,摄像头方向,摄像头id与打开摄像头的工作
     */
    private void initCameraManager() {
        mCameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
    }
    /**
     * 选择一颗我们需要使用的摄像头,主要是选择使用前摄还是后摄或者是外接摄像头
     */
    private void selectCamera() {
        if (mCameraManager != null) {
            Log.e(TAG, "selectCamera: CameraManager is null");

        }
        try {
            String[] cameraIdList = mCameraManager.getCameraIdList();   //获取当前设备的全部摄像头id集合
            if (cameraIdList.length == 0) {
                Log.e(TAG, "selectCamera: cameraIdList length is 0");
            }
            for (String cameraId : cameraIdList) {
                //遍历所有摄像头
                //得到当前id的摄像头描述特征
                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
                //获取摄像头的方向特征信息
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    //这里选择了后摄像头
                    mCameraId = cameraId;
                }
            }

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    @SuppressLint("MissingPermission")
    private void openCamera() {
        try {
            if (mCameraManager == null) {
                initCameraManager();
            }
            mCameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    private void closeCamera() {
        // 关闭预览就是关闭捕获会话
        stopPreview();
        // 关闭当前相机
        if (null != mCameraDevice) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        if (null != mMediaRecorder) {
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        if (mHandlerThread != null) {
            stopBackgroundThread();
        }
    }

    /**
     * 开启预览
     * 使用TextureView显示相机预览数据,
     * 预览和拍照数据都是使用CameraCaptureSession会话来请求
     */
    private void startPreview() {
        stopPreview();
        SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
        Size cameraSize = getMatchingSize();
        //设置TextureView的缓冲区大小
        mSurfaceTexture.setDefaultBufferSize(cameraSize.getWidth(),
                cameraSize.getHeight());
        //获取Surface显示预览数据
        Surface previewSurface = new Surface(mSurfaceTexture);
        try {
            //创建CaptureRequestBuilder,TEMPLATE_PREVIEW比表示预览请求
            mPreviewCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            //设置Surface作为预览数据的显示界面
            mPreviewCaptureRequest.addTarget(previewSurface);
            //创建相机捕获会话,第一个参数是捕获数据Surface列表,
            // 第二个参数是CameraCaptureSession的状态回调接口,
            //当他创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行
            mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    mCameraCaptureSession = session;
                    updatePreview();
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    Toast.makeText(getActivity().getApplicationContext(), "Faileedsa ", Toast.LENGTH_SHORT).show();
                }
            }, mChildHandler);
        } catch (CameraAccessException e) {
            Log.d("huangxin", "5");
            e.printStackTrace();
        }
    }
    /**
     * 更新预览
     */
    private void updatePreview() {
        if (null == mCameraDevice) {
            return;
        }
        try {
            setUpCaptureRequestBuilder(mPreviewCaptureRequest);
            HandlerThread thread = new HandlerThread("CameraPreview");
            thread.start();
            mCameraCaptureSession.setRepeatingRequest(mPreviewCaptureRequest.build(), null, mChildHandler);
        } catch (CameraAccessException e) {
            Log.d("huangxin", "3");
            e.printStackTrace();
        }
    }
    /**
     * 关闭预览
     */
    private void stopPreview() {
        //关闭预览就是关闭捕获会话
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }
    }

    /**
     * 初始化子线程Handler,操作Camera2需要一个子线程的Handler
     */
    private void initChildHandler() {
        mHandlerThread = new HandlerThread("Camera2Demo");
        mHandlerThread.start();
        mChildHandler = new Handler(mHandlerThread.getLooper());
    }
    /**
     * 关闭线程
     */
    public void stopBackgroundThread() {
        if (mHandlerThread != null) {
            //quitSafely 安全退出
            mHandlerThread.quitSafely();
            try {
                mHandlerThread.join();
                mHandlerThread = null;
                mHandlerThread = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 配置录制视频相关数据
     */
    private void configMediaRecorder() {
        File file = new File(Environment.getExternalStorageDirectory() +
                "/DCIM/camera/myMP4" + System.currentTimeMillis() + ".mp4");
        if (file.exists()) {
            file.delete();
        }
        if (mMediaRecorder == null) {
            mMediaRecorder = new MediaRecorder();
        }
        //设置音频来源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //设置视频来源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        //设置输出格式
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //设置音频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择AAC
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        //设置视频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择H264
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        //设置比特率 一般是 1*分辨率 到 10*分辨率 之间波动。比特率越大视频越清晰但是视频文件也越大。
        mMediaRecorder.setVideoEncodingBitRate(8 * 1024 * 1920);
        //设置帧数 选择 30即可, 过大帧数也会让视频文件更大当然也会更流畅,但是没有多少实际提升。人眼极限也就30帧了。
        mMediaRecorder.setVideoFrameRate(30);
        Size size = getMatchingSize();
        mMediaRecorder.setVideoSize(size.getWidth(), size.getHeight());
        mMediaRecorder.setOrientationHint(90);
        Surface surface = new Surface(mTextureView.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);
        mMediaRecorder.setOutputFile(file.getAbsolutePath());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 配置录制视频时的CameraCaptureSession
     */
    private void config() {
        try {
            if (mCameraCaptureSession != null) {
                mCameraCaptureSession.stopRepeating();//停止预览,准备切换到录制视频
                mCameraCaptureSession.close();//关闭预览的会话,需要重新创建录制视频的会话
                mCameraCaptureSession = null;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        configMediaRecorder();
        Size cameraSize = getMatchingSize();
        SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(cameraSize.getWidth(), cameraSize.getHeight());
        Surface previewSurface = new Surface(surfaceTexture);
        Surface recorderSurface = mMediaRecorder.getSurface();//从获取录制视频需要的Surface
        try {
            mPreviewCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            mPreviewCaptureRequest.addTarget(previewSurface);
            mPreviewCaptureRequest.addTarget(recorderSurface);
            //请注意这里设置了Arrays.asList(previewSurface,recorderSurface) 2个Surface,很好理解录制视频也需要有画面预览,
            // 第一个是预览的Surface,第二个是录制视频使用的Surface
            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, recorderSurface),
                    mSessionStateCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始录制视频
     */
    private void startRecorder() {
        mMediaRecorder.start();
    }

    /**
     * 暂停录制视频(暂停后视频文件会自动保存)
     */
    private void stopRecorder() {
        if (mMediaRecorder != null) {
            mMediaRecorder.stop();
            mMediaRecorder.reset();
        }
        broadcast();
        setLastImagePath();
        startPreview();
    }
    // 广播通知相册更新
    public void broadcast() {
        Log.d(TAG, "broadcast: success");
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/";
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(new File(path));
        intent.setData(uri);
        getActivity().sendBroadcast(intent);
    }
    //打开相册
    private void openAlbum() {
        Log.d(TAG, "openAlbum: success");
        Intent intent = new Intent();
        // 在ImageShowActivity中直接从相册中遍历 不需要传递过去
        //intent.putStringArrayListExtra("myList", imageList);
        intent.setClass(getContext(), ImageShowActivity.class);
        startActivity(intent);
    }
}

Supongo que te gusta

Origin blog.csdn.net/I_am_a_loser/article/details/120455787
Recomendado
Clasificación