1 概述

通过Android Camera拍摄预览中设置setPreviewCallback实现onPreviewFrame接口,实时截取每一帧视频流数据


2 知识点

① Android Camera使用:    参考 Refs/Related 0-4

Camera 支持格式

拍照流程

② Android SurfaceView使用:  参考 Refs/Related 5-10

③ Camera权限


3 核心源码

① SurfaceView相关

// 定义对象 private SurfaceView mSurfaceview = null;  // SurfaceView对象:(视图组件)视频显示  private SurfaceHolder mSurfaceHolder = null;  // SurfaceHolder对象:(抽象接口)SurfaceView支持类  private Camera mCamera =null;     // Camera对象,相机预览
 // InitSurfaceView private void initSurfaceView() {  mSurfaceview = (SurfaceView) this.findViewById(R.id.Surfaceview);  mSurfaceHolder = mSurfaceview.getHolder(); // 绑定SurfaceView,取得SurfaceHolder对象  mSurfaceHolder.addCallback(mainActivity.this); // SurfaceHolder加入回调接口  // mSurfaceHolder.setFixedSize(176, 144); // 预览大小設置  mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 設置顯示器類型,setType必须设置 }

② 主Activity实现SurfaceHolder.Callback接口,编写回调函数

/*【SurfaceHolder.Callback 回调函数】*/ public void surfaceCreated(SurfaceHolder holder) // SurfaceView启动时/初次实例化,预览界面被创建时,该方法被调用。 {  // TODO Auto-generated method stub    mCamera = Camera.open();// 开启摄像头(2.3版本后支持多摄像头,需传入参数)   try  {      Log.i(TAG, "SurfaceHolder.Callback:surface Created");   mCamera.setPreviewDisplay(mSurfaceHolder);//set the surface to be used for live preview

  } catch (Exception ex)  {   if(null != mCamera)   {    mCamera.release();    mCamera = null;        }   Log.i(TAG+"initCamera", ex.getMessage());  } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) // 当SurfaceView/预览界面的格式和大小发生改变时,该方法被调用 {  // TODO Auto-generated method stub    Log.i(TAG, "SurfaceHolder.Callback:Surface Changed");  //mPreviewHeight = height;  //mPreviewWidth = width;  initCamera();   } public void surfaceDestroyed(SurfaceHolder holder)  // SurfaceView销毁时,该方法被调用 {  // TODO Auto-generated method stub  Log.i(TAG, "SurfaceHolder.Callback:Surface Destroyed");  if(null != mCamera)  {   mCamera.setPreviewCallback(null); //!!这个必须在前,不然退出出错   mCamera.stopPreview();    bIfPreview = false;    mCamera.release();   mCamera = null;       } }

核心子函数:  相机预览

 1 /*【2】【相机预览】*/ 2  private void initCamera()//surfaceChanged中调用 3  { 4   Log.i(TAG, "going into initCamera"); 5   if (bIfPreview) 6   { 7    mCamera.stopPreview();//stopCamera(); 8   } 9   if(null != mCamera)10   {11    try12    {13     /* Camera Service settings*/    14     Camera.Parameters parameters = mCamera.getParameters();15     // parameters.setFlashMode("off"); // 无闪光灯16     parameters.setPictureFormat(PixelFormat.JPEG); //Sets the image format for picture 设定相片格式为JPEG,默认为NV21    17     parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP); //Sets the image format for preview picture,默认为NV2118     /*【ImageFormat】JPEG/NV16(YCrCb format,used for Video)/NV21(YCrCb format,used for Image)/RGB_565/YUY2/YU12*/19     20     // 【调试】获取caera支持的PictrueSize,看看能否设置??21     List<Size> pictureSizes = mCamera.getParameters().getSupportedPictureSizes();22     List<Size> previewSizes = mCamera.getParameters().getSupportedPreviewSizes();23     List<Integer> previewFormats = mCamera.getParameters().getSupportedPreviewFormats();24     List<Integer> previewFrameRates = mCamera.getParameters().getSupportedPreviewFrameRates();25     Log.i(TAG+"initCamera", "cyy support parameters is ");26     Size psize = null;27     for (int i = 0; i < pictureSizes.size(); i++)28     {29      psize = pictureSizes.get(i);30      Log.i(TAG+"initCamera", "PictrueSize,width: " + psize.width + " height" + psize.height);31     }32     for (int i = 0; i < previewSizes.size(); i++)33     {34      psize = previewSizes.get(i);35      Log.i(TAG+"initCamera", "PreviewSize,width: " + psize.width + " height" + psize.height);36     }37     Integer pf = null;38     for (int i = 0; i < previewFormats.size(); i++)39     {40      pf = previewFormats.get(i);41      Log.i(TAG+"initCamera", "previewformates:" + pf);42     }43     44     // 设置拍照和预览图片大小45     parameters.setPictureSize(640, 480); //指定拍照图片的大小46     parameters.setPreviewSize(mPreviewWidth, mPreviewHeight); // 指定preview的大小 47 //这两个属性 如果这两个属性设置的和真实手机的不一样时,就会报错48     49 // 横竖屏镜头自动调整50     if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) 51     {52      parameters.set("orientation", "portrait"); //53      parameters.set("rotation", 90); // 镜头角度转90度(默认摄像头是横拍) 54      mCamera.setDisplayOrientation(90); // 在2.2以上可以使用55     } else// 如果是横屏56     {57      parameters.set("orientation", "landscape"); //58      mCamera.setDisplayOrientation(0); // 在2.2以上可以使用59     } 60     61     /* 视频流编码处理 */ 62     //添加对视频流处理函数63     64     65 // 设定配置参数并开启预览66     mCamera.setParameters(parameters); // 将Camera.Parameters设定予Camera    67     mCamera.startPreview(); // 打开预览画面68     bIfPreview = true;69     70     // 【调试】设置后的图片大小和预览大小以及帧率71     Camera.Size csize = mCamera.getParameters().getPreviewSize();72     mPreviewHeight = csize.height; //73     mPreviewWidth = csize.width;74     Log.i(TAG+"initCamera", "after setting, previewSize:width: " + csize.width + " height: " + csize.height);75     csize = mCamera.getParameters().getPictureSize();76     Log.i(TAG+"initCamera", "after setting, pictruesize:width: " + csize.width + " height: " + csize.height);77     Log.i(TAG+"initCamera", "after setting, previewformate is " + mCamera.getParameters().getPreviewFormat());78     Log.i(TAG+"initCamera", "after setting, previewframetate is " + mCamera.getParameters().getPreviewFrameRate());79    } catch (Exception e)80    { 81     e.printStackTrace();82    }83   }84  }

说明1:  在/* 视频流编码处理 */ 中通过setPreviewCallback添加对视频流进行处理,如
     mCamera.setPreviewCallback(new encoderVideo(mCamera.getParameters().getPreviewSize().width, 
         mCamera.getParameters().getPreviewSize().height,(ImageView) findViewById(R.id.ImageView2)));//①原生yuv420sp视频存储方式
     mCamera.setPreviewCallback(new encoderH264(mCamera.getParameters().getPreviewSize().width, 
         mCamera.getParameters().getPreviewSize().height)); //②x264编码方式 
     mCamera.setPreviewCallback(mJpegPreviewCallback);  //③JPEG压缩方式
 
 说明2: 当然也可以不在主Acitivity中实现SurfaceHolder.Callback接口,而是在①的initSurfaceView中的addCallback函数,修改为
 mSurfaceHolder.addCallback(new MyCallback);  再在这函数中实现上述三个回调函数

③视频帧回调接口

 1  // 【获取视频预览帧的接口】 2   mJpegPreviewCallback = new Camera.PreviewCallback() 3   { 4    @Override 5    public void onPreviewFrame(byte[] data, Camera camera)  6    { 7     //传递进来的data,默认是YUV420SP的 8 // TODO Auto-generated method stub     9     try10     {11      Log.i(TAG, "going into onPreviewFrame");12      //mYUV420sp = data;   // 获取原生的YUV420SP数据13      YUVIMGLEN = data.length;14      15      // 拷贝原生yuv420sp数据16      mYuvBufferlock.acquire();17      System.arraycopy(data, 0, mYUV420SPSendBuffer, 0, data.length);18      //System.arraycopy(data, 0, mWrtieBuffer, 0, data.length);19      mYuvBufferlock.release();20      21      // 开启编码线程,如开启PEG编码方式线程22      mSendThread1.start();23      24     } catch (Exception e)25     {26      Log.v("System.out", e.toString());27     }// endtry    28    }// endonPriview  29   };

转自:http://blog.csdn.NET/yanzi1225627/article/details/8605061

很多时候,android摄像头模块不仅预览,拍照这么简单,而是需要在预览视频的时候,能够做出一些检测,比如最常见的人脸检测。在未按下拍照按钮前,就检测出人脸然后矩形框标示出来,再按拍照。那么如何获得预览帧视频么?

只需要在Activity里继承PreviewCallback这个接口就行了。示例如下:

public class RectPhoto extends Activity implements SurfaceHolder.Callback, PreviewCallback{}。(注意这个SurfaceHolder.Callback是用来预览摄像头视频,参见我的前贴)。

继承这个方法后,会自动重载这个函数:public void onPreviewFrame(byte[] data, Camera camera) {}这个函数里的data就是实时预览帧视频。一旦程序调用PreviewCallback接口,就会自动调用onPreviewFrame这个函数。调用PreviewCallback的方法有三种,可以参考这里,总共有三种方式调用这个回调。所谓回调就是当条件满足时,自动触发调用这个函数。分别是:.setPreviewCallback, setOneShotPreviewCallback, setPreviewCallbackWithBuffer, 我一般是使用第二种方式。

这里解释下,如果Activity继承了PreviewCallback这个接口,只需                Camera.setOneShotPreviewCallback(this);就可以了。程序会自动调用主类Activity里的onPreviewFrame函数。如果Camera.setOneShotPreviewCallback()这个函数是在主类Activity里的内部类如class A里面,里面的参数应写为Camera.setOneShotPreviewCallback(YourActivity.this)。当然这里,也可以定义一个变量,如Camera.PreviewCallback mPreviewCallback,在调用的时候用Camera.setOneShotPreviewCallback(mPreviewCallback)来完成。相信很多人都熟悉这点,就不罗嗦了。

按理说只要在onPreviewFrame()这个函数里写你的处理程序就可以了。当通常不这么做,因为处理实时预览帧视频的算法可能比较复杂,这就需要借助AsyncTask开启一个线程在后台处理数据。这里假设我们定义一个FaceTask来进行人脸检测,可以这样写:

/*自定义的FaceTask类,开启一个线程分析数据*/
    private class FaceTask extends AsyncTask<Void, Void, Void>{

private byte[] mData;
        //构造函数
        PalmTask(byte[] data){
            this.mData = data;
        }
        
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub
            Size size = myCamera.getParameters().getPreviewSize(); //获取预览大小
            final int w = size.width;  //宽度
            final int h = size.height;
            final YuvImage image = new YuvImage(mData, ImageFormat.NV21, w, h, null);
            ByteArrayOutputStream os = new ByteArrayOutputStream(mData.length);
            if(!image.compressToJpeg(new Rect(0, 0, w, h), 100, os)){
                return null;
            }
            byte[] tmp = os.toByteArray();
            Bitmap bmp = BitmapFactory.decodeByteArray(tmp, 0,tmp.length); 
            doSomethingNeeded(bmp);   //自己定义的实时分析预览帧视频的算法

return null;
        }
        
    }

注意上面的bmp就是Bitmap格式的实时预览帧数据。doSomethingNeeded(bmp) 就是你要对预览帧视频进行的处理,可以是检测人脸或其他,如分析有无火灾。或者是进行传输。  另外,这里是通过YuvImage和ImageFormat.NV21来解析数据的。在华为u9200上,android4.0.3的系统运行良好。不同手机上支持的格式可能有所不同。网上也有自己写算法进行转化的,需要的可以自己找,但这里如果支持这个格式就不用自己写转换算法了。 
     onPreviewFrame()里可以这样写:

/*获取预览帧视频*/
    public void onPreviewFrame(byte[] data, Camera camera) {
        // TODO Auto-generated method stub
        if(null != mFaceTask){
            switch(mFaceTask.getStatus()){
            case RUNNING:
                return;
            case PENDING:
                mFaceTask.cancel(false);
                break;
            }
        }
        mFaceTask = new PalmTask(data);
        mFaceTask.execute((Void)null);
        
    }

上面的mFaceTask是一个全局变量。通过onPreviewFrame,AsyncTask的综合应用,让复杂的处理算法执行在后台,也就是doInBackground这里,是不是比较绿色?

接下来就是什么时候触发onPreviewFrame()这个函数里,可以是按一个按键触发一次,就在按键的监听里写上       myCamera.setOneShotPreviewCallback(RectPhoto.this);便会自动触发一次。有人说想先聚焦,然后再分析预览帧。就在onAutofocus里的回调写。如下:

//自动聚焦变量回调
        myAutoFocusCallback = new AutoFocusCallback() {

public void onAutoFocus(boolean success, Camera camera) {
                // TODO Auto-generated method stub
                if(success)//success表示对焦成功
                {
                    Log.i(tag, "myAutoFocusCallback: success...");
                    myCamera.setOneShotPreviewCallback(RectPhoto.this);

}
                else
                {
                    //未对焦成功

Log.i(tag, "myAutoFocusCallback: 失败了...");

                  //这里也可以加上myCamera.autoFocus(myAutoFocusCallback),如果聚焦失败就再次启动聚焦。
                }

}
        };

大多数时候,希望程序自动每隔多长时间,自动进行一次检测预览帧。这也好办,实施如下:

[java] view plaincopy
  1. <span xmlns="http://www.w3.org/1999/xhtml" style="">    class ScanThread implements Runnable{
  2. public void run() {
  3. // TODO Auto-generated method stub
  4. while(!Thread.currentThread().isInterrupted()){
  5. try {
  6. if(null != myCamera && isPreview)
  7. {
  8. //myCamera.autoFocus(myAutoFocusCallback);
  9. myCamera.setOneShotPreviewCallback(RectPhoto.this);
  10. Log.i(tag, "setOneShotPreview...");
  11. }
  12. Thread.sleep(1500);
  13. } catch (InterruptedException e) {
  14. // TODO Auto-generated catch block
  15. e.printStackTrace();
  16. Thread.currentThread().interrupt();
  17. }
  18. }
  19. }
  20. }</span>

在on Create里new Thread(new ScanThread()).start()开启扫描线程。如果想手动触发中止这种扫描活动,可以在ScanThread里的while循环里设置标志位,具体可看我以前的博文。

最后提醒的是,如果程序中加入了previewCallback,在surfaceDestroy释放camera的时候,最好执行myCamera.setOneShotPreviewCallback(null); 或者myCamera.setPreviewCallback(null);中止这种回调,然后再释放camera更安全。否则可能会报错。

Android Camera设置setPreviewCallback实现onPreviewFrame接口实时截取每一帧视频流数据相关推荐

  1. android camera分辨率设置,android CAMERA 设置照片大小

    最近在 REAL210 android 上调试 CAMERA 驱动. 发现原来的开发板只支持 640*320 以及 320*240 , 但是用的是 OV3640 300W 的摄像头,应该可以拍照到 2 ...

  2. android Camera 设置焦距

    1.添加Camera权限 2.判断是否支持变焦 public boolean isSupportZoom(){boolean isSuppport = true;if (mCamera.getPara ...

  3. android camera设置分辨率,Android Camera2预览输出大小

    我正在尝试使用Camera2 API通过ImageReader(YUV_420_888格式)设置相机预览.首先,我需要选择支持的预览尺寸: StreamConfigurationMap scmap = ...

  4. Android 音频开发(四) 如何播放一帧音频数据下

    再看这一篇文章前,如果你是小白,我建议你先看一下Android 音频开发(一) 基础入门篇这一篇.今天继续讲解如何通过Android SDK自带API实现播放一帧音频数据. 我们都知道,Android ...

  5. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

  6. 【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )

    文章目录 安卓直播推流专栏博客总结 一. Camera 传感器方向简介 二. Camera 图像传感器横向显示数据 三. Camera 图像传感器纵向显示数据 四. 设置 Camera 预览数据方向 ...

  7. Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

    第一篇 Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜) 第二篇 Android Camera使用OpenGL ES 2.0和T ...

  8. Android Camera API 2使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

    这段时间有点忙,一直没时间写第三篇教程,其实代码很早之前就写好了.本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读前面两篇文档. 第一篇 Android Camer ...

  9. Android Camera使用OpenGL ES 2.0和TextureView对预览进行实时二次处理(黑白滤镜)

    本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读下述第一篇文档,因为第一篇讲过的知识,本文并不会细讲了. 第一篇 Android Camera使用OpenGL ES ...

最新文章

  1. loj2058 「TJOI / HEOI2016」求和 NTT
  2. SpringMVC 学习系列 (3) 之 URL请求到Action的映射规则
  3. PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to fi
  4. 对称密码获取(OJ)
  5. 技巧打开网页进行客户个性化信息提交(代码编写)
  6. Ansible roles角色实战案例:httpd nginx memcached mysql
  7. 浅析Java的“克隆”方法[zt]
  8. 【转】3.3SharePoint服务器端对象模型 之 访问文件和文件夹(Part 3)
  9. HDU 1142 A Walk Through the Forest dijkstra + DFS
  10. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能
  11. HTML5获取地理位置定位信息
  12. python 生成器装饰器_python: 生成器,装饰器以及列表推导式写法
  13. 内存超频时序怎么调_电脑内存条专业科普,内存选购、内存品牌、内存安装、内存时序体质、内存超频频率详细讲解...
  14. 【Spring源码】AOP切面源码
  15. 广东哪个服务器稳定,稳定服务器地址广东
  16. 数据库表关联关系的基础知识
  17. Linux下MySQL起动报错The server quit without updating PID file
  18. 公司 MyEclipse设置和SVN安装设置
  19. 网络出版服务许可证 你了解吗?
  20. matlab学习笔记 struct函数

热门文章

  1. JAVA偏向锁的什么时候释放_Java中的偏向锁
  2. WaitForMultipleObjects用法详解
  3. linux下通过inotify-tools监控文件系统
  4. Theano - Numpy 新手
  5. Maven 使用代理下载依赖
  6. cocos2dx spine之一 :spine缓存 (c++ lua)
  7. C#(WinForm)实现软件注册
  8. KVM基础功能——Cpu配置
  9. 第五章 Spring进阶-注解方式实现AOP(1)
  10. Java线程状态及 wait、sleep、join、interrupt、yield等的区别