Android多媒体之Camera2的相关操作

5,297 阅读8分钟

零、前言

Android 5.0+ (API 21)

Camera过时.png

---->[源码里让我们用camera2]
 * @deprecated We recommend using the new 
 {@link android.hardware.camera2} API for new applications.

这里的camera2可不是一个类哦


一、打开相机并预览

1.早听闻camera2很复杂,没想到这么复杂,我都有点小hold不住
视图布局和上一篇一样,只是实现变了而已

1.打开相机:Camera2Activity#surfaceCreated方法中

开个相机就这么麻烦...(图画出来感觉清楚多了)

打开相机.png


1.1:准备两个Handler
HandlerThread handlerThread = new HandlerThread("Camera2");//线程名,随意
handlerThread.start();
mainHandler = new Handler(getMainLooper());//主线程Handler
childHandler = new Handler(handlerThread.getLooper());//子线程Handler

上来就一个HandlerThread类,它是干嘛的?又是Handler又是Thread的,名字怪吓人的
一看源码,它仿佛是在逗我笑...一共就166行,继承自Thread
getThreadHandler方法还是 * @hide的,这不是明摆着说:快用getLooper方法吗?
有了Looper就能根据Looper创建该线程下的Handler,名字起的很到位

HandlerThread.png


1.2:打开相机

主要在CameraDevice.StateCallback的onOpened回调函数中有CameraDevice的引用
注意,到这里还只是打开了相机,并没有预览

mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//后摄像头
//获取摄像头管理器
mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
    //AndroidStudio自动生成...if
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
            PackageManager.PERMISSION_GRANTED) {
        return;
    }
    CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            mCameraDevice = camera;
            //TODO 预览方法见下:--startPreview()
        }
        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
        }
        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
        }
    };
    mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
} catch (CameraAccessException e) {
    e.printStackTrace();
}

2.开启预览:方法startPreview

开启预览真的挺费劲的

开启预览.png

/**
 * 开启预览
 */
private void startPreview() {
    try {
        // 创建预览需要的CaptureRequest.Builder
        final CaptureRequest.Builder reqBuilder =
                mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        // 将SurfaceView的surface作为CaptureRequest.Builder的目标
        reqBuilder.addTarget(mHolder.getSurface());
        //reqBuilder可以设置参数
        reqBuilder.set( // 自动对焦
                CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        reqBuilder.set(// 打开闪光灯
                CaptureRequest.CONTROL_AE_MODE,
                CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
        // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
        CameraCaptureSession.StateCallback stateCallback =
                new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                        if (null == mCameraDevice) return;
                        // 当摄像头已经准备好时,开始显示预览
                        mCameraCaptureSession = cameraCaptureSession;
                        try {
                            // 显示预览
                            mCameraCaptureSession.setRepeatingRequest(
                                    reqBuilder.build(), null, childHandler);
                        } catch (CameraAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    @Override
                    public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                        Toast.makeText(Camera2Activity.this, "配置失败", Toast.LENGTH_SHORT).show();
                    }
                };
        mImageReader = ImageReader.newInstance(
                mIdSvVideo.getWidth(), mIdSvVideo.getHeight(),
                ImageFormat.JPEG, 1);
        mCameraDevice.createCaptureSession(
                Arrays.asList(mHolder.getSurface(), mImageReader.getSurface()),
                stateCallback,
                childHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

3.修正预览的宽高比

费了这么大的劲,然后终于可以预览了,结果如下...头像都变形了,这怎么能忍
百度了一会,并没有找到好的解决方法,然后发挥自己的聪明才智
把SurfaceView宽高比缩放成3:4,并对横竖屏分别适配,完美解决

竖屏横屏

代码实现起来也非常简单,根据长宽的大小,setScale,保证比例就行了
注意:SurfaceView在surfaceCreated回调时才有尺寸,在onCreate时宽高为0

/**
 * 适应屏幕
 *
 * @param surfaceView
 */
private void adjustScreen(View surfaceView) {
    int height = surfaceView.getHeight();
    int width = surfaceView.getWidth();
    if (height > width) {
        float justH = width * 4.f / 3;
        mIdSvVideo.setScaleX(height / justH);
    } else {
        float justW = height * 4.f / 3;
        mIdSvVideo.setScaleY(width / justW);
    }
}
竖屏横屏

Ok,总算完美显示出来了,良好的开端是成功的一半,继续


二、拍张照

想拍张照也不简单啊...

拍照逻辑概览.png


1.拍照方法封装
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();//旋转方向集合
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}

/**
 * 拍照方法封装
 *
 */
private void takePicture() {
    if (mCameraDevice != null) {
        try {
            CaptureRequest.Builder reqBuilder = mCameraDevice
                    .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            reqBuilder.addTarget(mImageReader.getSurface());
            // 自动对焦
            reqBuilder.set(
                    CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 自动曝光
            reqBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            // 获取手机方向
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            // 根据设备方向计算设置照片的方向
            reqBuilder.set(
                    CaptureRequest.JPEG_ORIENTATION,
                    ORIENTATIONS.get(rotation));
            //拍照
            mCameraCaptureSession.capture(reqBuilder.build(), null, childHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

2.存储照片逻辑
//可以在这里处理拍照得到的临时照片
mImageReader.setOnImageAvailableListener(reader -> {
    // 拿到拍照照片数据
    Image image = reader.acquireNextImage();
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);//由缓冲区存入字节数组
    File file = FileHelper.get()
            .createFile("camera2/IMG-"+StrUtil.getCurrentTime_yyyyMMddHHmmss()+".jpg");
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(file);
        fos.write(bytes);
        fos.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        startPreview();
        image.close();
    }
}, mainHandler);

3.说个小坑

当拍一张时,一切挺好,再拍一张居然崩了...
前面ImageReader的maxImages参数设为1,maxImages (1) has already been acquired

    Process: com.toly1994.video, PID: 11594
    java.lang.IllegalStateException: maxImages (1) has already been acquired, call #close before acquiring more.
        at android.media.ImageReader.acquireNextImage(ImageReader.java:501)
        at com.toly1994.video.cameral2.Camera2Activity.lambda$surfaceCreated$9(Camera2Activity.java:355)
        at com.toly1994.video.cameral2.-?Lambda$Camera2Activity$ip57VOWPzaqDJe_HhvMUPkOS6eo.onImageAvailable(Unknown Source:2)

再看看ImageReader对maxImages的表述

he maximum number of images the user will want to access simultaneously. 
This should be as small as possible to limit memory use. 
Once maxImages Images are obtained by the user, one of them has to be released
before a new Image will become available for access through

用户想要同时访问的最大图像数量。这应该尽可能小,以限制内存的使用。
一旦用户获得了maxImages图像,在可以通过新图像进行访问之前,必须先释放其中一个图像

所以拍完照后释放一下还有重新startPreview()一下,不然就不动了


 image.close();

三、照片尺寸问题

1.获取支持的尺寸种类

这个和ImageReader个尺寸有关,我只说有关,没说就是
经过前一篇我们知道,照片的尺寸都是固定的某些种
看下面,我用1080* 19201080* 1925结果拍的两张尺寸一样
说明传参只是参考值,内部会自己进行调整,我设成1*1,结果尺寸144*176
这时应该会想到上一篇中打印的的图片种类支持情况,这篇看一下camera2里怎么获取

关于尺寸.png

//mImageReader = ImageReader.newInstance(
//                    1080, 1920,
//                   ImageFormat.JPEG, 1);

//mImageReader = ImageReader.newInstance(
//                    1080, 1925,
//                    ImageFormat.JPEG, 1);
                    
mImageReader = ImageReader.newInstance(
        1, 1,
        ImageFormat.JPEG, 1);
        
        

// 获取摄像头支持的配置属性
StreamConfigurationMap map = characteristics.get(
        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// 获取摄像头支持的最大尺寸
List<Size> sizes = Arrays.asList(
        map.getOutputSizes(ImageFormat.JPEG));

for (int i = 0; i < sizes.size(); i++) {
    Size pSize = sizes.get(i);
    L.d("PictureSize.width = " + pSize.getWidth() + "--------PictureSize.height = " + pSize.getHeight());
}

打印结果:和上一篇获取的结果一样

PictureSize.width = 5184--------PictureSize.height = 3880
PictureSize.width = 4608--------PictureSize.height = 3456
PictureSize.width = 4608--------PictureSize.height = 2592
PictureSize.width = 4608--------PictureSize.height = 2304
PictureSize.width = 4608--------PictureSize.height = 2176
PictureSize.width = 4608--------PictureSize.height = 2126
PictureSize.width = 4160--------PictureSize.height = 3120
PictureSize.width = 4160--------PictureSize.height = 2340
PictureSize.width = 4000--------PictureSize.height = 3000
PictureSize.width = 3840--------PictureSize.height = 2160
PictureSize.width = 3264--------PictureSize.height = 2448
PictureSize.width = 3264--------PictureSize.height = 1632
PictureSize.width = 3264--------PictureSize.height = 1552
PictureSize.width = 3264--------PictureSize.height = 1504
PictureSize.width = 3200--------PictureSize.height = 2400
PictureSize.width = 2592--------PictureSize.height = 1944
PictureSize.width = 2592--------PictureSize.height = 1940
PictureSize.width = 2592--------PictureSize.height = 1296
PictureSize.width = 2592--------PictureSize.height = 1232
PictureSize.width = 2592--------PictureSize.height = 1458
PictureSize.width = 2560--------PictureSize.height = 1920
PictureSize.width = 2688--------PictureSize.height = 1512
PictureSize.width = 2304--------PictureSize.height = 1728
PictureSize.width = 2304--------PictureSize.height = 1296
PictureSize.width = 2048--------PictureSize.height = 1536
PictureSize.width = 1920--------PictureSize.height = 1080
PictureSize.width = 1840--------PictureSize.height = 1380
PictureSize.width = 1600--------PictureSize.height = 1200
PictureSize.width = 1600--------PictureSize.height = 900
PictureSize.width = 1440--------PictureSize.height = 1080
PictureSize.width = 1280--------PictureSize.height = 960
PictureSize.width = 1280--------PictureSize.height = 768
PictureSize.width = 1280--------PictureSize.height = 720
PictureSize.width = 1024--------PictureSize.height = 768
PictureSize.width = 800--------PictureSize.height = 600
PictureSize.width = 800--------PictureSize.height = 480
PictureSize.width = 720--------PictureSize.height = 480
PictureSize.width = 640--------PictureSize.height = 480
PictureSize.width = 352--------PictureSize.height = 288
PictureSize.width = 320--------PictureSize.height = 240
PictureSize.width = 176--------PictureSize.height = 144

2.获取最大的尺寸

取容器的最大值而已,打印了一下,无误

Size maxSize = Collections.max(sizes, (o1, o2) -> //获取容器最大值
                o1.getWidth() * o1.getHeight() - o2.getWidth() * o1.getHeight());

这下知道为什么一开始的时候是变形的了,SurfaceView将Camera的区域全部显示
然后宽必须变窄才能容下,所以预览看起来就是变窄了,但排出的照片是好的

1080*19203880*5184

3.这就有个问题:

不能所见即所得,但它和界面不变形又二者不可兼得
没办法,写个函数适配一下吧,根据手机的尺寸来动态计算何时的图片大小
将屏幕高的2倍进行参考,取差值最小的index,再根据屏幕宽的两倍,取差值最小的index
横向纵向比较,就能取到最适合的了。看了一下,我手机自带的相机拍出来的是2126*4608

/**
 * 计算该手机合适的照片尺寸
 */
private void fitPhotoSize() {
    // 获取指定摄像头的特性
    CameraCharacteristics characteristics = null;
    try {
        characteristics = mCameraManager.getCameraCharacteristics(mCameraID);
        // 获取摄像头支持的配置属性
        StreamConfigurationMap map = characteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        // 获取摄像头支持的最大尺寸
        List<Size> sizes = Arrays.asList(map.getOutputSizes(ImageFormat.JPEG));
        int minIndex = 0;//差距最小的索引
        int minDx = Integer.MAX_VALUE;
        int minDy = Integer.MAX_VALUE;
        int[] dxs = new int[sizes.size()];
        int justW = mWinSize.getHeight() * 2;//相机默认是横向的,so
        int justH = mWinSize.getWidth() * 2;
        for (int i = 0; i < sizes.size(); i++) {
            dxs[i] = sizes.get(i).getWidth() - justW;
        }
        for (int i = 0; i < dxs.length; i++) {
            int abs = Math.abs(dxs[i]);
            if (abs < minDx) {
                minIndex = i;//获取高的最适索引
                minDx = abs;
            }
        }
        for (int i = 0; i < sizes.size(); i++) {
            Size size = sizes.get(i);
            if (size.getWidth() == sizes.get(minIndex).getWidth()) {
                int dy = Math.abs(justH - size.getHeight());
                if (dy < minDy) {
                    minIndex = i;//获取宽的最适索引
                    minDy = dy;
                }
            }
        }
        justSize = sizes.get(minIndex);
        L.d(justSize + L.l());
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

由于拍照的方法已封装,所以延迟拍照功能和上一篇一样


四:其他用法

1.预览缩放处理

图和上一篇差不多,直接拿来用了,录个屏也怪麻烦...
这个效果百度找不到...根据套路看CaptureRequest源码里支持那些请求
看到SCALER_CROP_REGION是一个Rect,感觉有点像,就用了,然后瞎猫碰到死耗子...

预览缩放处理


/**
 * 缩放封装
 */
public void setZoom() {
    if ((mRate - 1) * 10 / 4 + 1 > 4.6f) {
        mRate = 1;
    }
    String rate = new DecimalFormat("#.0").format((mRate - 1) * 10 / 4 + 1);
    mIdIvZoom.setText(rate + "x");
    try {
        CaptureRequest.Builder reqBuilder = mCameraDevice
                .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        // 将SurfaceView的surface作为CaptureRequest.Builder的目标
        reqBuilder.addTarget(mHolder.getSurface());
        reqBuilder.set(
                CaptureRequest.SCALER_CROP_REGION,
                new Rect(0, 0, (int) (justSize.getWidth() / mRate), (int) (justSize.getHeight() / mRate)));
        mCameraCaptureSession.setRepeatingRequest(reqBuilder.build(), null, childHandler);
        mRate += 0.15;
    } catch (CameraAccessException e) {
        e.printStackTrace();

    }
}

2.灯光的开启与关闭

打灯.gif

//开闪光灯
mIdIvSplash.setOnClickListener(v -> {
    if (!isFlashLight) {
        mIdIvSplash.setImageTintList(ColorStateList.valueOf(0xffEFB90F));
    } else {
        mIdIvSplash.setImageTintList(ColorStateList.valueOf(0xfffffffF));
    }
    isFlashLight = !isFlashLight;
    try {
        CaptureRequest.Builder reqBuilder = mCameraDevice
                .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        reqBuilder.addTarget(mHolder.getSurface());
        reqBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
        reqBuilder.set(CaptureRequest.FLASH_MODE,
                isFlashLight?CameraMetadata.FLASH_MODE_TORCH:CameraMetadata.FLASH_MODE_OFF);
        mCameraCaptureSession.setRepeatingRequest(reqBuilder.build(), null, childHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
});

3.切换镜头:本功能参考文章

切换镜头.gif

 /**
  * 打开指定摄像头
  */
 private void changeCamera(int id) {
     closeCamera();
     try {
         if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
             throw new RuntimeException("Time out waiting to lock camera opening.");
         }
         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
             return;
         }
         mCameraManager.openCamera(id+"", mStateCallback, childHandler);
     } catch (CameraAccessException e) {
         e.printStackTrace();
     } catch (InterruptedException e) {
         throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
     }
 }
 /**
  * 关闭当前相机
  */
 private void closeCamera() {
     try {
         mCameraOpenCloseLock.acquire();
         if (null != mCameraCaptureSession) {
             mCameraCaptureSession.close();
             mCameraCaptureSession = null;
         }
         if (null != mCameraDevice) {
             mCameraDevice.close();
             mCameraDevice = null;
         }
         if (null != mImageReader) {
             mImageReader.close();
             mImageReader = null;
         }
     } catch (InterruptedException e) {
         throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
     } finally {
         mCameraOpenCloseLock.release();
     }
 }

五、录像

视频录制.png


1.辅助类

有个小bug,只能录一次...仅供参考,如果有好的解决方案,还请指教

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/8 0008:16:29<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:视频录制辅助类
 */
public class VideoRecorder2Utils {
    private MediaRecorder mediaRecorder;
    private SurfaceHolder.Callback callback;
    private SurfaceView surfaceView;
    private CameraDevice mCameraDevice;
    private int height;
    private int width;
    List<Surface> surfaces = new ArrayList<>();
    public static Size WH_2160X1080 = new Size(2160, 1080);
    public static Size WH_1920X1080 = new Size(1920, 1080);
    public static Size WH_1280X960 = new Size(1280, 960);
    public static Size WH_1440X720 = new Size(1440, 720);
    public static Size WH_1280X720 = new Size(1280, 720);
    public static Size WH_864X480 = new Size(864, 480);
    public static Size WH_800X480 = new Size(800, 480);
    public static Size WH_720X480 = new Size(720, 480);
    public static Size WH_640X480 = new Size(640, 480);
    public static Size WH_352X288 = new Size(352, 288);
    public static Size WH_320X240 = new Size(320, 240);
    public static Size WH_176X144 = new Size(176, 144);
    private CaptureRequest.Builder mPreviewBuilder;
    private CaptureRequest mCaptureRequest;
    private CameraCaptureSession mPreviewSession;
    public void create(SurfaceView surfaceView, CameraDevice cameraDevice, Size size) {
        this.surfaceView = surfaceView;
        mCameraDevice = cameraDevice;
        //创建录制的session会话中的请求
        try {
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        height = size.getHeight();
        width = size.getWidth();
        mediaRecorder = new MediaRecorder();
    }
    public void stopRecord() {
        mediaRecorder.release();
        mediaRecorder = null;
        mediaRecorder = new MediaRecorder();
        surfaces.clear();
    }
    public void stop() {
        if (mediaRecorder != null) {
            mediaRecorder.release();
        }
    }
    public void destroy() {
        if (mediaRecorder != null) {
            mediaRecorder.release();
            mediaRecorder = null;
        }
    }
    /**
     * @param path 保存的路径
     * @param name 录像视频名称(不包含后缀)
     */
    public void startRecord(String path, String name, Handler handler) {
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mediaRecorder.setVideoEncodingBitRate(700 * 1024);
        mediaRecorder.setVideoSize(width, height);
        mediaRecorder.setVideoFrameRate(24);
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        mediaRecorder.setOutputFile(path + File.separator + name + ".mp4");
        File file1 = new File(path + File.separator + name + ".mp4");
        if (file1.exists()) {
            file1.delete();
        }
        try {
            mediaRecorder.prepare();
            mediaRecorder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        review(handler);
    }
    public void review(Handler handler) {
        Surface previewSurface = surfaceView.getHolder().getSurface();
        surfaces.add(previewSurface);
        mPreviewBuilder.addTarget(previewSurface);
        Surface recorderSurface = mediaRecorder.getSurface();
        surfaces.add(recorderSurface);
        mPreviewBuilder.addTarget(recorderSurface);
        try {
            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        //创建捕获请求
                        mCaptureRequest = mPreviewBuilder.build();
                        mPreviewSession = session;
                        //设置反复捕获数据的请求,这样预览界面就会一直有数据显示
                        mPreviewSession.setRepeatingRequest(mCaptureRequest, null, handler);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                @Override
                public void onConfigureFailed(CameraCaptureSession session) {
                }
            }, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    //清除预览Session
    private void closePreviewSession() {
        if (mPreviewSession != null) {
            mPreviewSession.close();
            mPreviewSession = null;
        }
    }
}

2.使用
 /**
  * 录像
  */
 private void recodeVideo() {
     String path = Environment.getExternalStorageDirectory().getAbsolutePath();
     mVideoRecorderUtils.startRecord(path, "Video",childHandler);
     mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xffff0000));
 }
 /**
  * 停止录像
  */
 private void stopRecodeVideo() {
     mIdIvSnap.setImageTintList(ColorStateList.valueOf(0xff0FC2EF));
     mVideoRecorderUtils.stopRecord();
     startPreview();
 }
 
//打开照相机时初始化
mVideoRecorderUtils = new VideoRecorder2Utils();
mVideoRecorderUtils.create(mIdSvVideo, mCameraDevice,VideoRecorder2Utils.WH_720X480);

后记:捷文规范

1.本文成长记录及勘误表
项目源码日期备注
V0.1-github2018-1-9Android多媒体之Camera2的相关操作
2.更多关于我
笔名QQ微信爱好
张风捷特烈1981462002zdl1994328语言
我的github我的简书我的掘金个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持


icon_wx_200.png