package com.cjt2325.cameralibrary; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.YuvImage; import android.hardware.Camera; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.MediaRecorder; import android.os.Build; import android.os.Environment; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.widget.ImageView; import com.cjt2325.cameralibrary.listener.ErrorListener; import com.cjt2325.cameralibrary.util.AngleUtil; import com.cjt2325.cameralibrary.util.CameraParamUtil; import com.cjt2325.cameralibrary.util.CheckPermission; import com.cjt2325.cameralibrary.util.DeviceUtil; import com.cjt2325.cameralibrary.util.FileUtil; import com.cjt2325.cameralibrary.util.LogUtil; import com.cjt2325.cameralibrary.util.ScreenUtils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static android.graphics.Bitmap.createBitmap; /** * ===================================== * 作 者: 陈嘉桐 * 版 本:1.1.4 * 创建日期:2017/4/25 * 描 述:camera操作单例 * ===================================== */ @SuppressWarnings("deprecation") public class CameraInterface implements Camera.PreviewCallback { private static final String TAG = "CJT"; private volatile static CameraInterface mCameraInterface; public static void destroyCameraInterface() { if (mCameraInterface != null) { mCameraInterface = null; } } private Camera mCamera; private Camera.Parameters mParams; private boolean isPreviewing = false; private int SELECTED_CAMERA = -1; private int CAMERA_POST_POSITION = -1; private int CAMERA_FRONT_POSITION = -1; private SurfaceHolder mHolder = null; private float screenProp = -1.0f; private boolean isRecorder = false; private MediaRecorder mediaRecorder; private String videoFileName; private String saveVideoPath; private String videoFileAbsPath; private Bitmap videoFirstFrame = null; private ErrorListener errorLisenter; private ImageView mSwitchView; private ImageView mFlashLamp; private int preview_width; private int preview_height; private int angle = 0; private int cameraAngle = 90;//摄像头角度 默认为90度 private int rotation = 0; private byte[] firstFrame_data; public static final int TYPE_RECORDER = 0x090; public static final int TYPE_CAPTURE = 0x091; private int nowScaleRate = 0; private int recordScleRate = 0; //视频质量 private int mediaQuality = JCameraView.MEDIA_QUALITY_MIDDLE; private SensorManager sm = null; //获取CameraInterface单例 public static synchronized CameraInterface getInstance() { if (mCameraInterface == null) synchronized (CameraInterface.class) { if (mCameraInterface == null) mCameraInterface = new CameraInterface(); } return mCameraInterface; } public void setSwitchView(ImageView mSwitchView, ImageView mFlashLamp) { this.mSwitchView = mSwitchView; this.mFlashLamp = mFlashLamp; if (mSwitchView != null) { cameraAngle = CameraParamUtil.getInstance().getCameraDisplayOrientation(mSwitchView.getContext(), SELECTED_CAMERA); } } private SensorEventListener sensorEventListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) { return; } float[] values = event.values; angle = AngleUtil.getSensorAngle(values[0], values[1]); rotationAnimation(); } public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; //切换摄像头icon跟随手机角度进行旋转 private void rotationAnimation() { if (mSwitchView == null) { return; } if (rotation != angle) { int start_rotaion = 0; int end_rotation = 0; switch (rotation) { case 0: start_rotaion = 0; switch (angle) { case 90: end_rotation = -90; break; case 270: end_rotation = 90; break; } break; case 90: start_rotaion = -90; switch (angle) { case 0: end_rotation = 0; break; case 180: end_rotation = -180; break; } break; case 180: start_rotaion = 180; switch (angle) { case 90: end_rotation = 270; break; case 270: end_rotation = 90; break; } break; case 270: start_rotaion = 90; switch (angle) { case 0: end_rotation = 0; break; case 180: end_rotation = 180; break; } break; } ObjectAnimator animC = ObjectAnimator.ofFloat(mSwitchView, "rotation", start_rotaion, end_rotation); ObjectAnimator animF = ObjectAnimator.ofFloat(mFlashLamp, "rotation", start_rotaion, end_rotation); AnimatorSet set = new AnimatorSet(); set.playTogether(animC, animF); set.setDuration(500); set.start(); rotation = angle; } } @SuppressWarnings("ResultOfMethodCallIgnored") void setSaveVideoPath(String saveVideoPath) { this.saveVideoPath = saveVideoPath; File file = new File(saveVideoPath); if (!file.exists()) { file.mkdirs(); } } public void setZoom(float zoom, int type) { if (mCamera == null) { return; } if (mParams == null) { mParams = mCamera.getParameters(); } if (!mParams.isZoomSupported() || !mParams.isSmoothZoomSupported()) { return; } switch (type) { case TYPE_RECORDER: //如果不是录制视频中,上滑不会缩放 if (!isRecorder) { return; } if (zoom >= 0) { //每移动50个像素缩放一个级别 int scaleRate = (int) (zoom / 40); if (scaleRate <= mParams.getMaxZoom() && scaleRate >= nowScaleRate && recordScleRate != scaleRate) { mParams.setZoom(scaleRate); mCamera.setParameters(mParams); recordScleRate = scaleRate; } } break; case TYPE_CAPTURE: if (isRecorder) { return; } //每移动50个像素缩放一个级别 int scaleRate = (int) (zoom / 50); if (scaleRate < mParams.getMaxZoom()) { nowScaleRate += scaleRate; if (nowScaleRate < 0) { nowScaleRate = 0; } else if (nowScaleRate > mParams.getMaxZoom()) { nowScaleRate = mParams.getMaxZoom(); } mParams.setZoom(nowScaleRate); mCamera.setParameters(mParams); } LogUtil.i("setZoom = " + nowScaleRate); break; } } void setMediaQuality(int quality) { this.mediaQuality = quality; } @Override public void onPreviewFrame(byte[] data, Camera camera) { firstFrame_data = data; } public void setFlashMode(String flashMode) { if (mCamera == null) return; Camera.Parameters params = mCamera.getParameters(); params.setFlashMode(flashMode); mCamera.setParameters(params); } public interface CameraOpenOverCallback { void cameraHasOpened(); } private CameraInterface() { findAvailableCameras(); SELECTED_CAMERA = CAMERA_POST_POSITION; saveVideoPath = ""; } /** * open Camera */ void doOpenCamera(CameraOpenOverCallback callback) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (!CheckPermission.isCameraUseable(SELECTED_CAMERA) && this.errorLisenter != null) { this.errorLisenter.onError(); return; } } if (mCamera == null) { openCamera(SELECTED_CAMERA); } callback.cameraHasOpened(); } private void setFlashModel() { mParams = mCamera.getParameters(); mParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); //设置camera参数为Torch模式 mCamera.setParameters(mParams); } private synchronized void openCamera(int id) { try { this.mCamera = Camera.open(id); } catch (Exception var3) { var3.printStackTrace(); if (this.errorLisenter != null) { this.errorLisenter.onError(); } } if (Build.VERSION.SDK_INT > 17 && this.mCamera != null) { try { this.mCamera.enableShutterSound(false); } catch (Exception e) { e.printStackTrace(); Log.e("CJT", "enable shutter sound faild"); } } } public synchronized void switchCamera(SurfaceHolder holder, float screenProp) { if (SELECTED_CAMERA == CAMERA_POST_POSITION) { SELECTED_CAMERA = CAMERA_FRONT_POSITION; } else { SELECTED_CAMERA = CAMERA_POST_POSITION; } doDestroyCamera(); LogUtil.i("open start"); openCamera(SELECTED_CAMERA); // mCamera = Camera.open(); if (Build.VERSION.SDK_INT > 17 && this.mCamera != null) { try { this.mCamera.enableShutterSound(false); } catch (Exception e) { e.printStackTrace(); } } LogUtil.i("open end"); doStartPreview(holder, screenProp); } /** * doStartPreview */ public void doStartPreview(SurfaceHolder holder, float screenProp) { if (isPreviewing) { LogUtil.i("doStartPreview isPreviewing"); } if (this.screenProp < 0) { this.screenProp = screenProp; } if (holder == null) { return; } this.mHolder = holder; if (mCamera != null) { try { mParams = mCamera.getParameters(); Camera.Size previewSize = CameraParamUtil.getInstance().getPreviewSize(mParams .getSupportedPreviewSizes(), 1000, screenProp); Camera.Size pictureSize = CameraParamUtil.getInstance().getPictureSize(mParams .getSupportedPictureSizes(), 1200, screenProp); mParams.setPreviewSize(previewSize.width, previewSize.height); preview_width = previewSize.width; preview_height = previewSize.height; mParams.setPictureSize(pictureSize.width, pictureSize.height); if (CameraParamUtil.getInstance().isSupportedFocusMode( mParams.getSupportedFocusModes(), Camera.Parameters.FOCUS_MODE_AUTO)) { mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } if (CameraParamUtil.getInstance().isSupportedPictureFormats(mParams.getSupportedPictureFormats(), ImageFormat.JPEG)) { mParams.setPictureFormat(ImageFormat.JPEG); mParams.setJpegQuality(100); } mCamera.setParameters(mParams); mParams = mCamera.getParameters(); mCamera.setPreviewDisplay(holder); //SurfaceView mCamera.setDisplayOrientation(cameraAngle);//浏览角度 mCamera.setPreviewCallback(this); //每一帧回调 mCamera.startPreview();//启动浏览 isPreviewing = true; Log.i(TAG, "=== Start Preview ==="); } catch (IOException e) { e.printStackTrace(); } } } /** * 停止预览 */ public void doStopPreview() { if (null != mCamera) { try { mCamera.setPreviewCallback(null); mCamera.stopPreview(); //这句要在stopPreview后执行,不然会卡顿或者花屏 mCamera.setPreviewDisplay(null); isPreviewing = false; Log.i(TAG, "=== Stop Preview ==="); } catch (IOException e) { e.printStackTrace(); } } } /** * 销毁Camera */ void doDestroyCamera() { errorLisenter = null; if (null != mCamera) { try { mCamera.setPreviewCallback(null); mSwitchView = null; mFlashLamp = null; mCamera.stopPreview(); //这句要在stopPreview后执行,不然会卡顿或者花屏 mCamera.setPreviewDisplay(null); mHolder = null; isPreviewing = false; mCamera.release(); mCamera = null; // destroyCameraInterface(); Log.i(TAG, "=== Destroy Camera ==="); } catch (IOException e) { e.printStackTrace(); } } else { Log.i(TAG, "=== Camera Null==="); } } /** * 拍照 */ private int nowAngle; public void takePicture(final TakePictureCallback callback) { if (mCamera == null) { return; } switch (cameraAngle) { case 90: nowAngle = Math.abs(angle + cameraAngle) % 360; break; case 270: nowAngle = Math.abs(cameraAngle - angle); break; } // Log.i("CJT", angle + " = " + cameraAngle + " = " + nowAngle); mCamera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); Matrix matrix = new Matrix(); if (SELECTED_CAMERA == CAMERA_POST_POSITION) { matrix.setRotate(nowAngle); } else if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) { matrix.setRotate(360 - nowAngle); matrix.postScale(-1, 1); } bitmap = createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (callback != null) { if (nowAngle == 90 || nowAngle == 270) { callback.captureResult(bitmap, true); } else { callback.captureResult(bitmap, false); } } } }); } //启动录像 public void startRecord(Surface surface, float screenProp, ErrorCallback callback) { mCamera.setPreviewCallback(null); final int nowAngle = (angle + 90) % 360; //获取第一帧图片 Camera.Parameters parameters = mCamera.getParameters(); int width = parameters.getPreviewSize().width; int height = parameters.getPreviewSize().height; YuvImage yuv = new YuvImage(firstFrame_data, parameters.getPreviewFormat(), width, height, null); ByteArrayOutputStream out = new ByteArrayOutputStream(); yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out); byte[] bytes = out.toByteArray(); videoFirstFrame = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); Matrix matrix = new Matrix(); if (SELECTED_CAMERA == CAMERA_POST_POSITION) { matrix.setRotate(nowAngle); } else if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) { matrix.setRotate(270); } videoFirstFrame = createBitmap(videoFirstFrame, 0, 0, videoFirstFrame.getWidth(), videoFirstFrame .getHeight(), matrix, true); if (isRecorder) { return; } if (mCamera == null) { openCamera(SELECTED_CAMERA); } if (mediaRecorder == null) { mediaRecorder = new MediaRecorder(); } if (mParams == null) { mParams = mCamera.getParameters(); } List<String> focusModes = mParams.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } mCamera.setParameters(mParams); mCamera.unlock(); mediaRecorder.reset(); mediaRecorder.setCamera(mCamera); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); int th = 800; // if (ScreenUtils.screenWidth > 700) // th = ScreenUtils.screenWidth; Camera.Size videoSize; if (mParams.getSupportedVideoSizes() == null) { videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedPreviewSizes(), th, screenProp); } else { videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedVideoSizes(), th, screenProp); } Log.i(TAG, "setVideoSize width = " + videoSize.width + "height = " + videoSize.height); // Log.i(TAG, "setVideoSize2 width = " + ScreenUtils.screenWidth + "height = " + ScreenUtils.screenHeight); // if (ScreenUtils.screenWidth > 700 && ScreenUtils.screenHeight > 1000) { // mediaRecorder.setVideoSize(ScreenUtils.screenWidth, ScreenUtils.screenHeight); // } else { if (videoSize.width == videoSize.height) { mediaRecorder.setVideoSize(preview_width, preview_height); } else { mediaRecorder.setVideoSize(videoSize.width, videoSize.height); } // } // if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) { // mediaRecorder.setOrientationHint(270); // } else { // mediaRecorder.setOrientationHint(nowAngle); //// mediaRecorder.setOrientationHint(90); // } if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) { //手机预览倒立的处理 if (cameraAngle == 270) { //横屏 if (nowAngle == 0) { mediaRecorder.setOrientationHint(180); } else if (nowAngle == 270) { mediaRecorder.setOrientationHint(270); } else { mediaRecorder.setOrientationHint(90); } } else { if (nowAngle == 90) { mediaRecorder.setOrientationHint(270); } else if (nowAngle == 270) { mediaRecorder.setOrientationHint(90); } else { mediaRecorder.setOrientationHint(nowAngle); } } } else { mediaRecorder.setOrientationHint(nowAngle); } // if (DeviceUtil.isHuaWeiRongyao()) { // mediaRecorder.setVideoEncodingBitRate(4 * 100000); // } else { // mediaRecorder.setVideoEncodingBitRate(mediaQuality); // } mediaRecorder.setVideoEncodingBitRate(mediaQuality); mediaRecorder.setPreviewDisplay(surface); videoFileName = "video_" + System.currentTimeMillis() + ".mp4"; if (saveVideoPath.equals("")) { saveVideoPath = Environment.getExternalStorageDirectory().getPath(); } videoFileAbsPath = saveVideoPath + File.separator + videoFileName; mediaRecorder.setOutputFile(videoFileAbsPath); try { mediaRecorder.prepare(); mediaRecorder.start(); isRecorder = true; } catch (IllegalStateException e) { e.printStackTrace(); Log.i("CJT", "startRecord IllegalStateException"); if (this.errorLisenter != null) { this.errorLisenter.onError(); } } catch (IOException e) { e.printStackTrace(); Log.i("CJT", "startRecord IOException"); if (this.errorLisenter != null) { this.errorLisenter.onError(); } } catch (RuntimeException e) { Log.i("CJT", "startRecord RuntimeException"); } } //停止录像 public void stopRecord(boolean isShort, StopRecordCallback callback) { if (!isRecorder) { return; } if (mediaRecorder != null) { mediaRecorder.setOnErrorListener(null); mediaRecorder.setOnInfoListener(null); mediaRecorder.setPreviewDisplay(null); try { mediaRecorder.stop(); } catch (RuntimeException e) { e.printStackTrace(); mediaRecorder = null; mediaRecorder = new MediaRecorder(); } finally { if (mediaRecorder != null) { mediaRecorder.release(); } mediaRecorder = null; isRecorder = false; } if (isShort) { if (FileUtil.deleteFile(videoFileAbsPath)) { callback.recordResult(null, null); } return; } doStopPreview(); String fileName = saveVideoPath + File.separator + videoFileName; callback.recordResult(fileName, videoFirstFrame); } } private void findAvailableCameras() { Camera.CameraInfo info = new Camera.CameraInfo(); int cameraNum = Camera.getNumberOfCameras(); for (int i = 0; i < cameraNum; i++) { Camera.getCameraInfo(i, info); switch (info.facing) { case Camera.CameraInfo.CAMERA_FACING_FRONT: CAMERA_FRONT_POSITION = info.facing; break; case Camera.CameraInfo.CAMERA_FACING_BACK: CAMERA_POST_POSITION = info.facing; break; } } } int handlerTime = 0; public void handleFocus(final Context context, final float x, final float y, final FocusCallback callback) { if (mCamera == null) { return; } final Camera.Parameters params = mCamera.getParameters(); Rect focusRect = calculateTapArea(x, y, 1f, context); mCamera.cancelAutoFocus(); if (params.getMaxNumFocusAreas() > 0) { List<Camera.Area> focusAreas = new ArrayList<>(); focusAreas.add(new Camera.Area(focusRect, 800)); params.setFocusAreas(focusAreas); } else { Log.i(TAG, "focus areas not supported"); callback.focusSuccess(); return; } final String currentFocusMode = params.getFocusMode(); try { params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); mCamera.setParameters(params); mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { if (success || handlerTime > 10) { Camera.Parameters params = camera.getParameters(); params.setFocusMode(currentFocusMode); camera.setParameters(params); handlerTime = 0; callback.focusSuccess(); } else { handlerTime++; handleFocus(context, x, y, callback); } } }); } catch (Exception e) { Log.e(TAG, "autoFocus failer"); } } private static Rect calculateTapArea(float x, float y, float coefficient, Context context) { float focusAreaSize = 300; int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue(); int centerX = (int) (x / ScreenUtils.getScreenWidth(context) * 2000 - 1000); int centerY = (int) (y / ScreenUtils.getScreenHeight(context) * 2000 - 1000); int left = clamp(centerX - areaSize / 2, -1000, 1000); int top = clamp(centerY - areaSize / 2, -1000, 1000); RectF rectF = new RectF(left, top, left + areaSize, top + areaSize); return new Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF .bottom)); } private static int clamp(int x, int min, int max) { if (x > max) { return max; } if (x < min) { return min; } return x; } void setErrorLinsenter(ErrorListener errorLisenter) { this.errorLisenter = errorLisenter; } public interface StopRecordCallback { void recordResult(String url, Bitmap firstFrame); } interface ErrorCallback { void onError(); } public interface TakePictureCallback { void captureResult(Bitmap bitmap, boolean isVertical); } public interface FocusCallback { void focusSuccess(); } void registerSensorManager(Context context) { if (sm == null) { sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); } sm.registerListener(sensorEventListener, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager .SENSOR_DELAY_NORMAL); } void unregisterSensorManager(Context context) { if (sm == null) { sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); } sm.unregisterListener(sensorEventListener); } void isPreview(boolean res) { this.isPreviewing = res; } }