Android Camera2拍照录制工具
工具类
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拍照录制工具相关推荐
- Android Camera2 拍照(四)——对焦模式
原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...
- Android Camera2拍照时照片拍出来特别暗
最近在开发相机的过程中遇到这样一个问题,相机打开闪光灯后拍照后照片特别的暗,刚开始打算修改一些拍照时的参数,提高曝光率啊,延长曝光时长来改善照片问题 如一下思路 1.通过设置曝光时间范围(Captur ...
- Android camera2拍照旋转角度,以及镜像
一.需要系统自带相机,拍照旋转90度. 路径:packages\apps\Camera2 找到三个CaptureRequest.JPEG_ORIENTATION ,修改为解决: rootBuilder ...
- Camera2视频录制工具类
Android通过Camera2录制视频,写了个工具类,需要时直接拿来用 import android.Manifest; import android.annotation.SuppressLint ...
- android camera2拍照图像输出过慢,华为手机比较明显
最近在camera2自定义相机拍照,在点击拍照按钮回调,在处理图像流的时候总是卡主 尤其是华为手机,几乎所有手机都会拍照后拿不到imageReader读取的image 然后我加了个300ms的延迟后, ...
- Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式
一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新打开preview. public void switchCamera() {if ...
- 十分钟实现 Android Camera2 相机拍照
1. 前言 因为工作中要使用Android Camera2 API,但因为Camera2比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在CSDN上记录了下,希望能帮助到更 ...
- android camera(6)---camera2 拍照流程
android camera2 拍照流程 正文 camera2 API 的加入是从AndroidV5.0(21)开始的,因此我们使用Camera2应该是在Android 5.0(含5.0)之后.同时, ...
- 第3讲 Android Camera2 API 概述
本讲是Android Camera专题系列的第3讲,我们介绍Android Camera2 API概述,了解Camera2 API的核心组件,以及他们如何交互来完成Camera各种流程. 视频在线观看 ...
最新文章
- 你需要知道的requestAnimationFrame
- SAP ABAP程序效率优化 Perfomance Tune
- 电商大战折射商业伦理缺失形势探讨
- 2020诺奖预测出炉!“引文桂冠”奖今日公布,华人学者戴宏杰入选
- Ranger-AdminServer安装
- SAP BPC最佳实践-BPC系统备份及恢复
- 网络工程师考试2005年上半年下午试题解析(一)
- spring事务传播机制源码学习笔记
- OpenCV 人脸识别、图片相似度检测
- Docker 视频教程 ( 猿课 )
- 综述文章笔记——《A Survey on Traffic Signal Control Methods》等
- wifi分析仪android 9,Wifi分析仪(无线信号检测)
- 从“杀猪盘”到杀洋盘,短信里藏了多少套路?
- 扬声器有小红叉,前置耳机孔没有声音,找不到realtek高清晰音频管理器
- 某摄像头的游戏的总结
- 立创EDA学习3-绘制pcb(初级)
- 深度揭秘阿里云函数计算异步任务能力
- 战双帕弥什qq登录服务器未响应是什么意思,战双帕弥什qq登录
- Java设计模式(疯狂Java联盟版)
- StyleGAN3重磅发布!皮肤、毛发不再粘屏幕,还能360度旋转!英伟达最新开源