一种基于Android Camera无预览采集YUV帧的实现方式

3,318 阅读1分钟

有时需要使用Android原生的camera无预览采集YUV数据,用于生成图片与h.264编码。要实现该功能,只需要调用setPreviewTexture即可。

1、相机CameraHelper

public class CameraHelper implements Camera.PreviewCallback {

    private static final String TAG = "CameraHelper";
    public static final int WIDTH = 640;
    public static final int HEIGHT = 480;

    private int mCameraId;
    private Camera mCamera;
    private byte[] buffer;
    private SurfaceTexture surfaceTexture = new SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
    private boolean shut = false;
    private OnPictureRawCapture pictureRawCaptureListener;

    public void setPictureRawCaptureListener(OnPictureRawCapture pictureRawCaptureListener) {
        this.pictureRawCaptureListener = pictureRawCaptureListener;
    }

    public CameraHelper(int cameraId){
        mCameraId = cameraId;
    }

    public void switchCamera(){
        if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
            mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
        }else{
            mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
        }
        stopPreview();
        startPreview();
    }

    public void startPreview() {
        try {
            mCamera = Camera.open(mCameraId);
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewFormat(ImageFormat.NV21);
            parameters.setPreviewSize(WIDTH, HEIGHT);

            mCamera.setParameters(parameters);
            buffer = new byte[WIDTH * HEIGHT *3/2];

            mCamera.addCallbackBuffer(buffer);
            mCamera.setPreviewCallbackWithBuffer(this);

            mCamera.setPreviewTexture(surfaceTexture);//
            mCamera.startPreview();
        } catch (IOException e) {
            Log.e(TAG, "start preview failed");
            e.printStackTrace();
        }
    }

    public void stopPreview() {
        if (mCamera == null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if(shut){
            shut = false;
            if(pictureRawCaptureListener != null){
                pictureRawCaptureListener.onCapture(data);//生成图片
            }
        }
        //to do
        //h.264编码、模式识别等
        camera.addCallbackBuffer(buffer);
    }

    public void takePicture(){//拍照
        shut = true;
    }

    public interface OnPictureRawCapture{
        void onCapture(byte[] data);
    }

    public Camera.Size getPreviewSize(){
        Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
        return previewSize;
    }
}

2、相机启动线程

public class CameraRunnable implements Runnable{
    private CameraHelper cameraHelper;

    public CameraRunnable(CameraHelper cameraHelper) {
        this.cameraHelper = cameraHelper;
    }

        @Override
        public void run() {
            cameraHelper.startPreview();
        }
}

3、生成图片文件线程

public class PictureRunnable implements Runnable{
        private static final String CAPTRUE_PATH = "sdcard/img/";
        private static final String TEMPLATE = "yyyy_MM_dd_HH_mm_ss";
        private SimpleDateFormat sdf = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);

        private Camera.Size previewSize;
        private byte[] imgRaw;

        public PictureRunnable(byte[] data,Camera.Size size){
            this.imgRaw = data;
            this.previewSize = size;
        }

        @Override
        public void run() {
            createBmpAndSaveFile(imgRaw);
        }

        private void createBmpAndSaveFile(byte[] data) {
            BitmapFactory.Options newOpts = new BitmapFactory.Options();
            newOpts.inJustDecodeBounds = true;
            YuvImage yuvimage = new YuvImage(
                    data,
                    ImageFormat.NV21,
                    previewSize.width,
                    previewSize.height,
                    null);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);
            byte[] rawImage = baos.toByteArray();
            Bitmap bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (bitmap == null)
                return;
            else {
                try {
                    saveFile(bitmap);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private void saveFile(Bitmap bm) throws IOException {
            String fileName = getFileName()+".jpeg";
            File dirFile = new File(CAPTRUE_PATH);
            if(!dirFile.exists()){
                dirFile.mkdir();
            }
            File myCaptureFile = new File(CAPTRUE_PATH + fileName);
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
            bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
            bos.flush();
            bos.close();
            bm.recycle();
            bm=null;
            Log.d("CameraHelper","new img:"+myCaptureFile.getAbsolutePath());
            EventBus.getDefault().post(myCaptureFile.getAbsolutePath());
        }

        private String getFileName() {
            long time = System.currentTimeMillis();
            sdf.applyPattern(TEMPLATE);
            return sdf.format(time);
        }
    }

4、程序入口

4.1 开启相机

    private CameraHelper myCamera;
    private ExecutorService executorService;
    private CameraRunnable cameraRunnable;
    
 executorService = new ThreadPoolExecutor(2,4,0, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(10));
        myCamera = new CameraHelper( Camera.CameraInfo.CAMERA_FACING_BACK);
        myCamera.setPictureRawCaptureListener(this);
        cameraRunnable = new CameraRunnable(myCamera);
        executorService.execute(cameraRunnable);

4.2 拍照

myCamera.takePicture();

4.2.1 保存图片

@Override
    public void onCapture(byte[] data) {
        Log.d("CameraHelper","onCapture "+data.length);
        Camera.Size previewSize = myCamera.getPreviewSize();
        executorService.execute(new PictureRunnable(data,previewSize));
    }

4.3 关闭相机

executorService.shutdownNow();
myCamera.stopPreview();

5 测试结果