音视频之进程间传递 YUV 格式视频流,解决不能同时调用 Camera 问题

栏目: IOS · Android · 发布时间: 6年前

内容简介:由于项目需求,需要在执法仪本地录像的时候,执法软件能正常的使用设备本身的 Camera 资源。由于 Android 系统自身不允许多个软件同时使用 Camera 资源,故开发一套

由于项目需求,需要在执法仪本地录像的时候,执法软件能正常的使用设备本身的 Camera 资源。由于 Android 系统自身不允许多个软件同时使用 Camera 资源,故开发一套 内存共享 子码流传输协议,当执法软件需要视频流的时候,向执法仪设备请求往 MemoryFile 中写入 YUV 格式的视频流,执法软件每隔一段时间循环的去指定的内存中 read YUV 视频流。

完整代码入口

流程图

音视频之进程间传递 YUV 格式视频流,解决不能同时调用 Camera 问题

效果图

  • 本地相机未打开

    音视频之进程间传递 YUV 格式视频流,解决不能同时调用 Camera 问题
  • 本地相机打开

    音视频之进程间传递 YUV 格式视频流,解决不能同时调用 Camera 问题

简要流程及示例代码说明

  1. 执法仪服务端收到客户端的开启视频流写入内存的指令

    //处理客服端发送过来的需要子码流的数据
        private void onHandleAction(Context context, Intent intent) {
            switch (intent.getAction()) {
                /**
                 * 需要子码流
                 */
                case Constants.ACTION_CAMERE_CORE_SHOW:
                    //如果正在发送视频流,就不需要执行后面代码了
                    if (!MemoryFileServiceManager.getInsta(context).isSendVideoFrame())
                        MemoryFileServiceManager.getInsta(context).setSendVideoFrame(true, intent);
                    break;
            }
        }
    复制代码
  2. 服务端获取开启视频的条件

    private void sendVideoFrame(Intent intent) {
            if (intent != null && intent.getExtras() != null) {
                Bundle extras = intent.getExtras();
                //获取需要预览的宽
                Constants.PREVIEWHEIGHT = extras.getInt(Constants.Config.PREVIEW_WIDTH, 1280);
                //获取需要预览的高
                Constants.PREVIEWHEIGHT = extras.getInt(Constants.Config.PREVIEW_HEIGHT, 720);
                //需要绑定对方服务的进程
                Constants.BIND_OTHER_SERVICE_PCK = extras.getString(Constants.Config.BIND_OTHER_SERVICE_PCK, "");
                //需要绑定对方服务的全路径
                Constants.BIND_OTHER_SERVICE_CLASS = extras.getString(Constants.Config.BIND_OTHER_SERVICE_CLASS, "");
                //需要开启 Camera ID 的前置还是后置 0:后置 1:前置
                Constants.CAMERA_ID = extras.getInt(Constants.Config.CAMERA_ID, 0);
            }
        }
    复制代码
  3. 服务器是否开启相机,如果已经开启则不需要开启

    //是否摄像头
            if (mCamera == null)
                openCamera();
    复制代码
  4. 服务端初始化一块内存,用于写入 YUV 视频流。

    mMemoryFile = initMemoryFile(Constants.MEMORY_FILE_NAME, Constants.MEMORY_SIZE);
    复制代码
  5. 绑定对方服务,提供文件描述符号

    /**
         * 绑定对方服务,提供 文件描述符
         */
        private void bindOtherService() {
            try {
                if (TextUtils.isEmpty(Constants.BIND_OTHER_SERVICE_PCK) || TextUtils.isEmpty(Constants.BIND_OTHER_SERVICE_CLASS))
                    throw new NullPointerException("PCK or CLSS is null ?");
                Intent intent = new Intent();
                ComponentName cmp = new ComponentName(Constants.BIND_OTHER_SERVICE_PCK, Constants.BIND_OTHER_SERVICE_CLASS);
                intent.setComponent(cmp);
                context.bindService(intent, mCameraServiceConnection, Context.BIND_AUTO_CREATE);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }
    复制代码
  6. 绑定对方服务成功,交于文件描述符 ParcelFileDescriptor

    mCameraService = ICameraCoreService.Stub.asInterface(binder);
                if (mMemoryFile != null) {
                    try {
                        //反射拿到文件描述符号
                        mParcelFileDescriptor = MemoryFileHelper.getParcelFileDescriptor(mMemoryFile);
                        if (mParcelFileDescriptor != null) {
                            mCameraService.addExportMemoryFile(mParcelFileDescriptor, Constants.PREVIEWWIDTH, Constants.PREVIEWHEIGHT, Constants.MEMORY_SIZE);
    
                        }
    复制代码
  7. 发送数据,当标志位为 byte[0] == 0 代表服务端可将 YUV 写入内存, == 1 ,代表客服端可以读取可用的 YUV 数据。

    /**
         * 读标志位 写入视频流
         *
         * @param memoryFile
         */
        public void writeBytes(MemoryFile memoryFile) {
            try {
                if (mYUVQueue.size() > 0) {
                    BufferBean mBufferBean = new BufferBean(Constants.BUFFER_SIZE);
                    //读取标志符号
                    memoryFile.readBytes(mBufferBean.isCanRead, 0, 0, 1);
                    //当第一位为 0 的时候,代表客服端已经读取了,可以正常将视频流写入内存中
                    if (mBufferBean.isCanRead[0] == 0) {
                        //拿到视频流
                        byte[] video = mYUVQueue.poll();
                        if (video != null)
                            //将视频流写入内存中
                            memoryFile.writeBytes(video, 0, 0, video.length);
                        //标志位复位,等待客服端读取视频流
                        mBufferBean.isCanRead[0] = 1;
                        memoryFile.writeBytes(mBufferBean.isCanRead, 0, 0, 1);
                    } else {
                        Log.d(TAG, "readShareBufferMsg isCanRead:" + mBufferBean.isCanRead[0] + ";length:"
                                + mBufferBean.mBuffer.length);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                sendBroadcast(Constants.ACTION_FEEDBACK, e.getMessage());
            }
        }
    复制代码
  8. 开启成功或者失败等其他错误消息反馈给客服端

    //返回给客服端   
    public void sendBroadcast(String action,String content) {
            Intent intent = new Intent();
            intent.setAction(action);
            ComponentName componentName = new ComponentName("com.t01.sharevideostream",
                    "com.t01.sharevideostream.revices.FeedBackReceiver");
            intent.setComponent(componentName);
            Bundle extras = new Bundle();
            extras.putString(Constants.ACTION_FEEDBACK_CONTENT, content);
            intent.putExtras(extras);
            context.sendBroadcast(intent);
        }
    复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

趋势红利

趋势红利

刘润 / 文化发展出版社(原印刷工业出版社) / 2016-6-1 / 45.00

【编辑推荐】 1、国内顶尖的互联网转型专家,海尔、百度等知名企业战略顾问刘润送给传统企业的转型、创新“导航仪”,这个时代企业家的必修课 站在近200年商业全景图角度,刘润发现三种企业类型(产品型、渠道型、营销型),针对不同企业类型定制转型战略(找到自己的未来红利),方便 传统企业对号入座:不走错路就是节省时间,适合自己的最有效率。 本书内容还源自芬尼克兹、红领集团、名创优品、必要......一起来看看 《趋势红利》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具