博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Camera2预览和实时帧数据获取
阅读量:2389 次
发布时间:2019-05-10

本文共 22420 字,大约阅读时间需要 74 分钟。

1、预览

2、角度旋转

3、实时帧数据获取ImageReader的参数设置

4、实时帧数据格式转换

package com.neatech.stface.tools;import android.Manifest;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.graphics.ImageFormat;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.SurfaceTexture;import android.hardware.Camera;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.params.StreamConfigurationMap;import android.media.ExifInterface;import android.media.Image;import android.media.ImageReader;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.util.Log;import android.util.Size;import android.view.Surface;import android.view.TextureView;import android.widget.RelativeLayout;import com.neatech.stface.R;import com.neatech.stface.broadcast.CameraErrorReceiver;import com.neatech.stface.view.FaceDrawerView;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.nio.ByteBuffer;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Comparator;import java.util.List;/** * 功能:视频预览 * 作者:陈晗 * 时间:2019-3-7 16:47 */public class CameraPreviewHelper {    private String Tag = "CameraPreviewHelper";    private Context mContext;    private TextureView mTextureView;    private HandlerThread mHandlerThread;    private Handler childHandler;    private Handler mHandler;    private Activity activity;    //private TrackThread mTrackThread;    private LivenessTackThread mLiveTackThread;    private TrackAndSearchThread trackAndSearchThread;    private FaceDrawerView mFaceDrawerView;    private String mCameraId;    private Size mPreviewSize;    private CameraManager mCameraManager;//摄像头管理器    private CameraDevice mCameraDevice;    private SurfaceTexture mSurfaceTexture = null;    private CaptureRequest.Builder mCaptureRequestBuilder;    private CaptureRequest mCaptureRequest;    private CameraCaptureSession mCameraCaptureSession;    private boolean isConnect = false;    private ImageReader mImageReader;    private int capacity = -1;    private static byte[] yv12bytes;    private static byte[] imgData;  //1280*720 img    private int imgFormat;    private int imgHeight;    private int ingWidth;    public final static int picMode = 640;   //720p ,1080p 640p    public static final int CAPTURE_IMG_PREPARED = 1;    public static final int TRY_TO_CAPTURE_IMG = 2;    //  private static boolean isLiveness = false;    public static int preview_width;      //preview    public static int preview_higth;    public static int surface_width;    //surface    public static int surface_higth;    public static int delaytest = 1;    public CameraPreviewHelper(Activity activity, TextureView textureView) {        mContext = activity.getBaseContext();        mTextureView = textureView;        this.activity = activity;        mFaceDrawerView = new FaceDrawerView(mContext);        ((RelativeLayout) activity.findViewById(R.id.rootview_relativelayout_camera_verity)).addView(mFaceDrawerView);        mHandlerThread = new HandlerThread(Tag);        mHandlerThread.start();        childHandler = new Handler(mHandlerThread.getLooper());        mHandler = new Handler(activity.getMainLooper(), mHandlerCallback);    }    /**     * open camera and start preview     *     * @return     */    public boolean Connect(boolean isliveness) {        if (mContext == null || mTextureView == null) {            Log.e("asda", "Connect");            return false;        }        //   isLiveness = false;// isliveness;        mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);        // mSurfaceTexture = mTextureView.getSurfaceTexture();        if (mCameraDevice != null)            mCameraDevice.close();        isConnect = true;//        if (isLiveness) {//            mLiveTackThread = new LivenessTackThread(activity, mFaceDrawerView);//            mLiveTackThread.start();//        } else {//            mTrackThread = new TrackThread(activity, mFaceDrawerView, mTextName);//            mTrackThread.start();        trackAndSearchThread = new TrackAndSearchThread(activity, mFaceDrawerView);        trackAndSearchThread.start();        //     }        return true;    }    public void DisConnect() {        mContext = null;//        if (mSurfaceTexture != null) {//            mSurfaceTexture.release();//            Log.e(Tag, "mSurfaceTexture release");//////        }        activity = null;//            if (mTrackThread != null)//                mTrackThread.stopTrack();        if (trackAndSearchThread != null)            trackAndSearchThread.stopTrack();        if (mTextureView != null) {            mTextureView = null;        }        // mPreviewed=false;        if (childHandler != null) {            childHandler.removeCallbacksAndMessages(null);            childHandler = null;        }        if (mHandler != null)            mHandler.removeCallbacksAndMessages(null);            mHandler = null;        if (mHandlerThread != null) {            mHandlerThread.quit();            mHandlerThread = null;        }        if (null != mCameraCaptureSession) {            mCameraCaptureSession.close();            mCameraCaptureSession = null;        }        if (null != mCameraDevice) {            mCameraDevice.close();            mCameraDevice = null;        }        if (null != mImageReader) {            mImageReader.close();            mImageReader = null;        }    }    TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {        @Override        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {            //  mSurfaceTexture=surface;            try {                if (mCameraManager == null) {                    //获取摄像头管理                    mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);                    String[] CameraIdList=mCameraManager.getCameraIdList();//获取可用相机列表                    Log.e(Tag,"可用相机的个数是:"+CameraIdList.length);                    mCameraId = CameraIdList[0];                    CameraCharacteristics cameraCharacteristics=mCameraManager.getCameraCharacteristics(mCameraId);//获取某个相机(摄像头特性)                    cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);//检查支持                    //打开摄像头                    try {                        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {                            return;                        }                        if (mCameraManager == null)                            return;                        //镜面效果                        surface_width = width;                        surface_higth = height;                        //获取StreamConfigurationMap,他是管理摄像头支持的所有输出格式和尺寸                        StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);                        //获得最接近的尺寸大小                        mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class),width,height);                        Log.d(Tag, "getOptimalPreviewSize: w" + mPreviewSize.getWidth() + "  h" + mPreviewSize.getHeight());                        if (picMode == 720) {                            mPreviewSize = null;                            mPreviewSize = new Size(1280, 720);                            preview_higth = mPreviewSize.getWidth();                            preview_width = mPreviewSize.getHeight();                        } else {                            preview_higth = mPreviewSize.getWidth();                            preview_width = mPreviewSize.getHeight();                        }                        //如果视频显示需要角度旋转 用该函数进行角度转正                        configureTransform(surface_width, surface_higth);                        /**                         * 实时帧数据获取类                         * 由于获取实时帧所以选用YV12或者YUV_420_888两个格式,暂时不采用JPEG格式                         * 在真机显示的过程中,不同的数据格式所设置的width和height需要注意,否侧视频会很卡顿                         * YV12:width 720, height 960                         * YUV_420_888:width 720, height 960                         * JPEG:获取帧数据不能用 ImageFormat.JPEG 格式,否则你会发现预览非常卡的,因为渲染 JPEG 数据量过大,导致掉帧,所以预览帧请使用其他编码格式                         */                        mImageReader = ImageReader.newInstance(720, 960, ImageFormat.YV12, 10);//YUV_420_888                        mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);                        //打开摄像头                        mCameraManager.openCamera(mCameraId, stateCallback, mHandler);                    } catch (CameraAccessException e) {                        e.printStackTrace();                    }                }            } catch (Exception e) {                e.printStackTrace();                if (activity != null)                    activity.sendBroadcast(new Intent(CameraErrorReceiver.ACTION_CAMERA_OPEN_ERROR));            }        }        @Override        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {            configureTransform(width, height);        }        @Override        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {            if (mCameraDevice != null) {                mCameraDevice.close();                mCameraDevice = null;                Log.d(Tag, "onSurfaceTextureDestroyed: stopPreview");                // mPreviewed = false;            }            Log.e(Tag, "onSurfaceTextureDestroyed");            return true;        }        @Override        public void onSurfaceTextureUpdated(SurfaceTexture surface) {        }    };    /**     * 摄像头创建监听     */    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {        @Override        public void onOpened(CameraDevice camera) {//打开摄像头            mCameraDevice = camera;            if (mCameraDevice == null)                return;            SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();            mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());            Surface previewSurface = new Surface(mSurfaceTexture);            try {                mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);                mCaptureRequestBuilder.addTarget(previewSurface);                //设置实时帧数据接收                mCaptureRequestBuilder.addTarget(mImageReader.getSurface());                mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {                    @Override                    public void onConfigured(@NonNull CameraCaptureSession session) {                        try{                            mCameraCaptureSession = session;                            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);                            //开始预览                            mCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, childHandler);                        } catch (CameraAccessException e) {                            e.printStackTrace();                        }                    }                    @Override                    public void onConfigureFailed(@NonNull CameraCaptureSession session) {                    }                },childHandler);            } catch (CameraAccessException e) {                e.printStackTrace();            }        }        @Override        public void onDisconnected(CameraDevice camera) {//关闭摄像头            if (null != mCameraDevice) {                mCameraDevice.close();                mCameraDevice = null;            }        }        @Override        public void onError(CameraDevice camera, int error) {//发生错误            if (null != mCameraDevice) {                mCameraDevice.close();                mCameraDevice = null;            }            Log.e(Tag, "打开摄像头失败");        }    };    /**     * 收到实时帧数据进行处理:     * 一定要使用Image image = reader.acquireLatestImage();image.close();要不然会很卡顿     * 由于ImageReader不兼容NV21,所以将YV12转换为NV21     */    private ImageReader.OnImageAvailableListener mOnImageAvailableListener            = new ImageReader.OnImageAvailableListener() {        @Override        public void onImageAvailable(ImageReader reader) {            Image image = reader.acquireLatestImage();            if (!trackAndSearchThread.isHandleImg) {                imgFormat = ImageFormat.NV21;                imgHeight = image.getHeight();                ingWidth = image.getWidth();                imgData = ImageUtil.getBytesFromImageAsType(image, ImageUtil.NV21);                if (mHandler != null)                    mHandler.sendEmptyMessage(CAPTURE_IMG_PREPARED);            }            image.close();        }    };    Handler.Callback mHandlerCallback = new Handler.Callback() {        @Override        public boolean handleMessage(Message msg) {            // Log.e(Tag,"handleMessage");            //准备图片            if (msg.what == TRY_TO_CAPTURE_IMG) {            }            if (msg.what == CAPTURE_IMG_PREPARED) {                // mFaceDrawerView.drawFaces(facelist, mFaceDrawerView.getWidth(), 720, mContext.getResources().getColor(R.color.white));                //                synchronized (imgData) {                    //uiHandler.sendEmptyMessage(CAPTURE_IMG_PREPARED);                }                //mTrackThread.handleImgData(imgData, ingWidth, imgHeight, imgFormat);                trackAndSearchThread.handleImgData(imgData, ingWidth, imgHeight, imgFormat);            }            return false;        }    };    /**     * 解决预览变形问题     *     * @param sizeMap     * @param width     * @param height     * @return     */    //选择sizeMap中大于并且最接近width和height的size    private Size getOptimalSize(Size[] sizeMap, int width, int height) {        List
sizeList = new ArrayList<>(); for (Size option : sizeMap) { if (width > height) { if (option.getWidth() > width && option.getHeight() > height) { sizeList.add(option); } } else { if (option.getWidth() > height && option.getHeight() > width) { sizeList.add(option); } } } if (sizeList.size() > 0) { return Collections.min(sizeList, new Comparator
() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight()); } }); } return sizeMap[0]; } private Size getOptimalPreviewSize(Size []sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size Size size = null; for (int i = 0; i < sizes.length; i++) { size = sizes[i]; double ratio = (double) size.getWidth() / size.getHeight(); if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.getHeight() - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.getHeight() - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (int i = 0; i < sizes.length; i++) { size = sizes[i]; if (Math.abs(size.getHeight() - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.getHeight() - targetHeight); } } } return optimalSize; } public void updateList() { if (trackAndSearchThread != null) trackAndSearchThread.isUpdateFuture = true; } private void configureTransform(int viewWidth, int viewHeight) { if (null == mTextureView || null == mPreviewSize || null == activity) { return; } int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.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 scale1 = Math.max( (float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth()); float scale2 = Math.min( (float)mPreviewSize.getHeight() / viewHeight, (float) mPreviewSize.getWidth() / viewWidth); matrix.postScale(scale1, scale2, centerX, centerY); matrix.postRotate(90 * rotation, centerX, centerY); } mTextureView.setTransform(matrix); }}
package com.neatech.stface.tools;import android.graphics.ImageFormat;import android.media.Image;import java.nio.ByteBuffer;/** * 功能:YV12转其他格式 * 作者:chenhan * 时间:2019-3-7 16:47 *//** * yuv420p:  yyyyyyyyuuvv * yuv420sp: yyyyyyyyuvuv * nv21:     yyyyyyyyvuvu */public class ImageUtil {    public static final int YUV420P = 0;    public static final int YUV420SP = 1;    public static final int NV21 = 2;    private static final String TAG = "ImageUtil";    /***     * 此方法内注释以640*480为例     * 未考虑CropRect的     */    public static byte[] getBytesFromImageAsType(Image image, int type) {        try {            //获取源数据,如果是YUV格式的数据planes.length = 3            //plane[i]里面的实际数据可能存在byte[].length <= capacity (缓冲区总大小)            final Image.Plane[] planes = image.getPlanes();            //数据有效宽度,一般的,图片width <= rowStride,这也是导致byte[].length <= capacity的原因            // 所以我们只取width部分            int width = image.getWidth();            int height = image.getHeight();            //此处用来装填最终的YUV数据,需要1.5倍的图片大小,因为Y U V 比例为 4:1:1            byte[] yuvBytes = new byte[width * height * ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8];            //目标数组的装填到的位置            int dstIndex = 0;            //临时存储uv数据的            byte uBytes[] = new byte[width * height / 4];            byte vBytes[] = new byte[width * height / 4];            int uIndex = 0;            int vIndex = 0;            int pixelsStride, rowStride;            for (int i = 0; i < planes.length; i++) {                pixelsStride = planes[i].getPixelStride();                rowStride = planes[i].getRowStride();                ByteBuffer buffer = planes[i].getBuffer();                //如果pixelsStride==2,一般的Y的buffer长度=640*480,UV的长度=640*480/2-1                //源数据的索引,y的数据是byte中连续的,u的数据是v向左移以为生成的,两者都是偶数位为有效数据                byte[] bytes = new byte[buffer.capacity()];                buffer.get(bytes);                int srcIndex = 0;                if (i == 0) {                    //直接取出来所有Y的有效区域,也可以存储成一个临时的bytes,到下一步再copy                    for (int j = 0; j < height; j++) {                        System.arraycopy(bytes, srcIndex, yuvBytes, dstIndex, width);                        srcIndex += rowStride;                        dstIndex += width;                    }                } else if (i == 1) {                    //根据pixelsStride取相应的数据                    for (int j = 0; j < height / 2; j++) {                        for (int k = 0; k < width / 2; k++) {                            uBytes[uIndex++] = bytes[srcIndex];                            srcIndex += pixelsStride;                        }                        if (pixelsStride == 2) {                            srcIndex += rowStride - width;                        } else if (pixelsStride == 1) {                            srcIndex += rowStride - width / 2;                        }                    }                } else if (i == 2) {                    //根据pixelsStride取相应的数据                    for (int j = 0; j < height / 2; j++) {                        for (int k = 0; k < width / 2; k++) {                            vBytes[vIndex++] = bytes[srcIndex];                            srcIndex += pixelsStride;                        }                        if (pixelsStride == 2) {                            srcIndex += rowStride - width;                        } else if (pixelsStride == 1) {                            srcIndex += rowStride - width / 2;                        }                    }                }            }            image.close();            //根据要求的结果类型进行填充            switch (type) {                case YUV420P:                    System.arraycopy(uBytes, 0, yuvBytes, dstIndex, uBytes.length);                    System.arraycopy(vBytes, 0, yuvBytes, dstIndex + uBytes.length, vBytes.length);                    break;                case YUV420SP:                    for (int i = 0; i < vBytes.length; i++) {                        yuvBytes[dstIndex++] = uBytes[i];                        yuvBytes[dstIndex++] = vBytes[i];                    }                    break;                case NV21:                    for (int i = 0; i < vBytes.length; i++) {                        yuvBytes[dstIndex++] = vBytes[i];                        yuvBytes[dstIndex++] = uBytes[i];                    }                    break;            }            return yuvBytes;        } catch (final Exception e) {            if (image != null) {                image.close();            }        }        return null;    }}

 

转载地址:http://psxab.baihongyu.com/

你可能感兴趣的文章
OSSIM 2.1 - Multiple security vulnerabilities
查看>>
PHP文件上传源码分析(RFC1867)
查看>>
关于php5.*后的时区问题 date_default_timezone_set ();
查看>>
“Cache-control”常见的取值有private、no-cache、max-age、must-revalidate等
查看>>
安全工具集合
查看>>
Metasploit 3.3 Development Updates
查看>>
Windows Services for UNIX Version 3.5
查看>>
Linux 测试工具
查看>>
Modifying SSH to Capture Login Credentials from Attackers
查看>>
nikto 2.1 coming
查看>>
How to own a Windows Domain
查看>>
Longcat – multi-protocol stress testing tool
查看>>
数据流0day原理+实践
查看>>
淺談以STIX實現網路威脅情報標準化框架
查看>>
Top IT management trends - the next 5 years
查看>>
推荐 OWASP - Transport Layer Protection Cheat Sheet
查看>>
AutoNessus v1.3.2 released
查看>>
hack tools
查看>>
rhel5中管理swap空间
查看>>
/proc filesystem allows bypassing directory permissions on Linux
查看>>