此文章我会用Camera2 和CameraX 分别实现预览拍照录制视频

目录

此文章我会用Camera2 和CameraX 分别实现预览拍照录制视频

Camera2

APP实现 Camera2 需要提前声明

相机方向

Camera层级架构

API2流程

废话不多说 实战开始

CameraX

CameraX api

CameraX demo


Camera2

APP实现 Camera2 需要提前声明

相机方向

相机相对于手机屏幕默认方向不一致:

前置摄像头(指向与显示屏方向相同的摄像头)的传感器相对于手机旋转 270 度(顺时针),以符合 Android 兼容性定义

手机的传感器是顺时针旋转的 左横屏就是90度

 手机的传感器是顺时针旋转的 右横屏就是270度或者-90度

Camera层级架构

Camera 只是Android的一部分  所以框架层级整体跟Android一样为

Applications 应用层            -----------------------------对应camera APP

Framework层                      -----------------------------对应Java Framework

Libraries 系统运行库层       -----------------------------对应Native Framework (CameraService)

Hardwre Abstraction layer HAL硬件抽象层-----------------------------对应Camera Provider

Linux Kernel 内核层           -----------------------------对应Camera Driver

Camera根据Android 架构从上至下可分为

1)Applications: 最上层的应用,编译后生成Camera  APK;

2)Application Framework: 主要为Applications提供API;

3)JNI: 使Application Framework和Libraries可交互;

4)Libraries: 包括Camera Framework和Camera Service(camera service和camera client);

5)HAL: 硬件抽象层, 用来链接driver和 Camera Service;

6)Kernel: image sensor driver的实作.

API2流程

解释下来 其实就是CameraManager调用openCamera 下发open指令到底层,然后在CameraDevice.StateCallbackonOpened 中 拿到底层返回来的CameraDevice 通过CameraDevice去创建Session(createCaptureSession) 创建Session是需要添加对应的surface 预览的surface 有TextureView SurfaceView 拍照对应的Surface 为ImageReader 然后在Session的状态回调的onConfigured里(CameraCaptureSession.StateCallback)拿到CameraCaptureSession对象  通过CameraCaptureSession 可以申请预览 setRepeatingRequest 或者是 拍照 Capture 录制视频 需要配置 MediaRecorder 

废话不多说 实战开始

首先利用AndroidStudio 创建工程

Next

 Finish

打开布局文件

一步步实现 就慢慢来,activity_main.xml布局文件代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"><TextureViewandroid:id="@+id/previewSurfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><ImageButtonandroid:id="@+id/takePictureButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="70dp"android:background="@drawable/shape_white_ring"android:src="@drawable/shape_take_photo" /></RelativeLayout>

看到我放了两个控件一个TextureView 用来预览的控件 SurfaceView 等等也可以 还有一个拍照按钮 ImageButton

首先我们实现预览 预览是一切的基础

package com.example.camera2demo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.widget.ImageButton;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo" ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.takePictureButton:takePicture();break;}}private void takePicture() {Log.d(TAG,"-----takePicture");}}

可以看到 就是在onCreate 里去 初始化 预览控件和拍照按钮 并在 onStart 里去设置按钮 监听

在点击时间里 写了空函数 takePicture 基本框架已经实现

要实现预览 我们是需要 申请Camera权限的 所以接下来我们先申请权限

Android 6.0 之前只需要在 AndroidManifest.xml 中加入 就OK  6.0 之后需要加入并且动态申请权限

<uses-permission android:name="android.permission.CAMERA" />

代码 块如下

package com.example.camera2demo;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.widget.ImageButton;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo";//权限字符串数组 因为还需要其他 便于添加权限 选择字符串数组的形式private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA",};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);}@Overrideprotected void onResume() {super.onResume();//检查权限申请权限if (allPermissionsGranted()) {Log.d(TAG, "权限已授予");} else {Log.d(TAG, "申请权限");ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.takePictureButton:takePicture();break;}}private boolean allPermissionsGranted() {Log.d(TAG, "----- 检查权限");for (String permission : REQUIRED_PERMISSIONS) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//权限回调 申请完权限之后 返回的结果在这里接收@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//这个1 是申请权限时的第三个参数if (requestCode == 1) {if (allPermissionsGranted()) {openCamera();} else {ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}}private void openCamera() {Log.d(TAG,"----------openCamera");}private void takePicture() {Log.d(TAG,"-----takePicture");}}

在刚刚 的基础之上加了三个函数 一个检查权限的函数 一个权限回调函数 一个 openCamera的空函数 权限获取之后我们就正式进入openCamera

package com.example.camera2demo;import static java.lang.String.valueOf;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.TextureView;
import android.view.View;
import android.widget.ImageButton;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo";//权限字符串数组 因为还需要其他 便于添加权限 选择字符串数组的形式private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA",};//处理回调函数的线程private HandlerThread mCameraThread;private Handler mCameraHandler;//具体的相机设备private CameraDevice mCameraDevice;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);//开启线程 用于处理回调函数 可开可不开startCameraThread();}@Overrideprotected void onResume() {super.onResume();//检查权限申请权限if (allPermissionsGranted()) {Log.d(TAG, "权限已授予");} else {Log.d(TAG, "申请权限");ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.takePictureButton:takePicture();break;}}private boolean allPermissionsGranted() {Log.d(TAG, "----- 检查权限");for (String permission : REQUIRED_PERMISSIONS) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//权限回调 申请完权限之后 返回的结果在这里接收@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//这个1 是申请权限时的第三个参数if (requestCode == 1) {if (allPermissionsGranted()) {openCamera();} else {ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}}//开启线程 用于处理回调函数 可开可不开private void startCameraThread() {mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());}@SuppressLint("MissingPermission")private void openCamera() {Log.d(TAG, "----------openCamera");// 1.获取CameraManager 需要注意的是这里需要强转CameraManager cameraManager = (CameraManager) MainActivity.this.getSystemService(CAMERA_SERVICE);// 2.通过CameraManager 获取相机IDtry {String cameraId[] = cameraManager.getCameraIdList();for (String s : cameraId) {Log.d(TAG, "--------- cameraId = " + s);}// 3. 获取 0 后置的相机信息CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics("0");// 例如获取分辨率的信息 通过对应的KEY 获取对应信息StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);for (Size size : sizes) {Log.d(TAG,"size = "+size);}// 我就不去把ID 以及分辨率去放到代码中去写那么写代码很庞大 我就遍历一次输入一个相机支持的分辨率就好了// 4. 如果我这么写 2 3 步骤可以省略 openCamera 需要三个参数 第一个为相机ID  第二个是CameraDevice.StateCallback回调// 第三个是处理回调的线程 这里需要检查权限cameraManager.openCamera(valueOf(0),cameraDeviceStateCallback,mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {}};private void takePicture() {Log.d(TAG, "-----takePicture");}}

这段代码 开了个线程来处理回调函数可以要可不要,如果选择在主线程执行就可以穿null,加入了 openCamera 的关键 步骤获取CameraManger 通过CameraManger 可以获取到相机ID 如下图

还可以获取相机的特征信息 比如支持的分辨率 如下图

 最核心的是可以下发openCamera 指令 

然后在回调里拿到 CameraDevice   拿到CameraDevice 就可以创建Session 接下来是创建Session 建立预览

package com.example.camera2demo;import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_AUTO;
import static java.lang.String.valueOf;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
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.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.ImageButton;import java.util.Arrays;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo";//权限字符串数组 因为还需要其他 便于添加权限 选择字符串数组的形式private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA",};//处理回调函数的线程private HandlerThread mCameraThread;private Handler mCameraHandler;//具体的相机设备private CameraDevice mCameraDevice;//创建Session的构建者private CaptureRequest.Builder mCaptureRequestBuilder;//CameraCaptureSession是创建预览拍照 录像请求的关键  可以理解成管道 通过这个管道下发不同的参数请求 得到不同的效果private CameraCaptureSession mCameraCaptureSession;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//设置预览控件的监听  防止surface 没有渲染好就打开相机了mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);//开启线程 用于处理回调函数 可开可不开startCameraThread();}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.takePictureButton:takePicture();break;}}private boolean allPermissionsGranted() {Log.d(TAG, "----- 检查权限");for (String permission : REQUIRED_PERMISSIONS) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//权限回调 申请完权限之后 返回的结果在这里接收@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//这个1 是申请权限时的第三个参数if (requestCode == 1) {if (allPermissionsGranted()) {openCamera();} else {ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}}//开启线程 用于处理回调函数 可开可不开private void startCameraThread() {mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());}@SuppressLint("MissingPermission")private void openCamera() {Log.d(TAG, "----------openCamera");// 1.获取CameraManager 需要注意的是这里需要强转CameraManager cameraManager = (CameraManager) MainActivity.this.getSystemService(CAMERA_SERVICE);// 2.通过CameraManager 获取相机IDtry {String cameraId[] = cameraManager.getCameraIdList();for (String s : cameraId) {Log.d(TAG, "--------- cameraId = " + s);}// 3. 获取 0 后置的相机信息CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics("0");// 例如获取分辨率的信息 通过对应的KEY 获取对应信息StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);for (Size size : sizes) {Log.d(TAG, "size = " + size);}// 我就不去把ID 以及分辨率去放到代码中去写那么写代码很庞大 我就遍历一次输入一个相机支持的分辨率就好了// 4. 如果我这么写 2 3 步骤可以省略 openCamera 需要三个参数 第一个为相机ID  第二个是CameraDevice.StateCallback回调// 第三个是处理回调的线程如果不开线程可以传null 即在主线程执行  这里需要检查权限cameraManager.openCamera(valueOf(0), cameraDeviceStateCallback, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;createPreView();}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {}};private void createPreView() {//创建预览 之前创建Session 创建Session 需要接收数据的Surface//下面就是获取SurfaceSurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();//设置SurfaceView的缓冲区分辨率  与手机用的分辨一致 就可以防止预览拉伸surfaceTexture.setDefaultBufferSize(4160, 1856);Surface previewSurface = new Surface(surfaceTexture);//如果是surfaceView 如下
//      SurfaceHolder surfaceHolder = mPreviewSurfaceView.getHolder();
//      surfaceHolder.setFixedSize(1920,1080);
//      surface = surfaceHolder.getSurface();try {//创建Session的构建者mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//将Surface 传递下去mCaptureRequestBuilder.addTarget(previewSurface);//通过CameraMetadata 设置闪光灯 自动对焦等等 我这里是设置自动对焦mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//创建SessionmCameraDevice.createCaptureSession(Arrays.asList(previewSurface), cameraCaptureSessionStateCallback, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraCaptureSession.StateCallback cameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mCameraCaptureSession = session;try {session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {}};//很多人写到这里觉得大功告成了  是 申请权限下来 openCamera 一点问题没有 但是 发现再次进入  崩溃了   还找不到原因 这里是因为 我们openCamera时Surface//还么渲染好 导致的解决办法有两个//1TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {//检查权限申请权限if (allPermissionsGranted()) {Log.d(TAG, "权限已授予");openCamera();} else {Log.d(TAG, "申请权限");ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}@Overridepublic void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}};//2.在onCreate里添加下面这段
//        mTextureView.post(new Runnable() {
//            @Override
//            public void run() {
//                if (allPermissionsGranted()) {
//                    Log.d(TAG,"权限ok");
//                    startCamera();
//                } else {
//                    Log.d(TAG,"申请权限");
//                    ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
//                }
//            }
//        });private void takePicture() {Log.d(TAG, "-----takePicture");}}

至此简单的预览完成了

预览拍照代码

package com.example.camera2demo;import static java.lang.String.valueOf;import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
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.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo";//权限字符串数组 因为还需要其他 便于添加权限 选择字符串数组的形式private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA","android.permission.WRITE_EXTERNAL_STORAGE","android.permission.READ_EXTERNAL_STORAGE",};//处理回调函数的线程private HandlerThread mCameraThread;private Handler mCameraHandler;//具体的相机设备private CameraDevice mCameraDevice;//创建Session的构建者private CaptureRequest.Builder mCaptureRequestBuilder;//CameraCaptureSession是创建预览拍照 录像请求的关键  可以理解成管道 通过这个管道下发不同的参数请求 得到不同的效果private CameraCaptureSession mCameraCaptureSession;private ImageReader mImageReader = null;protected ImageView mThumbnail;//用于处理拍照方向问题protected static final SparseIntArray ORIENTATION = new SparseIntArray();static {ORIENTATION.append(Surface.ROTATION_0, 90);//90  270 还没处理ORIENTATION.append(Surface.ROTATION_90, 0);ORIENTATION.append(Surface.ROTATION_180, 270);ORIENTATION.append(Surface.ROTATION_270, 180);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//设置预览控件的监听  防止surface 没有渲染好就打开相机了mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);mThumbnail = findViewById(R.id.thumbnail);mThumbnail.setOnClickListener(this);//开启线程 用于处理回调函数 可开可不开startCameraThread();//去掉导航栏getSupportActionBar().hide();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//透明状态栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//透明导航栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);}}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.takePictureButton:takePicture();break;case R.id.thumbnail:gotoGallery();break;}}private boolean allPermissionsGranted() {Log.d(TAG, "----- 检查权限");for (String permission : REQUIRED_PERMISSIONS) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//权限回调 申请完权限之后 返回的结果在这里接收@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//这个1 是申请权限时的第三个参数if (requestCode == 1) {if (allPermissionsGranted()) {openCamera();} else {ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}}//开启线程 用于处理回调函数 可开可不开private void startCameraThread() {mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());}@SuppressLint("MissingPermission")private void openCamera() {Log.d(TAG, "----------openCamera");// 1.获取CameraManager 需要注意的是这里需要强转CameraManager cameraManager = (CameraManager) MainActivity.this.getSystemService(CAMERA_SERVICE);// 2.通过CameraManager 获取相机IDtry {String cameraId[] = cameraManager.getCameraIdList();for (String s : cameraId) {Log.d(TAG, "--------- cameraId = " + s);}// 3. 获取 0 后置的相机信息CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics("0");// 例如获取分辨率的信息 通过对应的KEY 获取对应信息StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);for (Size size : sizes) {Log.d(TAG, "size = " + size);}// 我就不去把ID 以及分辨率去放到代码中去写那么写代码很庞大 我就遍历一次输入一个相机支持的分辨率就好了// 4. 如果我这么写 2 3 步骤可以省略 openCamera 需要三个参数 第一个为相机ID  第二个是CameraDevice.StateCallback回调// 第三个是处理回调的线程如果不开线程可以传null 即在主线程执行  这里需要检查权限cameraManager.openCamera(valueOf(0), cameraDeviceStateCallback, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;createPreView();}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {}};private void createPreView() {initImageReader(); //拍照要初始化//创建预览 之前创建Session 创建Session 需要接收数据的Surface//下面就是获取SurfaceSurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();//设置SurfaceView的缓冲区分辨率  与手机用的分辨一致 就可以防止预览拉伸surfaceTexture.setDefaultBufferSize(1920, 1080);Surface previewSurface = new Surface(surfaceTexture);//如果是surfaceView 如下
//      SurfaceHolder surfaceHolder = mPreviewSurfaceView.getHolder();
//      surfaceHolder.setFixedSize(1920,1080);
//      surface = surfaceHolder.getSurface();try {//创建Session的构建者mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//将Surface 传递下去mCaptureRequestBuilder.addTarget(previewSurface);//通过CameraMetadata 设置闪光灯 自动对焦等等 我这里是设置自动对焦mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//创建SessionmCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), cameraCaptureSessionStateCallback, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraCaptureSession.StateCallback cameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mCameraCaptureSession = session;try {session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {}};//很多人写到这里觉得大功告成了  是 申请权限下来 openCamera 一点问题没有 但是 发现再次进入  崩溃了   还找不到原因 这里是因为 我们openCamera时Surface//还么渲染好 导致的解决办法有两个//1TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {//检查权限申请权限if (allPermissionsGranted()) {Log.d(TAG, "权限已授予");openCamera();} else {Log.d(TAG, "申请权限");ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}showThumbnail();}@Overridepublic void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}};//2.在onCreate里添加下面这段
//        mTextureView.post(new Runnable() {
//            @Override
//            public void run() {
//                if (allPermissionsGranted()) {
//                    Log.d(TAG,"权限ok");
//                    startCamera();
//                } else {
//                    Log.d(TAG,"申请权限");
//                    ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
//                }
//            }
//        });//拍照 开始拍照之前得配置对应的Surfac 就是ImageReaderprotected void initImageReader() {Log.d("djh", "--------------------initImageReader");//四个参数分别是照片的分辨率 格式   最多获取几帧mImageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1);//设置ImageReader监听,当有图像流数据可用时会回调onImageAvailablemImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader imageReader) {Toast.makeText(MainActivity.this, "图片已保存", Toast.LENGTH_SHORT).show();//获得Image 数据Image image = imageReader.acquireNextImage();//可以开启线程保存图片new Thread(new Runnable() {@Overridepublic void run() {//字节缓冲ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] data = new byte[buffer.remaining()];buffer.get(data);String path = Environment.getExternalStorageDirectory() + "/DCIM/camera/myPicture"+ System.currentTimeMillis() + ".jpg";File imageFile = new File(path);FileOutputStream fos = null;try {fos = new FileOutputStream(imageFile);fos.write(data, 0, data.length);} catch (IOException e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}broadcast();image.close(); // 必须关闭 不然拍第二章会报错showThumbnail();}}}).start();}}, null);}private void takePicture() {Log.d(TAG, "-----takePicture");try {//拍照跟预览的区别就是Builder不一样回调不一样仅此而已CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(mImageReader.getSurface());int rotation = getWindowManager().getDefaultDisplay().getRotation();captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));mCameraCaptureSession.stopRepeating();mCameraCaptureSession.capture(captureBuilder.build(), captureCallback, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {super.onCaptureStarted(session, request, timestamp, frameNumber);try {session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, null);} catch (CameraAccessException e) {e.printStackTrace();}}};// 通知广播刷新相册protected void broadcast() {Log.d("djh", "--------------------broadcast");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);MainActivity.this.sendBroadcast(intent);}//预览拍照完成显示缩略图protected void showThumbnail() {ArrayList<String> imageList = getImageFilePath();String path = imageList.get(imageList.size() - 1);if (path.contains("jpg")) {Bitmap bitmap = (Bitmap) BitmapFactory.decodeFile(path);//又是bitmap 为空  忘记给mImageView finViewByIdmThumbnail.setImageBitmap(bitmap);int rotation = getWindowManager().getDefaultDisplay().getRotation();mThumbnail.setRotation(ORIENTATION.get(rotation));} else {MediaMetadataRetriever retriever = new MediaMetadataRetriever();retriever.setDataSource(path);//取第一帧Bitmap bitmap = retriever.getFrameAtTime(1);mThumbnail.setImageBitmap(bitmap);}}//遍历系统相册protected ArrayList<String> getImageFilePath() {ArrayList<String> imageList = new ArrayList<>();File file = new File(Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera");File[] dirEpub = file.listFiles();if (dirEpub.length != 0) {for (int i = 0; i < dirEpub.length; i++) {String fileName = dirEpub[i].toString();imageList.add(fileName);Log.i("File", "File name = " + fileName);}}return imageList;}//跳转相册 (画廊)protected void gotoGallery() {ArrayList<String> temp = getImageFilePath();String lastPath = temp.get(temp.size() - 1);Uri uri = getMediaUriFromPath(this, lastPath);Intent intent = new Intent("com.android.camera.action.REVIEW", uri);intent.setData(uri);startActivity(intent);}@SuppressLint("Range")public Uri getMediaUriFromPath(Context context, String path) {Uri uri = null;if (path.contains("jpg")) {Uri picUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;Cursor cursor = context.getContentResolver().query(picUri,null,MediaStore.Images.Media.DISPLAY_NAME + "= ?",new String[]{path.substring(path.lastIndexOf("/") + 1)},null);if (cursor.moveToFirst()) {uri = ContentUris.withAppendedId(picUri,cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)));}cursor.close();} else if (path.contains("mp4")) {Uri mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;Cursor cursor = context.getContentResolver().query(mediaUri,null,MediaStore.Video.Media.DISPLAY_NAME + "= ?",new String[]{path.substring(path.lastIndexOf("/") + 1)},null);if (cursor.moveToFirst()) {uri = ContentUris.withAppendedId(mediaUri,cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media._ID)));}cursor.close();}return uri;}}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"><TextureViewandroid:id="@+id/previewSurfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><ImageButtonandroid:id="@+id/takePictureButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="70dp"android:background="@drawable/shape_white_ring"android:src="@drawable/shape_take_photo" /><ImageViewandroid:id="@+id/thumbnail"android:layout_width="45dp"android:layout_height="55dp"android:layout_alignParentBottom="true"android:layout_marginLeft="50dp"android:layout_marginBottom="70dp" /><ImageButtonandroid:id="@+id/changeCamera"android:layout_width="45dp"android:layout_height="45dp"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_marginRight="50dp"android:layout_marginBottom="77dp"android:background="@drawable/camera_quick_switch" /></RelativeLayout>

再下来就是录像了

录像跟拍照很相似 只是 CaptureRequest.Builder对象不一样 需要配置一个MediaRecorder

预览拍照录制视频的完整代码

package com.example.camera2demo;import static java.lang.String.valueOf;import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
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.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;public class MainActivity extends AppCompatActivity implements View.OnClickListener {//预览画面控件private TextureView mTextureView;//拍照按钮private ImageButton mTakePictureButton;//日志的tagprivate final String TAG = "Camera2Demo";//权限字符串数组 因为还需要其他 便于添加权限 选择字符串数组的形式private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA","android.permission.WRITE_EXTERNAL_STORAGE","android.permission.READ_EXTERNAL_STORAGE","android.permission.RECORD_AUDIO",};//处理回调函数的线程private HandlerThread mCameraThread;private Handler mCameraHandler;//具体的相机设备private CameraDevice mCameraDevice;//创建Session的构建者private CaptureRequest.Builder mPreviewBuilder;//CameraCaptureSession是创建预览拍照 录像请求的关键  可以理解成管道 通过这个管道下发不同的参数请求 得到不同的效果private CameraCaptureSession mCameraCaptureSession;private ImageReader mImageReader = null;protected ImageView mThumbnail;//用于处理拍照方向问题protected static final SparseIntArray ORIENTATION = new SparseIntArray();static {ORIENTATION.append(Surface.ROTATION_0, 90);//90  270 还没处理ORIENTATION.append(Surface.ROTATION_90, 0);ORIENTATION.append(Surface.ROTATION_180, 270);ORIENTATION.append(Surface.ROTATION_270, 180);}private Button mPhotoMode;private Button mVideoMode;//拍照按钮private ImageButton mCapTureVideoButton;private MediaRecorder mMediaRecorder;private boolean bStop = false;private ImageButton mChangeCamera;private String mCameraId = "0";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化预览控件mTextureView = findViewById(R.id.previewSurfaceView);//设置预览控件的监听  防止surface 没有渲染好就打开相机了mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);//初始化拍照按钮mTakePictureButton = findViewById(R.id.takePictureButton);mThumbnail = findViewById(R.id.thumbnail);mThumbnail.setOnClickListener(this);//开启线程 用于处理回调函数 可开可不开startCameraThread();//去掉导航栏getSupportActionBar().hide();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//透明状态栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//透明导航栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);}mPhotoMode = findViewById(R.id.photoMode);mPhotoMode.setOnClickListener(this);mVideoMode = findViewById(R.id.videoMode);mVideoMode.setOnClickListener(this);mCapTureVideoButton = findViewById(R.id.captureVideoButton);mCapTureVideoButton.setOnClickListener(this);mChangeCamera = findViewById(R.id.changeCamera);mChangeCamera.setOnClickListener(this);}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onStart() {super.onStart();//设置拍照按钮监听mTakePictureButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.takePictureButton:takePicture();break;case R.id.thumbnail:gotoGallery();break;case R.id.photoMode:mPhotoMode.setVisibility(View.GONE);mVideoMode.setVisibility(View.VISIBLE);mTakePictureButton.setVisibility(View.GONE);mCapTureVideoButton.setVisibility(View.VISIBLE);break;case R.id.videoMode:mVideoMode.setVisibility(View.GONE);mPhotoMode.setVisibility(View.VISIBLE);mTakePictureButton.setVisibility(View.VISIBLE);mCapTureVideoButton.setVisibility(View.GONE);break;case R.id.captureVideoButton:if (!bStop) {bStop = true;captureVideo();} else {bStop = false;stopVideo();}break;case R.id.changeCamera:changeCameraId();break;}}private boolean allPermissionsGranted() {Log.d(TAG, "----- 检查权限");for (String permission : REQUIRED_PERMISSIONS) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//权限回调 申请完权限之后 返回的结果在这里接收@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//这个1 是申请权限时的第三个参数if (requestCode == 1) {if (allPermissionsGranted()) {openCamera();} else {ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}}}//开启线程 用于处理回调函数 可开可不开private void startCameraThread() {mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());}@SuppressLint("MissingPermission")private void openCamera() {Log.d(TAG, "----------openCamera");// 1.获取CameraManager 需要注意的是这里需要强转CameraManager cameraManager = (CameraManager) MainActivity.this.getSystemService(CAMERA_SERVICE);// 2.通过CameraManager 获取相机IDtry {String cameraId[] = cameraManager.getCameraIdList();for (String s : cameraId) {Log.d(TAG, "--------- cameraId = " + s);}// 3. 获取 0 后置的相机信息CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics("0");// 例如获取分辨率的信息 通过对应的KEY 获取对应信息StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);for (Size size : sizes) {Log.d(TAG, "size = " + size);}// 我就不去把ID 以及分辨率去放到代码中去写那么写代码很庞大 我就遍历一次输入一个相机支持的分辨率就好了// 4. 如果我这么写 2 3 步骤可以省略 openCamera 需要三个参数 第一个为相机ID  第二个是CameraDevice.StateCallback回调// 第三个是处理回调的线程如果不开线程可以传null 即在主线程执行  这里需要检查权限cameraManager.openCamera(mCameraId, cameraDeviceStateCallback, null);} catch (CameraAccessException e) {e.printStackTrace();}}CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;createPreView();}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {}};private void createPreView() {initImageReader(); //拍照要初始化configMediaRecorder();//录制视频需要初始化//创建预览 之前创建Session 创建Session 需要接收数据的Surface//下面就是获取SurfaceSurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();//设置SurfaceView的缓冲区分辨率  与手机用的分辨一致 就可以防止预览拉伸surfaceTexture.setDefaultBufferSize(1920, 1080);Surface previewSurface = new Surface(surfaceTexture);//如果是surfaceView 如下
//      SurfaceHolder surfaceHolder = mPreviewSurfaceView.getHolder();
//      surfaceHolder.setFixedSize(1920,1080);
//      surface = surfaceHolder.getSurface();try {//创建Session的构建者mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//将Surface 传递下去mPreviewBuilder.addTarget(previewSurface);mPreviewBuilder.addTarget(mMediaRecorder.getSurface());//通过CameraMetadata 设置闪光灯 自动对焦等等 我这里是设置自动对焦mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//创建SessionmCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface(), mMediaRecorder.getSurface()), cameraCaptureSessionStateCallback, null);} catch (CameraAccessException e) {e.printStackTrace();}}CameraCaptureSession.StateCallback cameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mCameraCaptureSession = session;try {session.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {}};//很多人写到这里觉得大功告成了  是 申请权限下来 openCamera 一点问题没有 但是 发现再次进入  崩溃了   还找不到原因 这里是因为 我们openCamera时Surface//还么渲染好 导致的解决办法有两个//1TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {//检查权限申请权限if (allPermissionsGranted()) {Log.d(TAG, "权限已授予");openCamera();} else {Log.d(TAG, "申请权限");ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, 1);}//showThumbnail();}@Overridepublic void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}};//2.在onCreate里添加下面这段
//        mTextureView.post(new Runnable() {
//            @Override
//            public void run() {
//                if (allPermissionsGranted()) {
//                    Log.d(TAG,"权限ok");
//                    startCamera();
//                } else {
//                    Log.d(TAG,"申请权限");
//                    ActivityCompat.requestPermissions(MainActivity.this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
//                }
//            }
//        });//拍照 开始拍照之前得配置对应的Surfac 就是ImageReaderprotected void initImageReader() {Log.d("djh", "--------------------initImageReader");//四个参数分别是照片的分辨率 格式   最多获取几帧mImageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1);//设置ImageReader监听,当有图像流数据可用时会回调onImageAvailablemImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader imageReader) {Toast.makeText(MainActivity.this, "图片已保存", Toast.LENGTH_SHORT).show();//获得Image 数据Image image = imageReader.acquireNextImage();//可以开启线程保存图片new Thread(new Runnable() {@Overridepublic void run() {//字节缓冲ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] data = new byte[buffer.remaining()];buffer.get(data);String path = Environment.getExternalStorageDirectory() + "/DCIM/camera/myPicture"+ System.currentTimeMillis() + ".jpg";File imageFile = new File(path);FileOutputStream fos = null;try {fos = new FileOutputStream(imageFile);fos.write(data, 0, data.length);} catch (IOException e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}broadcast();image.close(); // 必须关闭 不然拍第二章会报错//showThumbnail();}}}).start();}}, null);}private void takePicture() {Log.d(TAG, "-----takePicture");try {//拍照跟预览的区别就是Builder不一样回调不一样仅此而已CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(mImageReader.getSurface());int rotation = getWindowManager().getDefaultDisplay().getRotation();if (mCameraId.equals("1")) {captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation) + 180);} else {captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));}mCameraCaptureSession.stopRepeating();mCameraCaptureSession.capture(captureBuilder.build(), captureCallback, null);} catch (CameraAccessException e) {e.printStackTrace();}}CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {super.onCaptureStarted(session, request, timestamp, frameNumber);try {session.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}};// 通知广播刷新相册protected void broadcast() {Log.d("djh", "--------------------broadcast");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);MainActivity.this.sendBroadcast(intent);}//预览拍照完成显示缩略图protected void showThumbnail() {ArrayList<String> imageList = getImageFilePath();String path = imageList.get(imageList.size() - 1);if (path.contains("jpg")) {Bitmap bitmap = BitmapFactory.decodeFile(path);mThumbnail.setImageBitmap(bitmap);int rotation = getWindowManager().getDefaultDisplay().getRotation();mThumbnail.setRotation(ORIENTATION.get(rotation));} else if (path.contains("mp4")) {MediaMetadataRetriever retriever = new MediaMetadataRetriever();retriever.setDataSource(path);//获取第1帧Bitmap bitmap = retriever.getFrameAtTime(1);mThumbnail.setImageBitmap(bitmap);}}//遍历系统相册protected ArrayList<String> getImageFilePath() {ArrayList<String> imageList = new ArrayList<>();File file = new File(Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera");File[] dirEpub = file.listFiles();if (dirEpub.length != 0) {for (int i = 0; i < dirEpub.length; i++) {String fileName = dirEpub[i].toString();imageList.add(fileName);Log.i("File", "File name = " + fileName);}}return imageList;}//跳转相册 (画廊)protected void gotoGallery() {ArrayList<String> temp = getImageFilePath();String lastPath = temp.get(temp.size() - 1);Uri uri = getMediaUriFromPath(this, lastPath);Intent intent = new Intent("com.android.camera.action.REVIEW", uri);intent.setData(uri);startActivity(intent);}@SuppressLint("Range")public Uri getMediaUriFromPath(Context context, String path) {Uri uri = null;if (path.contains("jpg")) {Uri picUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;Cursor cursor = context.getContentResolver().query(picUri,null,MediaStore.Images.Media.DISPLAY_NAME + "= ?",new String[]{path.substring(path.lastIndexOf("/") + 1)},null);if (cursor.moveToFirst()) {uri = ContentUris.withAppendedId(picUri,cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)));}cursor.close();} else if (path.contains("mp4")) {Uri mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;Cursor cursor = context.getContentResolver().query(mediaUri,null,MediaStore.Video.Media.DISPLAY_NAME + "= ?",new String[]{path.substring(path.lastIndexOf("/") + 1)},null);if (cursor.moveToFirst()) {uri = ContentUris.withAppendedId(mediaUri,cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media._ID)));}cursor.close();}return uri;}/*** 配置录制视频相关数据*/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();//如果是前置if (mCameraId.equals("1")) {mMediaRecorder.setOrientationHint(270);} else {mMediaRecorder.setOrientationHint(90);}//设置音频来源mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置视频来源mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);//设置输出格式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//设置音频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择AACmMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置视频编码格式,请注意这里使用默认,实际app项目需要考虑兼容问题,应该选择H264mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置比特率 一般是 1*分辨率 到 10*分辨率 之间波动。比特率越大视频越清晰但是视频文件也越大。mMediaRecorder.setVideoEncodingBitRate(8 * 1080 * 1920);//设置帧数 选择 30即可, 过大帧数也会让视频文件更大当然也会更流畅,但是没有多少实际提升。人眼极限也就30帧了。mMediaRecorder.setVideoFrameRate(30);//Size size = getMatchingSize();mMediaRecorder.setVideoSize(1920, 1080);Surface surface = new Surface(mTextureView.getSurfaceTexture());mMediaRecorder.setPreviewDisplay(surface);mMediaRecorder.setOutputFile(file.getAbsolutePath());try {mMediaRecorder.prepare();} catch (IOException e) {e.printStackTrace();}}}private void captureVideo() {Log.d(TAG, "---------  captureVideo");mMediaRecorder.start();}private void stopVideo() {Log.d(TAG, "---------  stopVideo");mMediaRecorder.stop();broadcast();createPreView();}private void changeCameraId() {Log.d(TAG, "changeCamera: success");if (mCameraId.equals(String.valueOf(CameraCharacteristics.LENS_FACING_BACK))) {Toast.makeText(this, "前置转后置", Toast.LENGTH_SHORT).show();mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);} else {Toast.makeText(this, "后置转前置", Toast.LENGTH_SHORT).show();mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_BACK);}mCameraDevice.close();openCamera();}}

xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"><TextureViewandroid:id="@+id/previewSurfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><ImageButtonandroid:id="@+id/takePictureButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="70dp"android:visibility="visible"android:background="@drawable/shape_white_ring"android:src="@drawable/shape_take_photo" /><ImageButtonandroid:id="@+id/captureVideoButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alagnParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="70dp"android:visibility="gone"android:background="@drawable/shape_white_ring"android:src="@drawable/shape_take_video" /><ImageViewandroid:id="@+id/thumbnail"android:layout_width="45dp"android:layout_height="55dp"android:layout_alignParentBottom="true"android:layout_marginLeft="50dp"android:layout_marginBottom="70dp" /><ImageButtonandroid:id="@+id/changeCamera"android:layout_width="45dp"android:layout_height="45dp"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_marginRight="50dp"android:layout_marginBottom="77dp"android:background="@drawable/camera_quick_switch" /><Buttonandroid:id="@+id/photoMode"android:layout_width="85dp"android:layout_height="40dp"android:text="Photo"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:visibility="visible"android:layout_marginBottom="160dp"/><Buttonandroid:id="@+id/videoMode"android:layout_width="85dp"android:text="Video"android:layout_height="40dp"android:visibility="gone"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:layout_marginBottom="160dp"/></RelativeLayout>

CameraX

用CameraX 时需要注意的是 build.gradle里面需要添加

//CameraXdef camerax_version = "1.0.0-alpha02"implementation "androidx.camera:camera-core:${camerax_version}"implementation "androidx.camera:camera-camera2:${camerax_version}"

CameraX api

CameraX 可以理解成将Camera2的API 进行了封装  使用变得更加简洁

实现预览 获取Camera权限之后  通过PreviewConfig去设置分辨率,画面比例,CameraId等等

再 new 一个 Preview 对象时将PreviewConfig对象传递下去,再利用bindTolifecycle 把Preview 对象 传递下去就完成预览了,相比Camera2是不是要简单很多呢

拍照也很简单 只需要用ImageCaptureConfig 配置拍照信息 比例 CameraId 拍照方式等等

再去new一个ImageCapture对象 将ImageCaptureConfig传递下去 再给 bindTolifecycle 多一个参数 这个参数传递ImageCapture对象

录像 VideoCaptureConfig

  • Quality.UHD,适用于 4K 超高清视频大小 (2160p)
  • Quality.FHD,适用于全高清视频大小 (1080p)
  • Quality.HD,适用于高清视频大小 (720p)
  • Quality.SD,适用于标清视频大小 (480p)

CameraX demo 改天补上临时有事

Camera2 和CameraX 从入门到精通 java实现相关推荐

  1. 视频教程-Spring Cloud微服务--入门到精通-Java

    Spring Cloud微服务--入门到精通 本系列课程由多位老师共同录制而成,旨在为想要学习Java的用户提供一套系统的成长方案. Java从入门到进阶 ¥59.00 立即订阅 扫码下载「CSDN程 ...

  2. 视频教程-springboot从入门到精通-Java

    springboot从入门到精通 本人具有7年java开发经验,两年java教学经验,擅长java开发相关技术,能够熟掌握并应用目前主流web开发技术,如SSH,SSM等,数据库开发技术oracle, ...

  3. 视频教程-Spring框架快速入门到精通-Java

    Spring框架快速入门到精通 十年项目开发经验,主要从事java相关的开发,熟悉各种mvc开发框架. 王振伟 ¥18.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 ...

  4. 视频教程-mybatis快速入门到精通-Java

    mybatis快速入门到精通 十年项目开发经验,主要从事java相关的开发,熟悉各种mvc开发框架. 王振伟 ¥18.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 A ...

  5. 火爆B站的阿玮老师,他带着课程走来了(含4天入门到精通Java直播课)

    他来啦.他来啦 终于把百万B站用户都爱的阿玮老师盼来啦! 此处应该有掌声 如果你是B站资深用户 相信早就耳闻阿玮老师的大名啦 阿玮老师在B站有多受欢迎,你往下康康~~ 这究竟是什么神仙老师呀 来啦就出 ...

  6. 最新《JPA入门到精通JAVA进阶项目实战》

    『课程介绍』: 通过对本课程的学习,能够对JPA能够有全面的认识,简化现有Java EE和Java SE应用开发工作.用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐 ...

  7. Java快速入门到精通— Java break语句详解

    所有流行的编程语言中都有循环语句.JAVA 中采用的循环语句与C语言中的循环语句相似,主要有 while.do-while 和 for! 那么在某些时候需要在某种条件出现时强行终止循环,而不是等到循环 ...

  8. java从入门到精通_想要开始学java?你要的java从入门到精通布列如下!

    java从入门到精通,让我来告诉你! 毫无疑问,java是当下最火的编程语言之一.对于许多未曾涉足计算机编程的领域「小白」来说,深入地掌握java看似是一件十分困难的事.其实,只要掌握了科学的学习方法 ...

  9. 《Java 开发从入门到精通》—— 2.2 编写第一段Java程序

    本节书摘来异步社区<Java 开发从入门到精通>一书中的第2章,第2.2节,作者: 扶松柏 , 陈小玉,更多章节内容可以访问云栖社区"异步社区"公众号查看. 2.2 编 ...

最新文章

  1. Zookeeper 的典型应用场景场景
  2. 百度智能云发布时空数据管理平台,打造一体化数据中台
  3. AQS理解之五—并发编程中AQS的理解
  4. SAP UI5 router的初始化逻辑
  5. JAVA面试题------------final 关键字是干什么用的?谈谈你的理解。
  6. 在Ubuntu Linux中获取上次访问的文件时间
  7. 将状态机模式实现为流处理器
  8. iphone viewdidLoad运行以及参数的传递。
  9. es6添加删除class_es6-class的基本用法
  10. h5 input 阴影_html5中input表单加边框,阴影效果
  11. linux 进程 D 状态,Linux 进程的 Uninterruptible sleep(D) 状态
  12. 计算机毕业设计Java宠物医院后台管理系统设计与实现(源码+系统+mysql数据库+lw文档)
  13. Devexpress 各版本中文语言包
  14. java实现冒泡算法
  15. ps-通道+高低频磨皮去斑
  16. android 出错信息为:Class 'Anonymous class derived from Handler' must either be declared abstract or imple
  17. 求x的n次方编程_C语言 用递归方法求X的n次方
  18. 名字解析/DNS服务
  19. cae计算机仿真分析技术,cae分析.doc
  20. 南宁市第二十六中学:教研路漫漫,花香伴我行

热门文章

  1. Matlab光标在线上移动,vi光标移动及常用指令
  2. 建网站框架LAMP之:PHP的安装
  3. 如何创建python虚拟环境
  4. 信创办公–基于WPS的EXCEL最佳实践系列 (规整数据摆放)
  5. 二、搭建mysql服务器,创建数据库
  6. 【日语】记单词的学习笔记
  7. java mybatis 批量更新数据_Mybatis批量更新详解
  8. 素材解析字体下载网!设计师必备!!!
  9. DirectX 画三角形 正交投影
  10. Calibre导出书库图书列表