当前位置:   article > 正文

Camera视频采集 提供源码_camera.setpreviewcallback回调数据通过udp发送

camera.setpreviewcallback回调数据通过udp发送

目录

1、  Camera基础知识

2、 视频采集的流程

2.1. SurfaceView的设置

2.2. Camera的初始化和Params设置

2.3. Camera的预览、帧回调处理、保存图片旋转和镜像处理

2.4. Camera的切换


1、  Camera基础知识

Camera 有几个重要的基础概念。

1. facing相机的方向,一般后置摄像头和前置摄像头。

2. Orientation:相机采集图片的角度,摄像头的传感器在手机中是横向的,在预览的时候根据Camera的预览方向进行顺时针旋转对应角度进行设置即可正常预览。如果不正确设置会导致预览时出现倒立、镜像等问题。把预览的图片保存为相册也要单独设置方向,注意这个方向和预览方向互不相干。

3. 预览图片的大小   预览容器的大小和摄像头支持的图片预览的图片大小,如果设置了Camera不支持的预览大小,会导致黑屏。

4. 可以设置帧回调然后,在每一帧中进行业务处理,比如,人脸识别等功能

5. Camera预览的图片格式有NV21 YUV420sp等

6. Camera需要一个容器把的Surface显示在屏幕上,一般SurfaceView,TextureView等。

2、 视频采集的流程

1. 通过SurfaceView拿到SurfaceHolder,然后设置addCallback回调,当Surface创建、销毁、改变时触发对应的回调,在其中可以进行Camera的初始化以及参数设置

2. 通过new Camera(cameralId)生成一个对象。然后Camera.getParams获取到相关的参数,可以把重要的或者说比较关系的parmas打印出来,比如说支持多少个摄像头、支持的预览图片的大小、每个摄像头的方向等信息。可以根据需要设置对应的参数,比如图片的格式、图片的预览大小等。当然有一个必须要设置的就是Camera的预览展示方向,否则预览的到图片和正常的方向不一致。

3. 可以设置Camera的setPreviewCallback获取每一帧的回调,根据需要设置处理,开始预览startPreview以及帧回调的处理

4. 摄像头的切换

如果出发Camera的切换,需要把前一个Camera释放,重新生成和设置Camera切换预览

 和Activity生命周期想的关系,这个是有SurfaceView决定的,当页面可见时(onResume)创建或重新创建,页面不可见时(onPause)销毁释放

**具体实现如下**

2.1. SurfaceView的设置

  1. private SurfaceHolder mSurfaceHolder;
  2.     private void initSurfaceView() {
  3.         mSurfaceHolder = surfaceview.getHolder();
  4.         mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
  5.             @Override
  6.             public void surfaceCreated(SurfaceHolder holder) {
  7.                 Log.d(TAG, "surfaceCreated: ");
  8.                 handleSurfaceCreated();
  9.             }
  10.             @Override
  11.             public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  12.             }
  13.             @Override
  14.             public void surfaceDestroyed(SurfaceHolder holder) {
  15.                 Log.d(TAG, "surfaceDestroyed: ");
  16.                 handleSurfaceDestroyed();
  17.             }
  18.         });
  19.     }
  20.     private void handleSurfaceDestroyed() {
  21.         releaseCamera();
  22.         mSurfaceHolder = null;
  23.         Log.i(TAG, "handleSurfaceDestroyed: ");
  24.     }
  25.     private void handleSurfaceCreated() {
  26.         Log.i(TAG, "handleSurfaceCreated: start");
  27.         if (mSurfaceHolder == null) {
  28.             mSurfaceHolder = surfaceview.getHolder();
  29.         }
  30.         if (mCamera == null) {
  31.             initCamera(curCameraId);
  32.         }
  33.         try {
  34.             //问题2:页面重新打开后SurfaceView的内容黑屏
  35.             //Camera is being used after Camera.release() was called
  36.             //在surfaceDestroyed时调用了Camera的release 但是没有设置为null,
  37.             //--》如何解耦合,把生命周期相关的方法和Camera的生命周期绑定而不时在回调中处理,方便业务实现
  38.             //onResume--》surfaceCreated
  39.             //onPause--》surfaceDestroyed
  40.             mCamera.setPreviewDisplay(mSurfaceHolder);
  41.         } catch (IOException e) {
  42.             e.printStackTrace();
  43.             Log.e(TAG, "handleSurfaceCreated: " + e.getMessage());
  44.         }
  45.         startPreview();
  46.         Log.i(TAG, "handleSurfaceCreated: end");
  47.     }
  48.     private void startPreview() {
  49. //        mCamera.setPreviewCallback(new Camera.PreviewCallback() {
  50. //            @Override
  51. //            public void onPreviewFrame(byte[] data, Camera camera) {
  52. //                Log.i(TAG, "onPreviewFrame: setPreviewCallback");
  53. //            }
  54. //        });
  55.         //问题:很多时候,不仅仅要预览,在预览视频的时候,希望能做一些检测,比如人脸检测等。这就需要获得预览帧视频,该如何做呐?
  56.         mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
  57.             @Override
  58.             public void onPreviewFrame(byte[] data, Camera camera) {
  59.                 Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
  60.                 Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
  61.                 YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
  62.                 ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
  63.                 if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
  64.                     Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
  65.                     return;
  66.                 }
  67.                 byte[] bytes = os.toByteArray();
  68.                 Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
  69.                 //这里的处理方式是简单的把预览的一帧图保存下。如果需要做人脸设别或者其他操作,可以拿到这个bitmap进行分析处理
  70.                 //我们可以通过找出这张图片发现预览保存的图片的方向是不对的,还是Camera的原始方向,需要旋转一定角度才可以。
  71.                 if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
  72.                     bitmap = BitmapUtils.rotate(bitmap,90);
  73.                 }else {
  74.                     bitmap = BitmapUtils.mirror(BitmapUtils.rotate(bitmap,270));
  75.                 }
  76.                 FileUtils.saveBitmapToFile(bitmap,"oneShot.jpg");
  77.             }
  78.         });
  79. //        mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
  80. //            @Override
  81. //            public void onPreviewFrame(byte[] data, Camera camera) {
  82. //                Log.i(TAG, "onPreviewFrame: setPreviewCallbackWithBuffer");
  83. //            }
  84. //        });
  85.         mCamera.startPreview();
  86.     }

2.2. Camera的初始化和Params设置

  1. private void initCamera(int cameraId) {
  2.     curCameraId = cameraId;
  3.     mCamera = Camera.open(curCameraId);
  4.     Log.d(TAG, "initCamera: Camera Open ");
  5.     setCamerDisplayOrientation(this, curCameraId, mCamera);
  6.     if (!hadPrinted) {
  7.         printCameraInfo();
  8.         hadPrinted = true;
  9.     }
  10.     Camera.Parameters parameters = mCamera.getParameters();
  11.     Camera.Size closelyPreSize = CameraUtil.getCloselyPreSize(true, SystemUtils.getDisplayWidth(), SystemUtils.getDisplayHeight(), parameters.getSupportedPreviewSizes());
  12.     Log.i(TAG, "initCamera: closelyPreSizeW="+closelyPreSize.width+" closelyPreSizeH="+closelyPreSize.height);
  13.     parameters.setPreviewSize(closelyPreSize.width, closelyPreSize.height);
  14.     mCamera.setParameters(parameters);
  15. }
  16. private void printCameraInfo() {
  17.     //1. 调用getParameters获取Parameters
  18.     Camera.Parameters parameters = mCamera.getParameters();
  19.     //2. 获取Camera预览支持的图片格式(常见的是NV21和YUV420sp)
  20.     int previewFormat = parameters.getPreviewFormat();
  21.     Log.d(TAG, "initCamera: previewFormat=" + previewFormat); // NV21
  22.     //3. 获取Camera预览支持的W和H的大小,
  23.     // 手动设置Camera的W和H时,要检测camera是否支持,如果设置了Camera不支持的预览大小,会出现黑屏。
  24.     // 那么这里有一个问,由于Camera不同厂商支持的预览大小不同,如果做到兼容呐?
  25.     // 需要使用方采用一定策略进行选择(比如:选择和预设置的最接近的支持的WH)
  26.     //通过输出信息,我们可以看到Camera是横向的即 W>H
  27.     List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
  28.     for (Camera.Size item : supportedPreviewSizes
  29.         ) {
  30.         Log.d(TAG, "initCamera: supportedPreviewSizes w= " + item.width + " h=" + item.height);
  31.     }
  32.     //可以看到Camera的宽高是屏幕的宽高是不一致的,手机屏幕是竖屏的H>W,而Camera的宽高是横向的W>H
  33.     Camera.Size previewSize = parameters.getPreviewSize();
  34.     int[] physicalSS = SystemUtils.getPhysicalSS(this);
  35.     Log.i(TAG, "initCamera: w=" + previewSize.width + " h=" + previewSize.height
  36.           + " screenW=" + SystemUtils.getDisplayWidth() + " screenH=" + SystemUtils.getDisplayHeight()
  37.           + " physicalW=" + physicalSS[0] + " physicalH=" + physicalSS[1]);
  38.     //4. 获取Camera支持的帧率 一般是10~30
  39.     List<Integer> supportedPreviewFrameRates = parameters.getSupportedPreviewFrameRates();
  40.     for (Integer item : supportedPreviewFrameRates
  41.         ) {
  42.         Log.i(TAG, "initCamera: supportedPreviewFrameRates frameRate=" + item);
  43.     }
  44.     //5. 获取Camera的个数信息,以及每一个Camera的orientation,这个很关键,如果根据Camera的orientation正确的设置Camera的DisplayOrientation可能会导致预览倒止或者出现镜像的情况
  45.     int numberOfCameras = Camera.getNumberOfCameras();
  46.     Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  47.     for (int i = 0; i < numberOfCameras; i++) {
  48.         Camera.getCameraInfo(i, cameraInfo);
  49.         Log.i(TAG, "initCamera: facing=" + cameraInfo.facing
  50.               + " orientation=" + cameraInfo.orientation);
  51.     }
  52. }
  53. /**
  54.      *
  55.      * @param activity
  56.      * @param cameraId
  57.      * @param camera
  58.      */
  59. public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
  60.     Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  61.     Camera.getCameraInfo(cameraId, cameraInfo);
  62.     int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
  63.     Log.i(TAG, "setCamerDisplayOrientation: rotation=" + rotation + " cameraId=" + cameraId);
  64.     int degress = 0;
  65.     switch (rotation) {
  66.         case Surface.ROTATION_0:
  67.             degress = 0;
  68.             break;
  69.         case Surface.ROTATION_90:
  70.             degress = 90;
  71.             break;
  72.         case Surface.ROTATION_180:
  73.             degress = 180;
  74.             break;
  75.         case Surface.ROTATION_270:
  76.             degress = 270;
  77.             break;
  78.     }
  79.     int result = 0;
  80.     if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  81.         result = (cameraInfo.orientation + degress) % 360;
  82.         result = (360 - result) % 360;
  83.     } else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
  84.         result = (cameraInfo.orientation - degress + 360) % 360;
  85.     }
  86.     Log.i(TAG, "setCamerDisplayOrientation: result=" + result + " cameraId=" + cameraId + " facing=" + cameraInfo.facing + " cameraInfo.orientation=" + cameraInfo.orientation);
  87.     camera.setDisplayOrientation(result);
  88. }

2.3. Camera的预览、帧回调处理、保存图片旋转和镜像处理

  1. private void startPreview() {
  2.     //问题六:很多时候,不仅仅要预览,在预览视频的时候,希望能做一些检测,比如人脸检测等。这就需要获得预览帧视频
  3.     mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
  4.         @Override
  5.         public void onPreviewFrame(byte[] data, Camera camera) {
  6.             Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
  7.             Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
  8.             YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
  9.             ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
  10.             if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
  11.                 Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
  12.                 return;
  13.             }
  14.             byte[] bytes = os.toByteArray();
  15.             Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
  16.             //这里的处理方式是简单的把预览的一帧图保存下。如果需要做人脸设别或者其他操作,可以拿到这个bitmap进行分析处理
  17.             //我们可以通过找出这张图片发现预览保存的图片的方向是不对的,还是Camera的原始方向,需要旋转一定角度才可以。
  18.             if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
  19.                 bitmap = rotate(bitmap,90);
  20.             }else {
  21.                 bitmap = mirror(rotate(bitmap,270));
  22.             }
  23.             saveBitmapToFile(bitmap,"oneShot.jpg");
  24.         }
  25.     });
  26.     mCamera.startPreview();
  27. }
  28. void saveBitmapToFile(Bitmap bitmap, String fileName) {
  29.     File file = new File(MyApplication.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
  30.     if (file.exists()) {
  31.         file.delete();
  32.     }
  33.     try {
  34.         file.createNewFile();
  35.         FileOutputStream fos = new FileOutputStream(file);
  36.         bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
  37.         fos.flush();
  38.         fos.close();
  39.     } catch (IOException e) {
  40.         e.printStackTrace();
  41.     }
  42. }
  43. //水平镜像翻转
  44. public Bitmap mirror(Bitmap rawBitmap) {
  45.     Matrix matrix = new Matrix();
  46.     matrix.postScale(-1f1f);
  47.     return Bitmap.createBitmap(rawBitmap, 00, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
  48. }
  49. //旋转
  50. public Bitmap rotate(Bitmap rawBitmap, float degree) {
  51.     Matrix matrix = new Matrix();
  52.     matrix.postRotate(degree);
  53.     return Bitmap.createBitmap(rawBitmap, 00, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
  54. }

2.4. Camera的切换

  1. private void switchCamera() {
  2.     if (mCamera != null) {
  3.         releaseCamera();
  4.         initCamera((curCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT);
  5.         try {
  6.             mCamera.setPreviewDisplay(mSurfaceHolder);
  7.         } catch (IOException e) {
  8.             e.printStackTrace();
  9.         }
  10.         startPreview();
  11.     }
  12. }
  13. private void releaseCamera() {
  14.     if (mCamera != null) {
  15.         mCamera.setPreviewCallback(null);
  16.         mCamera.stopPreview();
  17.         mCamera.release();
  18.         mCamera = null;
  19.     }
  20. }

## 4.3 遇到的问题  (页面卡住、黑屏、倒立等)

### 问题一 :切换摄像头后画面卡住

解决:需要先关闭Camera释放资源,然后重新打开切换后的Camera,重新设置PreviewDisplay 然后开始预览

### 问题二: 页面重新打开后(在预览页按Home键推到后台,然后再回到前台)SurfaceView的内容黑屏

解决:通过查看log看到有一个异常信息:“Camera is being used after Camera.release() was called” 原来是在surfaceDestroyed时调用了Camera的release 但是没有设置为null,    在surfaceCreated的时候是根据camera是否为空来判断是否需要重新初始化。

### 问题三:前摄像头预览出现倒立并且是镜像状态

  1. public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
  2.     Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  3.     Camera.getCameraInfo(cameraId, cameraInfo);
  4.     int result = 0;
  5.     if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  6.         result = (cameraInfo.orientation) % 360;
  7. camera.setDisplayOrientation(result);

解决:

  1. public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
  2.     Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  3.     Camera.getCameraInfo(cameraId, cameraInfo);
  4.     int result = 0;
  5.     if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  6.         result = (cameraInfo.orientation) % 360;
  7.         result = (360 - result) % 360;
  8. camera.setDisplayOrientation(result);

### 问题四:很多时候,不仅仅要预览,在预览视频的时候,希望能做一些检测,比如人脸检测等。这就需要获得预览帧视频,该如何做呐?

Camera提供了setPreviewCallback、setOneShotPreviewCallback以及setPreviewCallbackWithBuffer三个方法供使用者进行帧回调处理。比如下面的处理时,通过setOneShotPreviewCallback获取一帧的bitmap,然后进行保存到文件

  1. mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
  2.     @Override
  3.         public void onPreviewFrame(byte[] data, Camera camera) {
  4.         Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
  5.         Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
  6.         YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
  7.         ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
  8.         if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
  9.             Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
  10.             return;
  11.         }
  12.         byte[] bytes = os.toByteArray();
  13.         Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);           
  14.         //这里的处理方式是简单的把预览的一帧图保存下。如果需要做人脸设别或者其他操作,可以拿到这个bitmap进行分析处理
  15.         FileUtils.saveBitmapToFile(bitmap,"oneShot.jpg");
  16.     }
  17. });

### 问题五:发现保存的图片和预览的图片的方向不一致

解决: 预览通过Camera的setDisplay Orientation根据前后摄像头的需旋转的角度进行了处理,但是保存为图片是和预览时的设置时不相关的,需要单独处理


 

我们可以通过找出这张图片发现预览保存的图片的方向是不对的,还是Camera的原始方向,需要旋转一定角度才可以。

  1. if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
  2.     bitmap = BitmapUtils.rotate(bitmap,90);
  3. }else {
  4.     bitmap = BitmapUtils.mirror(BitmapUtils.rotate(bitmap,270));
  5. }
  6. public class BitmapUtils {
  7.     //水平镜像翻转
  8.     public static Bitmap mirror(Bitmap rawBitmap) {
  9.         Matrix matrix = new Matrix();
  10.         matrix.postScale(-1f1f);
  11.         return Bitmap.createBitmap(rawBitmap, 00, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
  12.     }
  13.     //旋转
  14.     public static Bitmap rotate(Bitmap rawBitmap, float degree) {
  15.         Matrix matrix = new Matrix();
  16.         matrix.postRotate(degree);
  17.         return Bitmap.createBitmap(rawBitmap, 00, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
  18.     }
  19. }

提供一份作者自己写的源码:

链接:https://pan.baidu.com/s/1c6PWFdYMatzxypAPqAFtmQ 
提取码:8888

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/249856
推荐阅读
相关标签
  

闽ICP备14008679号