capacitor-plugin-camera-preview
Advanced tools
| /* | ||
| * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. | ||
| */ | ||
| package com.yufu.camera.preview; | ||
| import android.Manifest; | ||
| import android.content.Context; | ||
| import android.content.pm.PackageManager; | ||
| import android.graphics.ImageFormat; | ||
| import android.graphics.Rect; | ||
| import android.graphics.SurfaceTexture; | ||
| import android.graphics.YuvImage; | ||
| import android.hardware.Camera; | ||
| import android.view.SurfaceHolder; | ||
| import android.view.SurfaceView; | ||
| import android.view.TextureView; | ||
| import android.view.View; | ||
| import android.widget.FrameLayout; | ||
| import android.widget.RelativeLayout; | ||
| import androidx.annotation.NonNull; | ||
| import androidx.core.app.ActivityCompat; | ||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.Comparator; | ||
| import java.util.List; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| /** | ||
| * 5.0以下相机API的封装。 | ||
| */ | ||
| @SuppressWarnings("deprecation") | ||
| public class Camera1Control implements ICameraControl { | ||
| private int displayOrientation = 0; | ||
| private int cameraId = 0; | ||
| private int flashMode; | ||
| private AtomicBoolean takingPicture = new AtomicBoolean(false); | ||
| private AtomicBoolean abortingScan = new AtomicBoolean(false); | ||
| private Context context; | ||
| private Camera camera; | ||
| private Camera.Parameters parameters; | ||
| private PermissionCallback permissionCallback; | ||
| private Rect previewFrame = new Rect(); | ||
| private PreviewView previewView; | ||
| private View displayView; | ||
| private int rotation = 0; | ||
| private OnDetectPictureCallback detectCallback; | ||
| private int previewFrameCount = 0; | ||
| private Camera.Size optSize; | ||
| /* | ||
| * 非扫描模式 | ||
| */ | ||
| private final int MODEL_NOSCAN = 0; | ||
| /* | ||
| * 本地质量控制扫描模式 | ||
| */ | ||
| private final int MODEL_SCAN = 1; | ||
| private int detectType = MODEL_NOSCAN; | ||
| public int getCameraRotation() { | ||
| return rotation; | ||
| } | ||
| public AtomicBoolean getAbortingScan() { | ||
| return abortingScan; | ||
| } | ||
| @Override | ||
| public void setDetectCallback(OnDetectPictureCallback callback) { | ||
| detectType = MODEL_SCAN; | ||
| detectCallback = callback; | ||
| } | ||
| private void onRequestDetect(byte[] data) { | ||
| // 相机已经关闭 | ||
| if (camera == null || data == null || optSize == null) { | ||
| return; | ||
| } | ||
| YuvImage img = new YuvImage(data, ImageFormat.NV21, optSize.width, optSize.height, null); | ||
| ByteArrayOutputStream os = null; | ||
| try { | ||
| os = new ByteArrayOutputStream(data.length); | ||
| img.compressToJpeg(new Rect(0, 0, optSize.width, optSize.height), 80, os); | ||
| byte[] jpeg = os.toByteArray(); | ||
| int status = detectCallback.onDetect(jpeg, getCameraRotation()); | ||
| if (status == 0) { | ||
| clearPreviewCallback(); | ||
| } | ||
| } catch (OutOfMemoryError e) { | ||
| // 内存溢出则取消当次操作 | ||
| } finally { | ||
| try { | ||
| os.close(); | ||
| } catch (Throwable e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| } | ||
| @Override | ||
| public void setDisplayOrientation(@CameraPreview.Orientation int displayOrientation) { | ||
| this.displayOrientation = displayOrientation; | ||
| switch (displayOrientation) { | ||
| case CameraPreview.ORIENTATION_PORTRAIT: | ||
| rotation = 90; | ||
| break; | ||
| case CameraPreview.ORIENTATION_HORIZONTAL: | ||
| rotation = 0; | ||
| break; | ||
| case CameraPreview.ORIENTATION_INVERT: | ||
| rotation = 180; | ||
| break; | ||
| default: | ||
| rotation = 0; | ||
| } | ||
| previewView.requestLayout(); | ||
| } | ||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| @Override | ||
| public void refreshPermission() { | ||
| startPreview(true); | ||
| } | ||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| @Override | ||
| public void setFlashMode(@FlashMode int flashMode) { | ||
| if (this.flashMode == flashMode) { | ||
| return; | ||
| } | ||
| this.flashMode = flashMode; | ||
| updateFlashMode(flashMode); | ||
| } | ||
| @Override | ||
| public int getFlashMode() { | ||
| return flashMode; | ||
| } | ||
| @Override | ||
| public void start() { | ||
| startPreview(false); | ||
| } | ||
| @Override | ||
| public void stop() { | ||
| if (camera != null) { | ||
| camera.setPreviewCallback(null); | ||
| stopPreview(); | ||
| // 避免同步代码,为了先设置null后release | ||
| Camera tempC = camera; | ||
| camera = null; | ||
| tempC.release(); | ||
| camera = null; | ||
| buffer = null; | ||
| } | ||
| } | ||
| private void stopPreview() { | ||
| if (camera != null) { | ||
| camera.stopPreview(); | ||
| } | ||
| } | ||
| @Override | ||
| public void pause() { | ||
| if (camera != null) { | ||
| stopPreview(); | ||
| } | ||
| setFlashMode(FLASH_MODE_OFF); | ||
| } | ||
| @Override | ||
| public void resume() { | ||
| takingPicture.set(false); | ||
| if (camera == null) { | ||
| openCamera(); | ||
| } else { | ||
| System.out.println("-------------------------=="); | ||
| System.out.println(previewView.textureView == null); | ||
| System.out.println(previewView.surfaceView == null); | ||
| previewView.textureView.setSurfaceTextureListener(surfaceTextureListener); | ||
| if (previewView.textureView.isAvailable()) { | ||
| startPreview(false); | ||
| } | ||
| } | ||
| } | ||
| @Override | ||
| public View getDisplayView() { | ||
| return displayView; | ||
| } | ||
| @Override | ||
| public void takePicture(final OnTakePictureCallback onTakePictureCallback) { | ||
| if (takingPicture.get()) { | ||
| return; | ||
| } | ||
| switch (displayOrientation) { | ||
| case CameraPreview.ORIENTATION_PORTRAIT: | ||
| parameters.setRotation(90); | ||
| break; | ||
| case CameraPreview.ORIENTATION_HORIZONTAL: | ||
| parameters.setRotation(0); | ||
| break; | ||
| case CameraPreview.ORIENTATION_INVERT: | ||
| parameters.setRotation(180); | ||
| break; | ||
| } | ||
| try { | ||
| Camera.Size picSize = getOptimalSize(camera.getParameters().getSupportedPictureSizes()); | ||
| parameters.setPictureSize(picSize.width, picSize.height); | ||
| camera.setParameters(parameters); | ||
| takingPicture.set(true); | ||
| cancelAutoFocus(); | ||
| CameraThreadPool.execute(new Runnable() { | ||
| @Override | ||
| public void run() { | ||
| camera.takePicture(null, null, new Camera.PictureCallback() { | ||
| @Override | ||
| public void onPictureTaken(byte[] data, Camera camera) { | ||
| startPreview(false); | ||
| takingPicture.set(false); | ||
| if (onTakePictureCallback != null) { | ||
| onTakePictureCallback.onPictureTaken(data); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } catch (RuntimeException e) { | ||
| e.printStackTrace(); | ||
| startPreview(false); | ||
| ; | ||
| takingPicture.set(false); | ||
| } | ||
| } | ||
| @Override | ||
| public void setPermissionCallback(PermissionCallback callback) { | ||
| this.permissionCallback = callback; | ||
| } | ||
| public Camera1Control(Context context) { | ||
| this.context = context; | ||
| previewView = new PreviewView(context); | ||
| openCamera(); | ||
| } | ||
| private void openCamera() { | ||
| setupDisplayView(); | ||
| } | ||
| private void setupDisplayView() { | ||
| // final TextureView textureView = new TextureView(context); | ||
| // previewView.textureView = textureView; | ||
| // previewView.setTextureView(textureView); | ||
| // displayView = previewView; | ||
| // textureView.setSurfaceTextureListener(surfaceTextureListener); | ||
| final SurfaceView surfaceView = new SurfaceView(context); | ||
| previewView.surfaceView = surfaceView; | ||
| previewView.setSurfaceView(surfaceView); | ||
| displayView = previewView; | ||
| SurfaceHolder holder = surfaceView.getHolder(); | ||
| holder.addCallback(surfaceHolderCallback); | ||
| } | ||
| private SurfaceTexture surfaceCache; | ||
| private byte[] buffer = null; | ||
| private void setPreviewCallbackImpl() { | ||
| if (buffer == null) { | ||
| buffer = new byte[displayView.getWidth() | ||
| * displayView.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8]; | ||
| } | ||
| if (camera != null && detectType == MODEL_SCAN) { | ||
| camera.addCallbackBuffer(buffer); | ||
| camera.setPreviewCallback(previewCallback); | ||
| } | ||
| } | ||
| private void clearPreviewCallback() { | ||
| if (camera != null && detectType == MODEL_SCAN) { | ||
| camera.setPreviewCallback(null); | ||
| stopPreview(); | ||
| } | ||
| } | ||
| Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { | ||
| @Override | ||
| public void onPreviewFrame(final byte[] data, Camera camera) { | ||
| // 扫描成功阻止打开新线程处理 | ||
| if (abortingScan.get()) { | ||
| return; | ||
| } | ||
| // 节流 | ||
| if (previewFrameCount++ % 5 != 0) { | ||
| return; | ||
| } | ||
| // 在某些机型和某项项目中,某些帧的data的数据不符合nv21的格式,需要过滤,否则后续处理会导致crash | ||
| if (data.length != parameters.getPreviewSize().width * parameters.getPreviewSize().height * 1.5) { | ||
| return; | ||
| } | ||
| camera.addCallbackBuffer(buffer); | ||
| CameraThreadPool.execute(new Runnable() { | ||
| @Override | ||
| public void run() { | ||
| Camera1Control.this.onRequestDetect(data); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| private void initCamera() { | ||
| try { | ||
| if (camera == null) { | ||
| Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); | ||
| for (int i = 0; i < Camera.getNumberOfCameras(); i++) { | ||
| Camera.getCameraInfo(i, cameraInfo); | ||
| if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { | ||
| cameraId = i; | ||
| } | ||
| } | ||
| try { | ||
| camera = Camera.open(cameraId); | ||
| } catch (Throwable e) { | ||
| e.printStackTrace(); | ||
| startPreview(true); | ||
| return; | ||
| } | ||
| } | ||
| if (parameters == null) { | ||
| parameters = camera.getParameters(); | ||
| parameters.setPreviewFormat(ImageFormat.NV21); | ||
| } | ||
| opPreviewSize(previewView.getWidth(), previewView.getHeight()); | ||
| camera.setPreviewTexture(surfaceCache); | ||
| setPreviewCallbackImpl(); | ||
| startPreview(false); | ||
| } catch (IOException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| private SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() { | ||
| @Override | ||
| public void surfaceCreated(@NonNull SurfaceHolder holder) { | ||
| } | ||
| @Override | ||
| public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { | ||
| } | ||
| @Override | ||
| public void surfaceDestroyed(@NonNull SurfaceHolder holder) { | ||
| } | ||
| }; | ||
| private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { | ||
| @Override | ||
| public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { | ||
| surfaceCache = surface; | ||
| initCamera(); | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { | ||
| opPreviewSize(previewView.getWidth(), previewView.getHeight()); | ||
| startPreview(false); | ||
| setPreviewCallbackImpl(); | ||
| } | ||
| @Override | ||
| public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureUpdated(SurfaceTexture surface) { | ||
| setPreviewCallbackImpl(); | ||
| } | ||
| }; | ||
| // 开启预览 | ||
| private void startPreview(boolean checkPermission) { | ||
| if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { | ||
| if (checkPermission && permissionCallback != null) { | ||
| permissionCallback.onRequestPermission(); | ||
| } | ||
| return; | ||
| } | ||
| if (camera == null) { | ||
| initCamera(); | ||
| } else { | ||
| camera.startPreview(); | ||
| startAutoFocus(); | ||
| } | ||
| } | ||
| private void cancelAutoFocus() { | ||
| camera.cancelAutoFocus(); | ||
| CameraThreadPool.cancelAutoFocusTimer(); | ||
| } | ||
| private void startAutoFocus() { | ||
| CameraThreadPool.createAutoFocusTimerTask(new Runnable() { | ||
| @Override | ||
| public void run() { | ||
| synchronized (Camera1Control.this) { | ||
| if (camera != null && !takingPicture.get()) { | ||
| try { | ||
| camera.autoFocus(new Camera.AutoFocusCallback() { | ||
| @Override | ||
| public void onAutoFocus(boolean success, Camera camera) { | ||
| } | ||
| }); | ||
| } catch (Throwable e) { | ||
| // startPreview是异步实现,可能在某些机器上前几次调用会autofocus failß | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| private void opPreviewSize(int width, @SuppressWarnings("unused") int height) { | ||
| if (parameters != null && camera != null && width > 0) { | ||
| optSize = getOptimalSize(camera.getParameters().getSupportedPreviewSizes()); | ||
| parameters.setPreviewSize(optSize.width, optSize.height); | ||
| previewView.setRatio(1.0f * optSize.width / optSize.height); | ||
| camera.setDisplayOrientation(getSurfaceOrientation()); | ||
| stopPreview(); | ||
| try { | ||
| camera.setParameters(parameters); | ||
| } catch (RuntimeException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| } | ||
| private Camera.Size getOptimalSize(List<Camera.Size> sizes) { | ||
| // int width = previewView.textureView.getWidth(); | ||
| // int height = previewView.textureView.getHeight(); | ||
| int width = previewView.surfaceView.getWidth(); | ||
| int height = previewView.surfaceView.getHeight(); | ||
| System.out.println("------------------------- getOptimalSize"); | ||
| System.out.println(width); | ||
| System.out.println(height); | ||
| Camera.Size pictureSize = sizes.get(0); | ||
| List<Camera.Size> candidates = new ArrayList<>(); | ||
| for (Camera.Size size : sizes) { | ||
| if (size.width >= width && size.height >= height && size.width * height == size.height * width) { | ||
| // 比例相同 | ||
| candidates.add(size); | ||
| } else if (size.height >= width && size.width >= height && size.width * width == size.height * height) { | ||
| // 反比例 | ||
| candidates.add(size); | ||
| } | ||
| } | ||
| if (!candidates.isEmpty()) { | ||
| return Collections.min(candidates, sizeComparator); | ||
| } | ||
| for (Camera.Size size : sizes) { | ||
| if (size.width > width && size.height > height) { | ||
| return size; | ||
| } | ||
| } | ||
| return pictureSize; | ||
| } | ||
| private Comparator<Camera.Size> sizeComparator = new Comparator<Camera.Size>() { | ||
| @Override | ||
| public int compare(Camera.Size lhs, Camera.Size rhs) { | ||
| return Long.signum((long) lhs.width * lhs.height - (long) rhs.width * rhs.height); | ||
| } | ||
| }; | ||
| private void updateFlashMode(int flashMode) { | ||
| switch (flashMode) { | ||
| case FLASH_MODE_TORCH: | ||
| parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); | ||
| break; | ||
| case FLASH_MODE_OFF: | ||
| parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); | ||
| break; | ||
| case ICameraControl.FLASH_MODE_AUTO: | ||
| parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); | ||
| break; | ||
| default: | ||
| parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); | ||
| break; | ||
| } | ||
| camera.setParameters(parameters); | ||
| } | ||
| private int getSurfaceOrientation() { | ||
| @CameraPreview.Orientation | ||
| int orientation = displayOrientation; | ||
| switch (orientation) { | ||
| case CameraPreview.ORIENTATION_PORTRAIT: | ||
| return 90; | ||
| case CameraPreview.ORIENTATION_HORIZONTAL: | ||
| return 0; | ||
| case CameraPreview.ORIENTATION_INVERT: | ||
| return 180; | ||
| default: | ||
| return 90; | ||
| } | ||
| } | ||
| /** | ||
| * 有些相机匹配不到完美的比例。比如。我们的layout是4:3的。预览只有16:9 | ||
| * 的,如果直接显示图片会拉伸,变形。缩放的话,又有黑边。所以我们采取的策略 | ||
| * 是,等比例放大。这样预览的有一部分会超出屏幕。拍照后再进行裁剪处理。 | ||
| */ | ||
| private class PreviewView extends RelativeLayout { | ||
| private TextureView textureView; | ||
| private SurfaceView surfaceView; | ||
| private float ratio = 0.75f; | ||
| void setTextureView(TextureView textureView) { | ||
| this.textureView = textureView; | ||
| removeAllViews(); | ||
| addView(textureView); | ||
| } | ||
| void setSurfaceView(SurfaceView surfaceView) { | ||
| this.surfaceView = surfaceView; | ||
| removeAllViews(); | ||
| addView(surfaceView); | ||
| } | ||
| void setRatio(float ratio) { | ||
| this.ratio = ratio; | ||
| requestLayout(); | ||
| relayout(getWidth(), getHeight()); | ||
| } | ||
| public PreviewView(Context context) { | ||
| super(context); | ||
| } | ||
| @Override | ||
| protected void onSizeChanged(int w, int h, int oldw, int oldh) { | ||
| super.onSizeChanged(w, h, oldw, oldh); | ||
| relayout(w, h); | ||
| } | ||
| private void relayout(int w, int h) { | ||
| int width = w; | ||
| int height = h; | ||
| if (w < h) { | ||
| // 垂直模式,高度固定。 | ||
| height = (int) (width * ratio); | ||
| } else { | ||
| // 水平模式,宽度固定。 | ||
| width = (int) (height * ratio); | ||
| } | ||
| int l = (getWidth() - width) / 2; | ||
| int t = (getHeight() - height) / 2; | ||
| previewFrame.left = l; | ||
| previewFrame.top = t; | ||
| previewFrame.right = l + width; | ||
| previewFrame.bottom = t + height; | ||
| } | ||
| @Override | ||
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { | ||
| super.onLayout(changed, left, top, right, bottom); | ||
| System.out.println("-------------------------== onLayout"); | ||
| System.out.println(previewView.textureView != null); | ||
| System.out.println(previewView.surfaceView != null); | ||
| System.out.println(previewFrame.left); | ||
| System.out.println(previewFrame.top); | ||
| System.out.println(previewFrame.right); | ||
| System.out.println(previewFrame.bottom); | ||
| if(textureView != null){ | ||
| textureView.layout(previewFrame.left, previewFrame.top, previewFrame.right, previewFrame.bottom); | ||
| } | ||
| if(surfaceView != null){ | ||
| surfaceView.layout(previewFrame.left, previewFrame.top, previewFrame.right, previewFrame.bottom); | ||
| } | ||
| } | ||
| } | ||
| @Override | ||
| public Rect getPreviewFrame() { | ||
| return previewFrame; | ||
| } | ||
| } |
| /* | ||
| * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. | ||
| */ | ||
| package com.yufu.camera.preview; | ||
| import android.Manifest; | ||
| import android.annotation.TargetApi; | ||
| import android.content.Context; | ||
| import android.content.pm.PackageManager; | ||
| import android.graphics.ImageFormat; | ||
| import android.graphics.Matrix; | ||
| import android.graphics.Point; | ||
| import android.graphics.Rect; | ||
| 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.CameraMetadata; | ||
| import android.hardware.camera2.CaptureFailure; | ||
| import android.hardware.camera2.CaptureRequest; | ||
| import android.hardware.camera2.CaptureResult; | ||
| import android.hardware.camera2.TotalCaptureResult; | ||
| import android.hardware.camera2.params.StreamConfigurationMap; | ||
| import android.media.Image; | ||
| import android.media.ImageReader; | ||
| import android.os.Build; | ||
| import android.os.Handler; | ||
| import android.os.HandlerThread; | ||
| 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 androidx.annotation.NonNull; | ||
| import androidx.core.content.ContextCompat; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.Comparator; | ||
| import java.util.List; | ||
| import java.util.concurrent.Semaphore; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| @TargetApi(Build.VERSION_CODES.LOLLIPOP) | ||
| public class Camera2Control implements ICameraControl { | ||
| @Override | ||
| public void setDetectCallback(OnDetectPictureCallback callback) { | ||
| // TODO 暂时只用camera | ||
| } | ||
| @Override | ||
| public AtomicBoolean getAbortingScan() { | ||
| // TODO 暂时只用camera | ||
| return null; | ||
| } | ||
| /** | ||
| * Conversion from screen rotation to JPEG orientation. | ||
| */ | ||
| private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); | ||
| private static final int MAX_PREVIEW_SIZE = 2048; | ||
| static { | ||
| ORIENTATIONS.append(Surface.ROTATION_0, 90); | ||
| ORIENTATIONS.append(Surface.ROTATION_90, 0); | ||
| ORIENTATIONS.append(Surface.ROTATION_180, 270); | ||
| ORIENTATIONS.append(Surface.ROTATION_270, 180); | ||
| } | ||
| private static final int STATE_PREVIEW = 0; | ||
| private static final int STATE_WAITING_FOR_LOCK = 1; | ||
| private static final int STATE_WAITING_FOR_CAPTURE = 2; | ||
| private static final int STATE_CAPTURING = 3; | ||
| private static final int STATE_PICTURE_TAKEN = 4; | ||
| private static final int MAX_PREVIEW_WIDTH = 1920; | ||
| private static final int MAX_PREVIEW_HEIGHT = 1080; | ||
| private int flashMode; | ||
| private int orientation = 0; | ||
| private int state = STATE_PREVIEW; | ||
| private Context context; | ||
| private OnTakePictureCallback onTakePictureCallback; | ||
| private PermissionCallback permissionCallback; | ||
| private String cameraId; | ||
| private TextureView textureView; | ||
| private Size previewSize; | ||
| private Rect previewFrame = new Rect(); | ||
| private HandlerThread backgroundThread; | ||
| private Handler backgroundHandler; | ||
| private ImageReader imageReader; | ||
| private CameraCaptureSession captureSession; | ||
| private CameraDevice cameraDevice; | ||
| private CaptureRequest.Builder previewRequestBuilder; | ||
| private CaptureRequest previewRequest; | ||
| private Semaphore cameraLock = new Semaphore(1); | ||
| private int sensorOrientation; | ||
| @Override | ||
| public void start() { | ||
| startBackgroundThread(); | ||
| if (textureView.isAvailable()) { | ||
| openCamera(textureView.getWidth(), textureView.getHeight()); | ||
| textureView.setSurfaceTextureListener(surfaceTextureListener); | ||
| } else { | ||
| textureView.setSurfaceTextureListener(surfaceTextureListener); | ||
| } | ||
| } | ||
| @Override | ||
| public void stop() { | ||
| textureView.setSurfaceTextureListener(null); | ||
| closeCamera(); | ||
| stopBackgroundThread(); | ||
| } | ||
| @Override | ||
| public void pause() { | ||
| setFlashMode(FLASH_MODE_OFF); | ||
| } | ||
| @Override | ||
| public void resume() { | ||
| state = STATE_PREVIEW; | ||
| } | ||
| @Override | ||
| public View getDisplayView() { | ||
| return textureView; | ||
| } | ||
| @Override | ||
| public Rect getPreviewFrame() { | ||
| return previewFrame; | ||
| } | ||
| @Override | ||
| public void takePicture(OnTakePictureCallback callback) { | ||
| this.onTakePictureCallback = callback; | ||
| // 拍照第一步,对焦 | ||
| lockFocus(); | ||
| } | ||
| @Override | ||
| public void setPermissionCallback(PermissionCallback callback) { | ||
| this.permissionCallback = callback; | ||
| } | ||
| @Override | ||
| public void setDisplayOrientation(@CameraPreview.Orientation int displayOrientation) { | ||
| this.orientation = displayOrientation / 90; | ||
| } | ||
| @Override | ||
| public void refreshPermission() { | ||
| openCamera(textureView.getWidth(), textureView.getHeight()); | ||
| } | ||
| @Override | ||
| public void setFlashMode(@FlashMode int flashMode) { | ||
| if (this.flashMode == flashMode) { | ||
| return; | ||
| } | ||
| this.flashMode = flashMode; | ||
| try { | ||
| previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, | ||
| CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); | ||
| updateFlashMode(flashMode, previewRequestBuilder); | ||
| previewRequest = previewRequestBuilder.build(); | ||
| captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler); | ||
| } catch (Exception e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| @Override | ||
| public int getFlashMode() { | ||
| return flashMode; | ||
| } | ||
| public Camera2Control(Context activity) { | ||
| this.context = activity; | ||
| textureView = new TextureView(activity); | ||
| } | ||
| private final TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { | ||
| @Override | ||
| public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { | ||
| openCamera(width, height); | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { | ||
| configureTransform(width, height); | ||
| previewFrame.left = 0; | ||
| previewFrame.top = 0; | ||
| previewFrame.right = width; | ||
| previewFrame.bottom = height; | ||
| } | ||
| @Override | ||
| public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { | ||
| stop(); | ||
| return true; | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureUpdated(SurfaceTexture texture) { | ||
| } | ||
| }; | ||
| private void openCamera(int width, int height) { | ||
| // 6.0+的系统需要检查系统权限 。 | ||
| if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { | ||
| requestCameraPermission(); | ||
| return; | ||
| } | ||
| setUpCameraOutputs(width, height); | ||
| configureTransform(width, height); | ||
| CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); | ||
| try { | ||
| if (!cameraLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | ||
| throw new RuntimeException("Time out waiting to lock camera opening."); | ||
| } | ||
| manager.openCamera(cameraId, deviceStateCallback, backgroundHandler); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } catch (InterruptedException e) { | ||
| throw new RuntimeException("Interrupted while trying to lock camera opening.", e); | ||
| } | ||
| } | ||
| private final CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() { | ||
| @Override | ||
| public void onOpened(@NonNull CameraDevice cameraDevice) { | ||
| cameraLock.release(); | ||
| Camera2Control.this.cameraDevice = cameraDevice; | ||
| createCameraPreviewSession(); | ||
| } | ||
| @Override | ||
| public void onDisconnected(@NonNull CameraDevice cameraDevice) { | ||
| cameraLock.release(); | ||
| cameraDevice.close(); | ||
| Camera2Control.this.cameraDevice = null; | ||
| } | ||
| @Override | ||
| public void onError(@NonNull CameraDevice cameraDevice, int error) { | ||
| cameraLock.release(); | ||
| cameraDevice.close(); | ||
| Camera2Control.this.cameraDevice = null; | ||
| } | ||
| }; | ||
| private void createCameraPreviewSession() { | ||
| try { | ||
| SurfaceTexture texture = textureView.getSurfaceTexture(); | ||
| assert texture != null; | ||
| texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); | ||
| Surface surface = new Surface(texture); | ||
| previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); | ||
| previewRequestBuilder.addTarget(surface); | ||
| updateFlashMode(this.flashMode, previewRequestBuilder); | ||
| cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), | ||
| new CameraCaptureSession.StateCallback() { | ||
| @Override | ||
| public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { | ||
| // The camera is already closed | ||
| if (null == cameraDevice) { | ||
| return; | ||
| } | ||
| captureSession = cameraCaptureSession; | ||
| try { | ||
| previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, | ||
| CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); | ||
| previewRequest = previewRequestBuilder.build(); | ||
| captureSession.setRepeatingRequest(previewRequest, | ||
| captureCallback, backgroundHandler); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| @Override | ||
| public void onConfigureFailed( | ||
| @NonNull CameraCaptureSession cameraCaptureSession) { | ||
| // TODO | ||
| } | ||
| }, null); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| private final ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() { | ||
| @Override | ||
| public void onImageAvailable(ImageReader reader) { | ||
| if (onTakePictureCallback != null) { | ||
| Image image = reader.acquireNextImage(); | ||
| ByteBuffer buffer = image.getPlanes()[0].getBuffer(); | ||
| byte[] bytes = new byte[buffer.remaining()]; | ||
| buffer.get(bytes); | ||
| image.close(); | ||
| onTakePictureCallback.onPictureTaken(bytes); | ||
| } | ||
| } | ||
| }; | ||
| private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { | ||
| private void process(CaptureResult result) { | ||
| switch (state) { | ||
| case STATE_PREVIEW: { | ||
| break; | ||
| } | ||
| case STATE_WAITING_FOR_LOCK: { | ||
| Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); | ||
| if (afState == null) { | ||
| captureStillPicture(); | ||
| return; | ||
| } | ||
| switch (afState) { | ||
| case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: | ||
| case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: | ||
| case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: { | ||
| Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); | ||
| if (aeState == null | ||
| || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { | ||
| captureStillPicture(); | ||
| } else { | ||
| runPreCaptureSequence(); | ||
| } | ||
| break; | ||
| } | ||
| default: | ||
| captureStillPicture(); | ||
| } | ||
| break; | ||
| } | ||
| case STATE_WAITING_FOR_CAPTURE: { | ||
| Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); | ||
| if (aeState == null | ||
| || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE | ||
| || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { | ||
| state = STATE_CAPTURING; | ||
| } else { | ||
| if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { | ||
| captureStillPicture(); | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| case STATE_CAPTURING: { | ||
| Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); | ||
| if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { | ||
| captureStillPicture(); | ||
| } | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| @Override | ||
| public void onCaptureProgressed(@NonNull CameraCaptureSession session, | ||
| @NonNull CaptureRequest request, | ||
| @NonNull CaptureResult partialResult) { | ||
| process(partialResult); | ||
| } | ||
| @Override | ||
| public void onCaptureCompleted(@NonNull CameraCaptureSession session, | ||
| @NonNull CaptureRequest request, | ||
| @NonNull TotalCaptureResult result) { | ||
| process(result); | ||
| } | ||
| }; | ||
| private Size getOptimalSize(Size[] choices, int textureViewWidth, | ||
| int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { | ||
| List<Size> bigEnough = new ArrayList<>(); | ||
| List<Size> notBigEnough = new ArrayList<>(); | ||
| int w = aspectRatio.getWidth(); | ||
| int h = aspectRatio.getHeight(); | ||
| for (Size option : choices) { | ||
| if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight | ||
| && option.getHeight() == option.getWidth() * h / w) { | ||
| if (option.getWidth() >= textureViewWidth | ||
| && option.getHeight() >= textureViewHeight) { | ||
| bigEnough.add(option); | ||
| } else { | ||
| notBigEnough.add(option); | ||
| } | ||
| } | ||
| } | ||
| // Pick the smallest of those big enough. If there is no one big enough, pick | ||
| // the | ||
| // largest of those not big enough. | ||
| if (bigEnough.size() > 0) { | ||
| return Collections.min(bigEnough, sizeComparator); | ||
| } | ||
| for (Size option : choices) { | ||
| if (option.getWidth() >= maxWidth && option.getHeight() >= maxHeight) { | ||
| return option; | ||
| } | ||
| } | ||
| if (notBigEnough.size() > 0) { | ||
| return Collections.max(notBigEnough, sizeComparator); | ||
| } | ||
| return choices[0]; | ||
| } | ||
| private Comparator<Size> sizeComparator = new Comparator<Size>() { | ||
| @Override | ||
| public int compare(Size lhs, Size rhs) { | ||
| return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); | ||
| } | ||
| }; | ||
| private void requestCameraPermission() { | ||
| if (permissionCallback != null) { | ||
| permissionCallback.onRequestPermission(); | ||
| } | ||
| } | ||
| @SuppressWarnings("SuspiciousNameCombination") | ||
| private void setUpCameraOutputs(int width, int height) { | ||
| CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); | ||
| try { | ||
| for (String cameraId : manager.getCameraIdList()) { | ||
| CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); | ||
| Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); | ||
| if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { | ||
| continue; | ||
| } | ||
| StreamConfigurationMap map = characteristics.get( | ||
| CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); | ||
| if (map == null) { | ||
| continue; | ||
| } | ||
| // int preferredPictureSize = (int) (Math.max(textureView.getWidth(), | ||
| // textureView | ||
| // .getHeight()) * 1.5); | ||
| // preferredPictureSize = Math.min(preferredPictureSize, 2560); | ||
| WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); | ||
| Point screenSize = new Point(); | ||
| windowManager.getDefaultDisplay().getSize(screenSize); | ||
| int maxImageSize = Math.max(MAX_PREVIEW_SIZE, screenSize.y * 3 / 2); | ||
| Size size = getOptimalSize(map.getOutputSizes(ImageFormat.JPEG), textureView.getWidth(), | ||
| textureView.getHeight(), maxImageSize, maxImageSize, new Size(4, 3)); | ||
| imageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(), | ||
| ImageFormat.JPEG, 1); | ||
| imageReader.setOnImageAvailableListener( | ||
| onImageAvailableListener, backgroundHandler); | ||
| int displayRotation = orientation; | ||
| // noinspection ConstantConditions | ||
| sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); | ||
| boolean swappedDimensions = false; | ||
| switch (displayRotation) { | ||
| case Surface.ROTATION_0: | ||
| case Surface.ROTATION_180: | ||
| if (sensorOrientation == 90 || sensorOrientation == 270) { | ||
| swappedDimensions = true; | ||
| } | ||
| break; | ||
| case Surface.ROTATION_90: | ||
| case Surface.ROTATION_270: | ||
| if (sensorOrientation == 0 || sensorOrientation == 180) { | ||
| swappedDimensions = true; | ||
| } | ||
| break; | ||
| default: | ||
| } | ||
| int rotatedPreviewWidth = width; | ||
| int rotatedPreviewHeight = height; | ||
| int maxPreviewWidth = screenSize.x; | ||
| int maxPreviewHeight = screenSize.y; | ||
| if (swappedDimensions) { | ||
| rotatedPreviewWidth = height; | ||
| rotatedPreviewHeight = width; | ||
| maxPreviewWidth = screenSize.y; | ||
| maxPreviewHeight = screenSize.x; | ||
| } | ||
| maxPreviewWidth = Math.min(maxPreviewWidth, MAX_PREVIEW_WIDTH); | ||
| maxPreviewHeight = Math.min(maxPreviewHeight, MAX_PREVIEW_HEIGHT); | ||
| previewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), | ||
| rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, | ||
| maxPreviewHeight, size); | ||
| this.cameraId = cameraId; | ||
| return; | ||
| } | ||
| } catch (CameraAccessException | NullPointerException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| private void closeCamera() { | ||
| try { | ||
| cameraLock.acquire(); | ||
| if (null != captureSession) { | ||
| captureSession.close(); | ||
| captureSession = null; | ||
| } | ||
| if (null != cameraDevice) { | ||
| cameraDevice.close(); | ||
| cameraDevice = null; | ||
| } | ||
| if (null != imageReader) { | ||
| imageReader.close(); | ||
| imageReader = null; | ||
| } | ||
| } catch (InterruptedException e) { | ||
| throw new RuntimeException("Interrupted while trying to lock camera closing.", e); | ||
| } finally { | ||
| cameraLock.release(); | ||
| } | ||
| } | ||
| private void startBackgroundThread() { | ||
| backgroundThread = new HandlerThread("ocr_camera"); | ||
| backgroundThread.start(); | ||
| backgroundHandler = new Handler(backgroundThread.getLooper()); | ||
| } | ||
| private void stopBackgroundThread() { | ||
| if (backgroundThread != null) { | ||
| backgroundThread.quitSafely(); | ||
| backgroundThread = null; | ||
| backgroundHandler = null; | ||
| } | ||
| } | ||
| private void configureTransform(int viewWidth, int viewHeight) { | ||
| if (null == textureView || null == previewSize || null == context) { | ||
| return; | ||
| } | ||
| int rotation = orientation; | ||
| Matrix matrix = new Matrix(); | ||
| RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); | ||
| RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.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 / previewSize.getHeight(), | ||
| (float) viewWidth / previewSize.getWidth()); | ||
| matrix.postScale(scale, scale, centerX, centerY); | ||
| matrix.postRotate(90 * (rotation - 2), centerX, centerY); | ||
| } else if (Surface.ROTATION_180 == rotation) { | ||
| matrix.postRotate(180, centerX, centerY); | ||
| } | ||
| textureView.setTransform(matrix); | ||
| } | ||
| // 拍照前,先对焦 | ||
| private void lockFocus() { | ||
| if (captureSession != null && state == STATE_PREVIEW) { | ||
| try { | ||
| previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, | ||
| CameraMetadata.CONTROL_AF_TRIGGER_START); | ||
| state = STATE_WAITING_FOR_LOCK; | ||
| captureSession.capture(previewRequestBuilder.build(), captureCallback, | ||
| backgroundHandler); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| } | ||
| private void runPreCaptureSequence() { | ||
| try { | ||
| previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, | ||
| CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); | ||
| state = STATE_WAITING_FOR_CAPTURE; | ||
| captureSession.capture(previewRequestBuilder.build(), captureCallback, | ||
| backgroundHandler); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| // 拍照session | ||
| private void captureStillPicture() { | ||
| try { | ||
| if (null == context || null == cameraDevice) { | ||
| return; | ||
| } | ||
| final CaptureRequest.Builder captureBuilder = cameraDevice | ||
| .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); | ||
| captureBuilder.addTarget(imageReader.getSurface()); | ||
| captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, | ||
| CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); | ||
| captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(orientation)); | ||
| updateFlashMode(this.flashMode, captureBuilder); | ||
| CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { | ||
| @Override | ||
| public void onCaptureCompleted(@NonNull CameraCaptureSession session, | ||
| @NonNull CaptureRequest request, | ||
| @NonNull TotalCaptureResult result) { | ||
| unlockFocus(); | ||
| } | ||
| @Override | ||
| public void onCaptureFailed(@NonNull CameraCaptureSession session, | ||
| @NonNull CaptureRequest request, | ||
| @NonNull CaptureFailure failure) { | ||
| super.onCaptureFailed(session, request, failure); | ||
| } | ||
| }; | ||
| // 停止预览 | ||
| captureSession.stopRepeating(); | ||
| captureSession.capture(captureBuilder.build(), captureCallback, backgroundHandler); | ||
| state = STATE_PICTURE_TAKEN; | ||
| // unlockFocus(); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| private int getOrientation(int rotation) { | ||
| return (ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360; | ||
| } | ||
| // 停止对焦 | ||
| private void unlockFocus() { | ||
| try { | ||
| previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, | ||
| CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); | ||
| captureSession.capture(previewRequestBuilder.build(), captureCallback, | ||
| backgroundHandler); | ||
| state = STATE_PREVIEW; | ||
| // 预览 | ||
| captureSession.setRepeatingRequest(previewRequest, captureCallback, | ||
| backgroundHandler); | ||
| textureView.setSurfaceTextureListener(surfaceTextureListener); | ||
| } catch (CameraAccessException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| private void updateFlashMode(@FlashMode int flashMode, CaptureRequest.Builder builder) { | ||
| switch (flashMode) { | ||
| case FLASH_MODE_TORCH: | ||
| builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH); | ||
| break; | ||
| case FLASH_MODE_OFF: | ||
| builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF); | ||
| break; | ||
| case ICameraControl.FLASH_MODE_AUTO: | ||
| default: | ||
| builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE); | ||
| break; | ||
| } | ||
| } | ||
| } |
| package com.yufu.camera.preview; | ||
| import android.app.Activity; | ||
| import android.content.Context; | ||
| import android.content.pm.PackageManager; | ||
| import android.graphics.Bitmap; | ||
| import android.graphics.Bitmap.CompressFormat; | ||
| import android.graphics.BitmapFactory; | ||
| import android.graphics.Matrix; | ||
| import android.graphics.Rect; | ||
| import android.graphics.YuvImage; | ||
| import android.hardware.Camera; | ||
| import android.hardware.Camera.PictureCallback; | ||
| import android.hardware.Camera.ShutterCallback; | ||
| import android.media.AudioManager; | ||
| import android.media.CamcorderProfile; | ||
| import android.media.MediaRecorder; | ||
| import android.os.Bundle; | ||
| import android.util.Base64; | ||
| import android.util.DisplayMetrics; | ||
| import android.util.Log; | ||
| import android.view.GestureDetector; | ||
| import android.view.Gravity; | ||
| import android.view.LayoutInflater; | ||
| import android.view.MotionEvent; | ||
| import android.view.Surface; | ||
| import android.view.View; | ||
| import android.view.ViewGroup; | ||
| import android.view.ViewTreeObserver; | ||
| import android.widget.FrameLayout; | ||
| import android.widget.RelativeLayout; | ||
| import androidx.fragment.app.Fragment; | ||
| public class CameraActivity extends Fragment { | ||
| public interface CameraPreviewListener { | ||
| void onPictureTaken(String originalPicture); | ||
| void onPictureTakenError(String message); | ||
| void onSnapshotTaken(String originalPicture); | ||
| void onSnapshotTakenError(String message); | ||
| void onFocusSet(int pointX, int pointY); | ||
| void onFocusSetError(String message); | ||
| void onBackButton(); | ||
| void onCameraStarted(); | ||
| void onStartRecordVideo(); | ||
| void onStartRecordVideoError(String message); | ||
| void onStopRecordVideo(String file); | ||
| void onStopRecordVideoError(String error); | ||
| } | ||
| private CameraPreviewListener eventListener; | ||
| private static final String TAG = "CameraActivity"; | ||
| public FrameLayout mainLayout; | ||
| public FrameLayout frameContainerLayout; | ||
| private boolean canTakePicture = true; | ||
| private View view; | ||
| private Camera.Parameters cameraParameters; | ||
| private Camera mCamera; | ||
| private int numberOfCameras; | ||
| private int cameraCurrentlyLocked; | ||
| private int currentQuality; | ||
| private enum RecordingState { | ||
| INITIALIZING, | ||
| STARTED, | ||
| STOPPED | ||
| } | ||
| private RecordingState mRecordingState = RecordingState.INITIALIZING; | ||
| private MediaRecorder mRecorder = null; | ||
| private String recordFilePath; | ||
| private float opacity; | ||
| // The first rear facing camera | ||
| private int defaultCameraId; | ||
| public String defaultCamera; | ||
| public boolean tapToTakePicture; | ||
| public boolean dragEnabled; | ||
| public boolean tapToFocus; | ||
| public boolean disableExifHeaderStripping; | ||
| public boolean storeToFile; | ||
| public boolean toBack; | ||
| public boolean enableOpacity = false; | ||
| public boolean enableZoom = false; | ||
| public int width; | ||
| public int height; | ||
| public int x; | ||
| public int y; | ||
| public void setEventListener(CameraPreviewListener listener) { | ||
| eventListener = listener; | ||
| } | ||
| private String appResourcesPackage; | ||
| @Override | ||
| public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||
| appResourcesPackage = getActivity().getPackageName(); | ||
| // Inflate the layout for this fragment | ||
| view = inflater.inflate(getResources().getIdentifier("camera_activity", "layout", appResourcesPackage), container, false); | ||
| createCameraPreview(); | ||
| return view; | ||
| } | ||
| private void createCameraPreview() { | ||
| FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); | ||
| layoutParams.setMargins(x, y, 0, 0); | ||
| frameContainerLayout = | ||
| (FrameLayout) view.findViewById(getResources().getIdentifier("frame_container", "id", appResourcesPackage)); | ||
| frameContainerLayout.setLayoutParams(layoutParams); | ||
| //video view | ||
| // mPreview = new Preview(getActivity(), enableOpacity); | ||
| ICameraControl iCameraControl = new Camera1Control(getActivity()); | ||
| View preview = iCameraControl.getDisplayView(); | ||
| mainLayout = (FrameLayout) view.findViewById(getResources().getIdentifier("video_view", "id", appResourcesPackage)); | ||
| mainLayout.setLayoutParams( | ||
| new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT) | ||
| ); | ||
| mainLayout.addView(preview); | ||
| mainLayout.setEnabled(false); | ||
| } | ||
| @Override | ||
| public void onResume() { | ||
| super.onResume(); | ||
| } | ||
| @Override | ||
| public void onPause() { | ||
| super.onPause(); | ||
| } | ||
| } |
| /* | ||
| * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. | ||
| */ | ||
| package com.yufu.camera.preview; | ||
| import java.util.Timer; | ||
| import java.util.TimerTask; | ||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Executors; | ||
| public class CameraThreadPool { | ||
| static Timer timerFocus = null; | ||
| /* | ||
| * 对焦频率 | ||
| */ | ||
| static final long cameraScanInterval = 2000; | ||
| /* | ||
| * 线程池大小 | ||
| */ | ||
| private static int poolCount = Runtime.getRuntime().availableProcessors(); | ||
| private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(poolCount); | ||
| /** | ||
| * 给线程池添加任务 | ||
| * @param runnable 任务 | ||
| */ | ||
| public static void execute(Runnable runnable) { | ||
| fixedThreadPool.execute(runnable); | ||
| } | ||
| /** | ||
| * 创建一个定时对焦的timer任务 | ||
| * @param runnable 对焦代码 | ||
| * @return Timer Timer对象,用来终止自动对焦 | ||
| */ | ||
| public static Timer createAutoFocusTimerTask(final Runnable runnable) { | ||
| if (timerFocus != null) { | ||
| return timerFocus; | ||
| } | ||
| timerFocus = new Timer(); | ||
| TimerTask task = new TimerTask() { | ||
| @Override | ||
| public void run() { | ||
| runnable.run(); | ||
| } | ||
| }; | ||
| timerFocus.scheduleAtFixedRate(task, 0, cameraScanInterval); | ||
| return timerFocus; | ||
| } | ||
| /** | ||
| * 终止自动对焦任务,实际调用了cancel方法并且清空对象 | ||
| * 但是无法终止执行中的任务,需额外处理 | ||
| * | ||
| */ | ||
| public static void cancelAutoFocusTimer() { | ||
| if (timerFocus != null) { | ||
| timerFocus.cancel(); | ||
| timerFocus = null; | ||
| } | ||
| } | ||
| } |
| package com.yufu.camera.preview; | ||
| import android.content.Context; | ||
| import android.view.SurfaceHolder; | ||
| import android.view.SurfaceView; | ||
| class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback { | ||
| private final String TAG = "CustomSurfaceView"; | ||
| CustomSurfaceView(Context context) { | ||
| super(context); | ||
| } | ||
| @Override | ||
| public void surfaceCreated(SurfaceHolder holder) {} | ||
| @Override | ||
| public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} | ||
| @Override | ||
| public void surfaceDestroyed(SurfaceHolder holder) {} | ||
| } |
| package com.yufu.camera.preview; | ||
| import android.content.Context; | ||
| import android.graphics.SurfaceTexture; | ||
| import android.view.TextureView; | ||
| class CustomTextureView extends TextureView implements TextureView.SurfaceTextureListener { | ||
| private final String TAG = "CustomTextureView"; | ||
| CustomTextureView(Context context) { | ||
| super(context); | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {} | ||
| @Override | ||
| public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} | ||
| @Override | ||
| public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { | ||
| return true; | ||
| } | ||
| @Override | ||
| public void onSurfaceTextureUpdated(SurfaceTexture surface) {} | ||
| } |
| /* | ||
| * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. | ||
| */ | ||
| package com.yufu.camera.preview; | ||
| import android.graphics.Rect; | ||
| import android.view.View; | ||
| import androidx.annotation.IntDef; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| /** | ||
| * Android 5.0 相机的API发生很大的变化。些类屏蔽掉了 api的变化。相机的操作和功能,抽象剥离出来。 | ||
| */ | ||
| public interface ICameraControl { | ||
| /** | ||
| * 闪光灯关 {@link #setFlashMode(int)} | ||
| */ | ||
| int FLASH_MODE_OFF = 0; | ||
| /** | ||
| * 闪光灯开 {@link #setFlashMode(int)} | ||
| */ | ||
| int FLASH_MODE_TORCH = 1; | ||
| /** | ||
| * 闪光灯自动 {@link #setFlashMode(int)} | ||
| */ | ||
| int FLASH_MODE_AUTO = 2; | ||
| @IntDef({FLASH_MODE_TORCH, FLASH_MODE_OFF, FLASH_MODE_AUTO}) | ||
| @interface FlashMode { | ||
| } | ||
| /** | ||
| * 照相回调。 | ||
| */ | ||
| interface OnTakePictureCallback { | ||
| void onPictureTaken(byte[] data); | ||
| } | ||
| /** | ||
| * 设置本地质量控制回调,如果不设置则视为不扫描调用本地质量控制代码。 | ||
| */ | ||
| void setDetectCallback(OnDetectPictureCallback callback); | ||
| /** | ||
| * 预览回调 | ||
| */ | ||
| interface OnDetectPictureCallback { | ||
| int onDetect(byte[] data, int rotation); | ||
| } | ||
| /** | ||
| * 打开相机。 | ||
| */ | ||
| void start(); | ||
| /** | ||
| * 关闭相机 | ||
| */ | ||
| void stop(); | ||
| void pause(); | ||
| void resume(); | ||
| /** | ||
| * 相机对应的预览视图。 | ||
| * @return 预览视图 | ||
| */ | ||
| View getDisplayView(); | ||
| /** | ||
| * 看到的预览可能不是照片的全部。返回预览视图的全貌。 | ||
| * @return 预览视图frame; | ||
| */ | ||
| Rect getPreviewFrame(); | ||
| /** | ||
| * 拍照。结果在回调中获取。 | ||
| * @param callback 拍照结果回调 | ||
| */ | ||
| void takePicture(OnTakePictureCallback callback); | ||
| /** | ||
| * 设置权限回调,当手机没有拍照权限时,可在回调中获取。 | ||
| * @param callback 权限回调 | ||
| */ | ||
| void setPermissionCallback(PermissionCallback callback); | ||
| /** | ||
| * 设置水平方向 | ||
| * @param displayOrientation 参数值见 {@link CameraPreview.Orientation} | ||
| */ | ||
| void setDisplayOrientation(@CameraPreview.Orientation int displayOrientation); | ||
| /** | ||
| * 获取到拍照权限时,调用些函数以继续。 | ||
| */ | ||
| void refreshPermission(); | ||
| /** | ||
| * 获取已经扫描成功,处理中 | ||
| */ | ||
| AtomicBoolean getAbortingScan(); | ||
| /** | ||
| * 设置闪光灯状态。 | ||
| * @param flashMode {@link #FLASH_MODE_TORCH,#FLASH_MODE_OFF,#FLASH_MODE_AUTO} | ||
| */ | ||
| void setFlashMode(@FlashMode int flashMode); | ||
| /** | ||
| * 获取当前闪光灯状态 | ||
| * @return 当前闪光灯状态 参见 {@link #setFlashMode(int)} | ||
| */ | ||
| @FlashMode | ||
| int getFlashMode(); | ||
| } |
| /* | ||
| * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. | ||
| */ | ||
| package com.yufu.camera.preview; | ||
| public interface PermissionCallback { | ||
| boolean onRequestPermission(); | ||
| } |
| package com.yufu.camera.preview; | ||
| import android.util.Log; | ||
| import androidx.annotation.IntDef; | ||
| public class CameraPreview { | ||
| public String echo(String value) { | ||
| Log.i("Echo", value); | ||
| return value; | ||
| } | ||
| /** | ||
| * 垂直方向 {@link #setOrientation(int)} | ||
| */ | ||
| public static final int ORIENTATION_PORTRAIT = 0; | ||
| /** | ||
| * 水平方向 {@link #setOrientation(int)} | ||
| */ | ||
| public static final int ORIENTATION_HORIZONTAL = 90; | ||
| /** | ||
| * 水平翻转方向 {@link #setOrientation(int)} | ||
| */ | ||
| public static final int ORIENTATION_INVERT = 270; | ||
| @IntDef({ORIENTATION_PORTRAIT, ORIENTATION_HORIZONTAL, ORIENTATION_INVERT}) | ||
| public @interface Orientation { | ||
| } | ||
| } |
| package com.yufu.camera.preview; | ||
| import android.content.Context; | ||
| import android.graphics.Color; | ||
| import android.hardware.camera2.CameraAccessException; | ||
| import android.hardware.camera2.CameraManager; | ||
| import android.view.ViewGroup; | ||
| import android.widget.FrameLayout; | ||
| import androidx.fragment.app.FragmentManager; | ||
| import androidx.fragment.app.FragmentTransaction; | ||
| import com.getcapacitor.JSObject; | ||
@@ -9,15 +19,54 @@ import com.getcapacitor.Plugin; | ||
| import java.util.Arrays; | ||
| @CapacitorPlugin(name = "CameraPreview") | ||
| public class CameraPreviewPlugin extends Plugin { | ||
| private CameraPreview implementation = new CameraPreview(); | ||
| private final int containerViewId = 20; | ||
| @PluginMethod | ||
| public void echo(PluginCall call) { | ||
| String value = call.getString("value"); | ||
| @PluginMethod | ||
| public void echo(PluginCall call) { | ||
| String value = call.getString("value"); | ||
| JSObject ret = new JSObject(); | ||
| call.resolve(ret); | ||
| } | ||
| JSObject ret = new JSObject(); | ||
| ret.put("value", implementation.echo(value)); | ||
| call.resolve(ret); | ||
| } | ||
| @PluginMethod | ||
| public void start(PluginCall call) throws CameraAccessException { | ||
| // CameraManager cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); | ||
| // String[] cameraIds = cameraManager.getCameraIdList(); | ||
| // | ||
| // System.out.println("11111--------"); | ||
| // System.out.println(Arrays.toString(cameraIds)); | ||
| // System.out.println("11111--------"); | ||
| bridge.getActivity().runOnUiThread(new Runnable() { | ||
| @Override | ||
| public void run() { | ||
| FrameLayout containerView = getBridge().getActivity().findViewById(containerViewId); | ||
| if(containerView == null){ | ||
| containerView = new FrameLayout(getActivity().getApplicationContext()); | ||
| containerView.setId(containerViewId); | ||
| getBridge().getWebView().setBackgroundColor(Color.TRANSPARENT); | ||
| ((ViewGroup) getBridge().getWebView().getParent()).addView(containerView); | ||
| CameraActivity fragment = new CameraActivity(); | ||
| FragmentManager fragmentManager = bridge.getActivity().getSupportFragmentManager(); | ||
| FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); | ||
| fragmentTransaction.add(containerView.getId(),fragment); | ||
| fragmentTransaction.commit(); | ||
| } else { | ||
| call.reject("camera already started"); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| @PluginMethod | ||
| public void stop(PluginCall call) { | ||
| } | ||
| } |
@@ -0,0 +0,0 @@ export interface CameraPreviewPlugin { |
| export {}; | ||
| //# sourceMappingURL=definitions.js.map |
@@ -0,0 +0,0 @@ import type { CameraPreviewPlugin } from './definitions'; |
@@ -0,0 +0,0 @@ import { registerPlugin } from '@capacitor/core'; |
@@ -0,0 +0,0 @@ import { WebPlugin } from '@capacitor/core'; |
+0
-0
@@ -0,0 +0,0 @@ import { WebPlugin } from '@capacitor/core'; |
@@ -0,0 +0,0 @@ 'use strict'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\r\nconst CameraPreview = registerPlugin('CameraPreview', {\r\n web: () => import('./web').then(m => new m.CameraPreviewWeb()),\r\n});\r\nexport * from './definitions';\r\nexport { CameraPreview };\r\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\r\nexport class CameraPreviewWeb extends WebPlugin {\r\n async echo(options) {\r\n console.log('ECHO', options);\r\n return options;\r\n }\r\n}\r\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;;;AACK,MAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;AAClE,CAAC;;ACFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;AAChD,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;AACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrC,QAAQ,OAAO,OAAO,CAAC;AACvB,KAAK;AACL;;;;;;;;;"} | ||
| {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst CameraPreview = registerPlugin('CameraPreview', {\n web: () => import('./web').then(m => new m.CameraPreviewWeb()),\n});\nexport * from './definitions';\nexport { CameraPreview };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CameraPreviewWeb extends WebPlugin {\n async echo(options) {\n console.log('ECHO', options);\n return options;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;;;AACK,MAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;AAClE,CAAC;;ACFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;AAChD,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;AACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrC,QAAQ,OAAO,OAAO,CAAC;AACvB,KAAK;AACL;;;;;;;;;"} |
+0
-0
@@ -0,0 +0,0 @@ var capacitorCameraPreview = (function (exports, core) { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\r\nconst CameraPreview = registerPlugin('CameraPreview', {\r\n web: () => import('./web').then(m => new m.CameraPreviewWeb()),\r\n});\r\nexport * from './definitions';\r\nexport { CameraPreview };\r\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\r\nexport class CameraPreviewWeb extends WebPlugin {\r\n async echo(options) {\r\n console.log('ECHO', options);\r\n return options;\r\n }\r\n}\r\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;;ICFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;IAChD,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;IACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,OAAO,OAAO,CAAC;IACvB,KAAK;IACL;;;;;;;;;;;;;;;;;"} | ||
| {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst CameraPreview = registerPlugin('CameraPreview', {\n web: () => import('./web').then(m => new m.CameraPreviewWeb()),\n});\nexport * from './definitions';\nexport { CameraPreview };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CameraPreviewWeb extends WebPlugin {\n async echo(options) {\n console.log('ECHO', options);\n return options;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;;ICFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;IAChD,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;IACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,OAAO,OAAO,CAAC;IACvB,KAAK;IACL;;;;;;;;;;;;;;;;;"} |
+1
-1
| { | ||
| "name": "capacitor-plugin-camera-preview", | ||
| "version": "0.0.2", | ||
| "version": "0.0.3", | ||
| "description": "相机预览", | ||
@@ -5,0 +5,0 @@ "main": "dist/plugin.cjs.js", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
68753
340.39%35
29.63%1653
1201.57%1
Infinity%