工具类

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
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.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Size;
import android.view.Gravity;
import android.view.Surface;
import android.view.TextureView;
import android.view.ViewGroup;
import android.widget.FrameLayout;import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;import com.example.mycamera.utils.L;
import com.example.mycamera.utils.ScreenUtil;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @描述 需要在使用的Activity节点添加:硬件加速* android:hardwareAccelerated="true"*/
public class Camera2Util {private Context context;private CameraCallBack cameraCallBack;private CameraManager cameraManager;// 默认相机id是0 LENS_FACING_FRONT,LENS_FACING_BACKprivate int cameraId = CameraCharacteristics.LENS_FACING_FRONT;private CameraDevice mCameraDevice;private String savePath;private FrameLayout frameLayout;// textureView的父布局private TextureView textureView;private SurfaceTexture mSurfaceTexture;private Surface mSurface, recordSurface;private CaptureRequest.Builder mPreviewRequestBuilder;private CameraCaptureSession mCameraCaptureSession;private CaptureRequest request;private MediaRecorder mRecorder;private ExecutorService service;private int needSetOrientation = 0;// 设置默认的拍照方向private boolean isInitOk = false;// 是否初始化成功private boolean isSessionClosed = true;// captureSession是否被关闭private boolean isRecording = false;// 是否正在录制private boolean isRecordFinish = false;// 是否已完成录制private boolean isRecordPrepareOk = false;// 相机是否已准备好可以录制了private final long RECORD_SAVE_DELAY_TIME_LONG = 1000;// 停止录制后延时1秒反馈(用于存储时间)private final int HANDLER_RECORD_COMPLETE = 2;// 用户手动停止private final int HANDLER_ERR = 3;// 拍照失败private final int HANDLER_TAKE_PHOTO_SUCCESS = 5;// 拍照成功private List<Integer> enableCameraList;//可用摄像头列表private boolean mFlashSupported = false;//是否支持闪光灯private SizeSelector sizeSelector;private Size mPreviewOutputSize;// 预览尺寸private Size mMediaOutputSize;// 录制/拍照尺寸private final int PREVIEW_TYPE_NORMAL = 0;// 默认预览private final int PREVIEW_TYPE_RECORD = 1;// 录屏预览private final int PREVIEW_TYPE_TAKE_PHOTO = 2;// 拍照预览private int previewType = 0;//默认预览/*** 处理静态图片的输出*/private ImageReader imageReader;private Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case HANDLER_ERR:cameraCallBack.onErr("" + msg.obj);isRecording = false;break;case HANDLER_RECORD_COMPLETE:isRecording = false;cameraCallBack.onRecordComplete(savePath);break;case HANDLER_TAKE_PHOTO_SUCCESS:cameraCallBack.onTakePhotoOk(savePath);break;}}};private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {L.d("record onSurfaceTextureAvailable");openCamera(surfaceTexture);}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {L.d("record onSurfaceTextureDestroyed");mSurfaceTexture = null;stopRecord(false);return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}};/*** 当相机设备的状态发生改变的时候,将会回调。*/protected final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {/*** 当相机打开的时候,调用* @param cameraDevice*/@Overridepublic void onOpened(@NonNull CameraDevice cameraDevice) {L.d("onOpened");mCameraDevice = cameraDevice;startPreview();}@Overridepublic void onDisconnected(@NonNull CameraDevice cameraDevice) {L.d("onDisconnected");cameraDevice.close();mCameraDevice = null;}/*** 发生异常的时候调用** 这里释放资源,然后关闭界面* @param cameraDevice* @param error*/@Overridepublic void onError(@NonNull CameraDevice cameraDevice, int error) {L.d("onError");cameraDevice.close();mCameraDevice = null;Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "相机设备异常,请重启!";handler.sendMessage(messagef);}/***当相机被关闭的时候*/@Overridepublic void onClosed(@NonNull CameraDevice camera) {super.onClosed(camera);L.d("onClosed");mCameraDevice = null;}};/*** 相机状态回调*/private CameraManager.AvailabilityCallback callback = new CameraManager.AvailabilityCallback() {@Overridepublic void onCameraAvailable(@NonNull String cameraId) {// 相机可用super.onCameraAvailable(cameraId);L.d("相机可用");}@Overridepublic void onCameraUnavailable(@NonNull String cameraId) {// 相机不可用super.onCameraUnavailable(cameraId);L.d("相机不可用");}};/*** 初始化** @param activity* @param cameraCallBack 回调*/public Camera2Util(Context activity, @NonNull CameraCallBack cameraCallBack) {this.context = activity;this.cameraCallBack = cameraCallBack;cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);// 对于静态图片,使用可用的最大值来拍摄。if (cameraManager != null) {isInitOk = true;cameraManager.registerAvailabilityCallback(callback, null);getCameras();setupImageReader();service = Executors.newSingleThreadExecutor();}}private void setupImageReader() {//前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据,本例的3代表ImageReader中最多可以获取2帧图像流imageReader = ImageReader.newInstance(640, 480, ImageFormat.JPEG, 1);//监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {Image image = reader.acquireLatestImage();//我们可以将这帧数据转成字节数组,类似于Camera1的PreviewCallback回调的预览帧数据ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] data = new byte[buffer.remaining()];buffer.get(data);image.close();saveFile(data, savePath);}}, null);}/*** 设置预览视图去预览** @param frameLayout 预览视图的父布局*/public void startPreview(final FrameLayout frameLayout) {L.d("startPreview:savePath = " + savePath);this.frameLayout = frameLayout;if (!isInitOk) {return;}frameLayout.removeAllViews();TextureView surfaceView = new TextureView(context);int screenWidth = ScreenUtil.getScreenWidth(frameLayout.getContext());if (screenWidth < 1600) {surfaceView.setLayoutParams(new FrameLayout.LayoutParams(screenWidth, ViewGroup.LayoutParams.MATCH_PARENT));} else {surfaceView.setLayoutParams(new FrameLayout.LayoutParams(1600, 900));}frameLayout.addView(surfaceView);this.textureView = surfaceView;
//        this.textureView.setRotation(270);// todo 设置预览方向this.textureView.setSurfaceTextureListener(textureListener);// 准备好后直接打开camera,开始预览}/*** 打开相机** @param surfaceTexture*/private void openCamera(SurfaceTexture surfaceTexture) {this.mSurfaceTexture = surfaceTexture;// 设置TextureView的缓冲区大小mSurfaceTexture.setDefaultBufferSize(1080, 768);// 获取Surface显示预览数据mSurface = new Surface(mSurfaceTexture);if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED|| ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {Message message = new Message();message.what = HANDLER_ERR;message.obj = "权限不足";handler.sendMessage(message);return;}try {cameraManager.openCamera(Integer.toString(cameraId), stateCallback, null);L.d("打开相机成功!");} catch (Exception e) {L.e("打开相机失败-Exception:" + e.getMessage());e.printStackTrace();Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "打开相机失败";handler.sendMessage(messagef);}}/*** 开始视频录制预览*/private void startPreview() {L.d("开始预览");// CaptureRequest添加imageReaderSurface,不加的话就会导致ImageReader的onImageAvailable()方法不会回调// 创建CaptureSession时加上imageReaderSurface,如下,这样预览数据就会同时输出到previewSurface和imageReaderSurface了try {// 创建CaptureRequestBuilder,TEMPLATE_PREVIEW比表示预览请求mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);mPreviewRequestBuilder.addTarget(mSurface);// 设置Surface作为预览数据的显示界面if (previewType == PREVIEW_TYPE_RECORD) {setUpMediaRecorder();mPreviewRequestBuilder.addTarget(recordSurface);// 将视频通过recordSurface记录到MediaRecorder中// 创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行mCameraDevice.createCaptureSession(Arrays.asList(mSurface, recordSurface), captureSessionStateCallBack, null);} else {// 创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行mCameraDevice.createCaptureSession(Arrays.asList(mSurface, imageReader.getSurface()), captureSessionStateCallBack, null);}} catch (CameraAccessException e) {e.printStackTrace();Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "捕获帧失败";handler.sendMessage(messagef);L.e("Camera获取成功,创建录制请求或捕获Session失败" + e.getMessage(), e);} catch (IOException ioe) {L.e("setUpMediaRecorder-IOException:" + ioe.getMessage(), ioe);Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "设置媒体录制失败";handler.sendMessage(messagef);}}/*** 捕获图片数据*/private CameraCaptureSession.StateCallback captureSessionStateCallBack = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {try {mCameraCaptureSession = session;isSessionClosed = false;request = mPreviewRequestBuilder.build();// 设置反复捕获数据的请求,这样预览界面就会一直有数据显示mCameraCaptureSession.setRepeatingRequest(request, null, null);isRecordPrepareOk = true;if (previewType == PREVIEW_TYPE_RECORD) {isRecording = true;mRecorder.start();cameraCallBack.onStartRecord();}} catch (Exception e) {e.printStackTrace();Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "开启图像预览失败";handler.sendMessage(messagef);}}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {L.d("onConfigureFailed");}};/*** 拍照*/public void takePicture(String savePath) {L.d("拍照:" + savePath);this.savePath = savePath;try {if (mCameraDevice == null || mPreviewRequestBuilder == null) return;mPreviewRequestBuilder.addTarget(imageReader.getSurface());//设置拍照方向mPreviewRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, this.needSetOrientation);//这个回调接口用于拍照结束时重启预览,因为拍照会导致预览停止CameraCaptureSession.CaptureCallback mImageSavedCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {L.d("拍照完成:reset");reset();}};//开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片mCameraCaptureSession.capture(mPreviewRequestBuilder.build(), mImageSavedCallback, null);} catch (CameraAccessException e) {L.d("takePhoto CameraAccessException:" + e.getMessage());e.printStackTrace();Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "拍照失败";handler.sendMessage(messagef);}}/*** 开始录制** @param savePath*/public void startRecord(@NonNull String savePath) {L.d("开始录制:" + savePath);this.savePath = savePath;previewType = PREVIEW_TYPE_RECORD;if (mCameraDevice == null && mSurfaceTexture != null) {openCamera(mSurfaceTexture);} else if (mCameraDevice == null && this.frameLayout != null) {startPreview(this.frameLayout);} else if (mCameraDevice != null) {startPreview();} else {L.e("startRecord-unKnownException");Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "视频录制失败,请稍后重试!";handler.sendMessage(messagef);}}/*** 停止预览,释放资源** @param isFinishRecord 是否是正常停止录制*/public void stopRecord(boolean isFinishRecord) {L.d("停止预览,释放资源");try {if (mCameraCaptureSession != null && !isSessionClosed) {mCameraCaptureSession.stopRepeating();mCameraCaptureSession.abortCaptures();mCameraCaptureSession.close();isSessionClosed = true;}// 停止录制if (mRecorder != null) {if (isRecording) {mRecorder.stop();}mRecorder.release();mRecorder = null;}if (mCameraDevice != null)mCameraDevice.close();if (this.textureView != null) {this.textureView.setSurfaceTextureListener(null);}isRecordFinish = true;if (isFinishRecord) {handler.sendEmptyMessageDelayed(HANDLER_RECORD_COMPLETE, RECORD_SAVE_DELAY_TIME_LONG);}} catch (Exception e) {e.printStackTrace();if (isFinishRecord) {handler.sendEmptyMessage(HANDLER_ERR);L.e("stopRecord-Exception:" + e.getMessage(), e);}}}/*** 设置媒体录制器的配置参数* <p>* 音频,视频格式,文件路径,频率,编码格式等等** @throws IOException*/private void setUpMediaRecorder() throws IOException {mRecorder = new MediaRecorder();mRecorder.reset();mRecorder.setOnErrorListener((mediaRecorder, i, i1) -> {this.savePath = null;L.d("MediaRecorder.onError");});mRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); // 设置从摄像头采集图像
//        mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); // 设置从麦克风采集声音mRecorder.setOrientationHint(this.needSetOrientation);
//        CamcorderProfile mProfile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 设置视频的输出格式 为MP4
//        mRecorder.setAudioEncoder(mProfile.audioCodec);// 设置音频的编码格式mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);// 设置视频的编码格式mRecorder.setOutputFile(savePath);mRecorder.setVideoSize(640, 480);// 设置录制的视频帧率,必须放在设置编码和格式的后面mRecorder.setVideoFrameRate(30);mRecorder.setVideoEncodingBitRate(2000 * 1000);//视频码率
//        mRecorder.setAudioEncodingBitRate(mProfile.audioBitRate);
//        mRecorder.setAudioChannels(1);//设置录制的音频通道数
//        mRecorder.setAudioSamplingRate(44100);mRecorder.setPreviewDisplay(new Surface(textureView.getSurfaceTexture()));mRecorder.prepare();recordSurface = mRecorder.getSurface();}/*** 切换摄像头*/public void toggleCamera() {if (isRecording || this.frameLayout == null || enableCameraList.size() <= 1) return;if (cameraId == CameraCharacteristics.LENS_FACING_FRONT) {cameraId = CameraCharacteristics.LENS_FACING_BACK;} else {cameraId = CameraCharacteristics.LENS_FACING_FRONT;}stopRecord(false);startPreview(this.frameLayout);}/*** 使用前置摄像头*/public void useFrontCamera() {if (cameraId != CameraCharacteristics.LENS_FACING_FRONT) {toggleCamera();}}/*** 使用后置摄像头*/public void useBackCamera() {if (cameraId != CameraCharacteristics.LENS_FACING_BACK) {toggleCamera();}}/*** 重置后,开始预览*/public void reset() {previewType = PREVIEW_TYPE_NORMAL;stopRecord(false);if (this.mSurfaceTexture != null) {openCamera(this.mSurfaceTexture);} elsestartPreview(this.frameLayout);}/*** 在 activity,fragment的onResume中调用*/public void onResume() {if (this.frameLayout != null)startPreview(this.frameLayout);}/*** 在 activity,fragment的onStop中调用*/public void onStop() {stopRecord(false);}/*** 注销 回调*/public void onDestroy() {this.cameraCallBack = null;if (cameraManager != null)cameraManager.unregisterAvailabilityCallback(callback);}/*** 获得可用的摄像头** @return SparseArray of available cameras ids。key为摄像头方位,见CameraCharacteristics#LENS_FACING,value为对应的摄像头id*/public void getCameras() {if (cameraManager == null) return;enableCameraList = new ArrayList<>();try {String[] camerasAvailable = cameraManager.getCameraIdList();CameraCharacteristics cam;Integer characteristic;L.d("-------------------------------------");for (String id : camerasAvailable) {L.d("getCameras:" + id);try {enableCameraList.add(Integer.parseInt(id));} catch (NumberFormatException e) {e.printStackTrace();}// 原生摄像头获取如下
//                cam = cameraManager.getCameraCharacteristics(id);//获得摄像头的特征值
//                characteristic = cam.get(CameraCharacteristics.LENS_FACING);//摄像头方位
//                if (characteristic != null) {
//                    switch (characteristic) {
//                        case CameraCharacteristics.LENS_FACING_FRONT://前置摄像头
//                            break;
//                        case CameraCharacteristics.LENS_FACING_BACK://后置摄像头
//                            break;
//                        case CameraCharacteristics.LENS_FACING_EXTERNAL://其他(USB摄像头)
//                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { }
//                            break;
//                    }
//                }}L.d("-------------------------------------");} catch (CameraAccessException e) {L.e("getCameras CameraAccessException:" + e.getMessage(), e);}}/*** 设置输出数据尺寸选择器,在selectCamera之前设置有效* (一般手机支持多种输出尺寸,请用户根据自身需要选择最合适的一种)* 举例:* SizeSelector maxPreview = SizeSelectors.and(SizeSelectors.maxWidth(720), SizeSelectors.maxHeight(480));* SizeSelector minPreview = SizeSelectors.and(SizeSelectors.minWidth(320), SizeSelectors.minHeight(240));* camera.setmOutputSizeSelector(SizeSelectors.or(* SizeSelectors.and(maxPreview, minPreview)//先在最大和最小中寻找* , SizeSelectors.and(maxPreview, SizeSelectors.biggest())//找不到则按不超过最大尺寸的那个选择* ));** @param selector*/public void setOutputSizeSelector(SizeSelector selector) {this.sizeSelector = selector;}/*** 选择摄像头,在打开摄像头之前调用** @param id Id of the camera which can be retrieved with getCameras().get(CameraCharacteristics.LENS_FACING_BACK)*/public void selectCamera(String id) {if (enableCameraList == null) {getCameras();}cameraId = enableCameraList.contains(Integer.parseInt(id)) ? -1 : Integer.parseInt(id);if (cameraId == -1) {L.e("未发现可用摄像头");return;}try {CameraCharacteristics mCameraCharacteristics = cameraManager.getCameraCharacteristics(String.valueOf(cameraId));// 设置是否支持闪光灯Boolean available = mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);mFlashSupported = available == null ? false : available;StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {L.e("Could not get configuration map.");return;}int mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);//这个方法来获取CameraSensor的方向。L.d("camera sensor orientation: " + mSensorOrientation);int displayRotation = this.frameLayout.getDisplay().getRotation();L.d("camera sensor orientation:" + mSensorOrientation + ",display rotation=" + displayRotation);// 获取手机目前的旋转方向(横屏还是竖屏, 对于"自然"状态下高度大于宽度的设备来说横屏是ROTATION_90// 或者ROTATION_270,竖屏是ROTATION_0或者ROTATION_180)// 获取相机传感器的方向("自然"状态下垂直放置为0, 顺时针算起, 每次加90读)// 注意, 这个参数, 是由设备的生产商来决定的, 大多数情况下, 该值为90, 以下的switch这么写// 是为了配适某些特殊的手机switch (displayRotation) {// ROTATION_0和ROTATION_180都是竖屏只需做同样的处理操作// 显示为竖屏时, 若传感器方向为90或者270, 则需要进行转换(标志位置true)case Surface.ROTATION_0:case Surface.ROTATION_180:if (mSensorOrientation == 90 || mSensorOrientation == 270) {}break;// ROTATION_90和ROTATION_270都是横屏只需做同样的处理操作// 显示为横屏时, 若传感器方向为0或者180, 则需要进行转换(标志位置true)case Surface.ROTATION_90:case Surface.ROTATION_270:if (mSensorOrientation == 0 || mSensorOrientation == 180) {}break;default:L.e("Display rotation is invalid: " + displayRotation);}int[] formats = map.getOutputFormats();//获得手机支持的输出格式,其中jpeg是一定会支持的,yuv_420_888是api21才开始支持的for (int format : formats) {L.d("手机格式支持: " + format);}Size[] yuvOutputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);Size[] mediaOutputSizes = map.getOutputSizes(MediaRecorder.class);Size[] previewOutputSizes = map.getOutputSizes(SurfaceTexture.class);Size[] jpegOutputSizes = map.getOutputSizes(ImageFormat.JPEG);List<Size> previewSizes = new ArrayList<>();List<Size> mediaSizes = new ArrayList<>();for (Size size : mediaOutputSizes) {mediaSizes.add(new Size(size.getWidth(), size.getHeight()));}for (Size size : previewOutputSizes) {previewSizes.add(new Size(size.getWidth(), size.getHeight()));}for (Size size : yuvOutputSizes) {L.d("yuvOutputSizes: " + size.toString());}for (Size size : jpegOutputSizes) {L.d("jpegOutputSizes: " + size.toString());}//test endif (sizeSelector != null) {Size previewSize = sizeSelector.select(previewSizes).get(0);Size mediaSize = sizeSelector.select(mediaSizes).get(0);mPreviewOutputSize = new Size(previewSize.getWidth(), previewSize.getHeight());mMediaOutputSize = new Size(mediaSize.getWidth(), mediaSize.getHeight());} else {
//                mPreviewOutputSize = Collections.min(Arrays.asList(yuvOutputSizes), new CompareSizesByArea());
//                mMediaOutputSize = Collections.min(Arrays.asList(mediaOutputSizes), new CompareSizesByArea());}L.d("selectCamera: previewsize=" + mPreviewOutputSize.toString() + "mediasize=" + mMediaOutputSize.toString());} catch (Exception e) {L.e("selectCamera Exception:" + e.getMessage(), e);}}/*** @param textureView* @param surfaceWidth* @param surfaceHeight*/private void setAspectRatioTextureView(TextureView textureView, int surfaceWidth, int surfaceHeight) {int rotation = this.frameLayout.getDisplay().getRotation();int newWidth = surfaceWidth, newHeight = surfaceHeight;switch (rotation) {case Surface.ROTATION_0:case Surface.ROTATION_180:newWidth = surfaceWidth;newHeight = (surfaceWidth * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());break;case Surface.ROTATION_90:case Surface.ROTATION_270:newWidth = surfaceHeight;newHeight = (surfaceHeight * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());break;}textureView.setLayoutParams(new FrameLayout.LayoutParams(newWidth, newHeight, Gravity.CENTER));rotatePreview(textureView, rotation, newWidth, newHeight);}/*** Configures the necessary {@link Matrix} transformation to `mTextureView`.* This method should not to be called until the camera preview size is determined in* openCamera, or until the size of `mTextureView` is fixed.** @param viewWidth  The width of `mTextureView`* @param viewHeight The height of `mTextureView`*/private void configureTransform(TextureView textureView, int viewWidth, int viewHeight) {if (null == textureView || null == mPreviewOutputSize || null == context) {return;}int rotation = frameLayout.getDisplay().getRotation();Matrix matrix = new Matrix();RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);RectF bufferRect = new RectF(0, 0, mPreviewOutputSize.getHeight(), mPreviewOutputSize.getWidth());float centerX = viewRect.centerX();float centerY = viewRect.centerY();if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);float scale = Math.max((float) viewHeight / mPreviewOutputSize.getHeight(),(float) viewWidth / mPreviewOutputSize.getWidth());matrix.postScale(scale, scale, centerX, centerY);matrix.postRotate(90 * (rotation - 2), centerX, centerY);}textureView.setTransform(matrix);}private void rotatePreview(TextureView mTextureView, int rotation, int viewWidth, int viewHeight) {Matrix matrix = new Matrix();RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);float centerX = viewRect.centerX();float centerY = viewRect.centerY();if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {matrix.postRotate(90 * (rotation - 2), centerX, centerY);} else if (Surface.ROTATION_180 == rotation) {matrix.postRotate(180, centerX, centerY);}mTextureView.setTransform(matrix);}//覆盖性保存private void saveFile(final byte[] data, final String savePath) {if (data == null || data.length == 0) return;service.execute(() -> {File file = new File(savePath);File parent = file.getParentFile();if (parent != null && !parent.exists()) {parent.mkdirs();}if (file.exists()) {file.delete();}try {file.createNewFile();FileOutputStream fos = new FileOutputStream(file);fos.write(data);fos.flush();fos.close();Message message = new Message();message.what = HANDLER_TAKE_PHOTO_SUCCESS;message.obj = savePath;handler.sendMessage(message);} catch (IOException e) {e.printStackTrace();L.e("图片保存-IOException:" + e.getMessage(), e);Message messagef = new Message();messagef.what = HANDLER_ERR;messagef.obj = "图片保存失败";handler.sendMessage(messagef);}});}
}

CameraCallBack

public interface CameraCallBack {void onCameraInited();void onErr(String message);void onTakePhotoOk(String path);void onStartRecord();void onRecordComplete(String path);
}

调用方式

....private void initData() {recordUtil = new Camera2Util(this, cameraCallBack);checkPermissionToPreview();}
..../*** 开始预览*/private void startPreview() {L.d("camera.startPreview");recordUtil.startPreview(surfaceBoX);isStartedPreview = true;}
....
String videoPath = PathUtil.getVideoRecordPath().concat("/").concat(new SimpleDateFormat(Constants.DATE_FORMAT_FULL).format(new Date())).concat(".mp4");recordUtil.startRecord(videoPath);

Android Camera2拍照录制工具相关推荐

  1. Android Camera2 拍照(四)——对焦模式

    原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...

  2. Android Camera2拍照时照片拍出来特别暗

    最近在开发相机的过程中遇到这样一个问题,相机打开闪光灯后拍照后照片特别的暗,刚开始打算修改一些拍照时的参数,提高曝光率啊,延长曝光时长来改善照片问题 如一下思路 1.通过设置曝光时间范围(Captur ...

  3. Android camera2拍照旋转角度,以及镜像

    一.需要系统自带相机,拍照旋转90度. 路径:packages\apps\Camera2 找到三个CaptureRequest.JPEG_ORIENTATION ,修改为解决: rootBuilder ...

  4. Camera2视频录制工具类

    Android通过Camera2录制视频,写了个工具类,需要时直接拿来用 import android.Manifest; import android.annotation.SuppressLint ...

  5. android camera2拍照图像输出过慢,华为手机比较明显

    最近在camera2自定义相机拍照,在点击拍照按钮回调,在处理图像流的时候总是卡主 尤其是华为手机,几乎所有手机都会拍照后拿不到imageReader读取的image 然后我加了个300ms的延迟后, ...

  6. Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式

    一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新打开preview. public void switchCamera() {if ...

  7. 十分钟实现 Android Camera2 相机拍照

    1. 前言 因为工作中要使用Android Camera2 API,但因为Camera2比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在CSDN上记录了下,希望能帮助到更 ...

  8. android camera(6)---camera2 拍照流程

    android camera2 拍照流程 正文 camera2 API 的加入是从AndroidV5.0(21)开始的,因此我们使用Camera2应该是在Android 5.0(含5.0)之后.同时, ...

  9. 第3讲 Android Camera2 API 概述

    本讲是Android Camera专题系列的第3讲,我们介绍Android Camera2 API概述,了解Camera2 API的核心组件,以及他们如何交互来完成Camera各种流程. 视频在线观看 ...

最新文章

  1. 你需要知道的requestAnimationFrame
  2. SAP ABAP程序效率优化 Perfomance Tune
  3. 电商大战折射商业伦理缺失形势探讨
  4. 2020诺奖预测出炉!“引文桂冠”奖今日公布,华人学者戴宏杰入选
  5. Ranger-AdminServer安装
  6. SAP BPC最佳实践-BPC系统备份及恢复
  7. 网络工程师考试2005年上半年下午试题解析(一)
  8. spring事务传播机制源码学习笔记
  9. OpenCV 人脸识别、图片相似度检测
  10. Docker 视频教程 ( 猿课 )
  11. 综述文章笔记——《A Survey on Traffic Signal Control Methods》等
  12. wifi分析仪android 9,Wifi分析仪(无线信号检测)
  13. 从“杀猪盘”到杀洋盘,短信里藏了多少套路?
  14. 扬声器有小红叉,前置耳机孔没有声音,找不到realtek高清晰音频管理器
  15. 某摄像头的游戏的总结
  16. 立创EDA学习3-绘制pcb(初级)
  17. 深度揭秘阿里云函数计算异步任务能力
  18. 战双帕弥什qq登录服务器未响应是什么意思,战双帕弥什qq登录
  19. Java设计模式(疯狂Java联盟版)
  20. StyleGAN3重磅发布!皮肤、毛发不再粘屏幕,还能360度旋转!英伟达最新开源

热门文章

  1. 高级技术之 JUC 高并发编程
  2. golang语言的包依赖管理方式 综述
  3. 在电脑上怎样将图片转成Word?
  4. 【HDOJ】2851 Lode Runner
  5. win8设置计算机休眠,Win8.1关闭休眠设置图文教程
  6. 大佬快来,救命!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  7. php 取某一日期的前一天
  8. 深入浅出Object.defineProperty()
  9. 一个阿里P8的工程师,一年能赚多少钱?
  10. 股市盛宴,看这个Excel插件如何玩转行情数据分析