学习笔记 -- 相机开发 -- 实时帧数据处理

博客 : 相机开发 -- 实时帧数据处理

UI设计
  # 方案:xml中添加两个按钮, 控制相机预览的开始和停止。 在代码中绑定对应的事件

  # 方法编写
      public void startPreview() {
          /*初始化相机预览
           * 1. 预览控件布局载入
           * 2. 相机获取、初始化设置信息
           * 3. 控件监听事件绑定 --  设置菜单
           */

          /*预览控件载入*/
        final CameraPreview mPreview = new CameraPreview(this);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);

        /*相机获取 && 初始化设置*/
        SettingsFragment.passCamera(mPreview.getCameraInstance());
        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
        SettingsFragment.setDefault(PreferenceManager.getDefaultSharedPreferences(this));
        SettingsFragment.init(PreferenceManager.getDefaultSharedPreferences(this));

        /*绑定监听事件 -- 设置菜单*/
          Button buttonSettings = (Button) findViewById(R.id.button_settings);
        buttonSettings.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager().beginTransaction().replace(R.id.camera_preview, new SettingsFragment()).addToBackStack(null).commit();
            }
        });
    }

    public void stopPreview() {
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.removeAllViews();
    }
  
  # 按钮绑定 [in onCreate()]
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button buttonStartPreview = (Button) findViewById(R.id.button_start_preview);
        buttonStartPreview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startPreview();
            }
        });

        Button buttonStopPreview = (Button) findViewById(R.id.button_stop_preview);
        buttonStopPreview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopPreview();
            }
        });
    }

基本的帧数据获取和处理
  # 概述:接口Camera.PreviewCallback 下的onPreviewFrame(byte[] data,Camera camera) 可获取帧数据 
  # 方案:为CameraPreview类增加Camera.PreviewCallback接口声明 并且实现对应方法 最后绑定接口

  # 接口声明:public class CameraPreview extends SurfaceView 
                  implements SurfaceHolder.Callback, Camera.PreviewCallback

  # 方法实现:
      public void onPreviewFrame(byte[] data, Camera camera) {
        
        Log.i(TAG, "processing frame");
        
        /*
         * 暂停0.5秒模拟帧数据处理
         */
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
      
  # 接口绑定:在surfaceCreated()中getCameraInstance()代码前绑定
    mCamera.setPreviewCallback(this);
    // 效果 : 每当有预览帧生成则调用onPreviewFrame()方法


UI线程分离 -- 防止UI线程的阻塞
  # 方案:借助HandlerThread实现。创建新线程,拥有自己的loop。
         通过Handler.post()确保在新线程执行指定语句

  # 线程设置 [in CameraPreview]
          private class CameraHandlerThread extends HandlerThread {
            
            /* 线程任务 : 执行 mCamer = Camera.open()
             *            确保通过handler.post()在Runnable中执行
             */

            Handler mHandler;

            // 构造:线程启动 获取mHnadler
            public CameraHandlerThread(String name) {
                super(name);
                start();//线程启动 并 创建一个 handler
                mHandler = new Handler(getLooper());
            }

            //相机确认
            synchronized void notifyCameraOpened() {
                notify();
            }

            /* 相机开启
             */
            void openCamera() {

                /* 安全设置 --- notify-wait控制 [确认相机打开后openCamare()才返回]
                 * post()执行会立即返回 BUT Runnable会异步执行
                 * SO 可能执行post()后立即使用mCamera时仍为null
                 */

                mHandler.post(new Runnable() { // UI更新
                    @Override
                    public void run() {
                        openCameraOriginal(); // 封装相机开启功能
                        notifyCameraOpened(); // 相机确认
                    }
                });

                try {
                    wait();
                   } catch (InterruptedException e) {
                    Log.w(TAG, "wait was interrupted");
                }
            }        
        }
  
  # 方法封装 [in CameraPreview ] 
          private void openCameraOriginal() {
             try {
              mCamera = Camera.open();
            } catch (Exception e) {
              Log.d(TAG, "camera is not available");
            }
        }
  
  # 线程添加
          public Camera getCameraInstance() {
            if (mCamera == null) {
                
                CameraHandlerThread mThread = new CameraHandlerThread("camera thread");
                
                synchronized (mThread) {
                    mThread.openCamera();
                }
            }
            return mCamera;
        }

帧数据处理
  # 方案:采用HandlerThread [利用Android的Message Queue异步处理帧数据]
           - onPreviewFrame()调用时将帧数据封装为Message, 发送给HandlerThread
           - HandlerThread在新线程中获取Message,对帧数据进行处理
             发送Meaasge时间短,不会造成帧数据丢失问题。保证最后效果的实时性
  
  # 新建ProcessWithHandlerThread类
      /* 
       * 新建线程类:父类 - HandlerThread  接口 - Handler.Callback 
       */
      public class ProcessWithHandlerThread extends HandlerThread implements Handler.Callback {
        
        private static final String TAG = "HandlerThread";
        public static final int WHAT_PROCESS_FRAME = 1;

        /* 构造 - 线程启动 */
        public ProcessWithHandlerThread(String name) {
            super(name);
            start();
        }

        /* 信息处理 */
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case WHAT_PROCESS_FRAME:
                    byte[] frameData = (byte[]) msg.obj;//数据类型转换
                    processFrame(frameData);//数据处理封装
                    return true;
                default:
                    return false;
            }
        }

        /* 帧数据处理 */
        private void processFrame(byte[] frameData) {
            Log.i(TAG, "test");
        }
    }

  # 线程实例化 [in CameraPreview]
    /* 线程实例化
     * 绑定接口
     * 帧数据封装
     * 信息发送
     */

     /*成员变量添加*/
    private static final int PROCESS_WITH_HANDLER_THREAD = 1;
    private int processType = PROCESS_WITH_HANDLER_THREAD;
    private ProcessWithHandlerThread processFrameHandlerThread;
    private Handler processFrameHandler;

    /*构造函数修改 - 末尾添加*/
    switch (processType) {
        case PROCESS_WITH_HANDLER_THREAD:
                /* 线程实例化 */
            processFrameHandlerThread = new ProcessWithHandlerThread("process frame");
            processFrameHandler = new Handler(processFrameHandlerThread.getLooper(), processFrameHandlerThread);//接口绑定
            break;
    }

    /*onPreviewFrame()修改*/
    public void onPreviewFrame(byte[] data, Camera camera) {
        switch (processType) {
            case PROCESS_WITH_HANDLER_THREAD:
                processFrameHandler.obtainMessage(ProcessWithHandlerThread.WHAT_PROCESS_FRAME, data).sendToTarget();//数据封装 && 消息传递
                break;
        }
    }

完整代码 : https://github.com/zhantong/AndroidCamera-ProcessFrames

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值