EasyAR自定义相机RTSP视频流(CustomCamera)

news2024/9/27 21:24:49

EasyAR可以使用视频源作为输入源,官方给出了示例和文档,但是对于大部分Unity开发人员来说看了文档还是一头雾水。

在Android Studio中将custom-camera.jar添加libs中,就可以查看源代码了

分析其源代码,主要是ExternalCameraSample类中的open函数和Start函数。

open即找开相机或视频流,start(callback)主要用于取图像帧,当有新的Frame时,调用callback,将最新的帧数据传入一个ByteArrayWrapper的结构中,在Unity中再将ByteArrayWrapper转换为InputFrame,即可进行识中坚力量。

用java模拟调用端的代码如下

 Button btnPlay = findViewById(R.id.btnPlay);
        btnPlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                externalCameraSample.open();
                externalCameraSample.start(new ExternalCameraSample.Callback() {
                    @Override
                    public void onPreviewFrame(ExternalCameraSample.ByteArrayWrapper var1) {
                        String str = String.valueOf(var1.BufferLength);
                        Log.d("easyar", str);
                    }
                });
            }
        });

修改ExternalCameraSample中open和start,重点是ByteArrayWrapper赋值

以下修改后的Java代码(C++代码略,需要自己写)

public class ExternalCameraSample {
    private  NativeLib nativeLib;
    private ExternalCameraParameters mCameraParameters;
    private boolean suc = false;
    public ExternalCameraSample() {
        nativeLib = new NativeLib();
    }

    public boolean open() {
        suc = nativeLib.StartPlay(0, "rtsp://admin:admin@192.168.43.110:554/stream1");
        return suc;
    }

    private float getRatioError(float x, float x0) {
        float a = x / Math.max(x0, 1.0F) - 1.0F;
        float b = x0 / Math.max(x, 1.0F) - 1.0F;
        return a * a + b * b;
    }


    private boolean ready() {
        return suc;
    }

    public boolean start(final ExternalCameraSample.Callback callback) {
        if (!this.ready()) {
            return false;
        } else {
            //刷新刷数据
            if(suc)
            {
                Timer timer = new Timer();
                timer.schedule(new TimerTask(){
                    public int flag=1;
                    @Override
                    public void run() {
                        nativeLib.native_updateFrame(0);

                        if(mCameraParameters == null)
                        {
                            mCameraParameters = new ExternalCameraParameters();
                            mCameraParameters.setCameraType(1);
                        }
                        mCameraParameters.setWidth(nativeLib.native_getWidth(0));
                        mCameraParameters.setHeight(nativeLib.native_getHeight(0));
                        mCameraParameters.setTimestamp(SystemClock.elapsedRealtimeNanos());

                        ExternalCameraSample.ByteArrayWrapper wrapper = new ExternalCameraSample.ByteArrayWrapper();
                        wrapper.Buffer = (byte[])nativeLib.native_getFrameData(0);
                        wrapper.BufferLength = nativeLib.native_getBytesLength(0);
                        wrapper.camParams = ExternalCameraSample.this.mCameraParameters;
                        callback.onPreviewFrame(wrapper);
                    }
                }, 1, 1);
            }
            return true;
        }
    }

    public boolean stop() {
        if (!this.ready()) {
            return true;
        } else {
            nativeLib.native_stopPlay(0);
            return true;
        }
    }

    public ExternalCameraParameters getCameraParameters() {
        return this.mCameraParameters;
    }

    public int getPixelFormat() {
        return 2;
    }

    public interface Callback {
        void onPreviewFrame(ExternalCameraSample.ByteArrayWrapper var1);
    }

    public static class ByteArrayWrapper {
        public byte[] Buffer;
        public int BufferLength;
        public ExternalCameraParameters camParams;

        public ByteArrayWrapper() {
        }
    }
}

jni从C++给java返回数组

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_nativelib_NativeLib_native_1getFrameData(JNIEnv *env, jobject thiz, jint index) {
    //实例,返回数组bytekey
    jbyteArray  jarrRV =env->NewByteArray(player[index].m_numBytes);
    env->SetByteArrayRegion(jarrRV, 0,player[index].m_numBytes,(jbyte*)player[index].m_imgData);

    return jarrRV;
}

Unity代码(在示例上做了少量修改)

//================================================================================================================================
//
//  Copyright (c) 2015-2023 VisionStar Information Technology (Shanghai) Co., Ltd. All Rights Reserved.
//  EasyAR is the registered trademark or trademark of VisionStar Information Technology (Shanghai) Co., Ltd in China
//  and other countries for the augmented reality technology developed by VisionStar Information Technology (Shanghai) Co., Ltd.
//
//================================================================================================================================

#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
using AOT;
using System.Runtime.InteropServices;
#endif
using System;
using UnityEngine;
using UnityEngine.UI;

namespace easyar
{
    public class CustomCameraSource : FrameSource
    {
        private bool willOpen = false;

        public override Optional<InputFrameSourceType> Type { get => InputFrameSourceType.General; }

        public override Optional<bool> IsAvailable { get => Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer; }

        protected override void OnEnable()
        {
            base.OnEnable();
            if (externalCamera != null)
                externalCamera.Call<bool>("start", cameraCallback);
        }

        protected override void OnDisable()
        {
            base.OnDisable();
            if (externalCamera != null)
                externalCamera.Call<bool>("stop");
        }

        protected virtual void OnDestroy()
        {
            Close();
        }

        public override void OnAssemble(ARSession session)
        {
            base.OnAssemble(session);
            Open();
        }

        public void Open()
        {
            if (Application.platform != RuntimePlatform.Android && Application.platform != RuntimePlatform.IPhonePlayer)
            {
                throw new UIPopupException(typeof(CustomCameraSource) + " not available under " + Application.platform);
            }
            willOpen = true;
            CameraDevice.requestPermissions(EasyARController.Scheduler, (Action<PermissionStatus, string>)((status, msg) =>
            {
                if (!willOpen)
                {
                    return;
                }
                 externalCamera = new AndroidJavaObject("com.example.nativelib.ExternalCameraSample");
                 externalCamera.Call<bool>("open");

                 cameraCallback = new CameraCallback(dataWrapper =>
                 {
                     if (sink == null)
                     {
                         return;
                     }

                     using (var param = dataWrapper.Get<AndroidJavaObject>("camParams"))
                     {
                         var byteArray = dataWrapper.Get<AndroidJavaObject>("Buffer");
                         var jniByteArray = byteArray.GetRawObject();

                         var buffer = JniUtility.wrapByteArray(jniByteArray, true, () => { byteArray.Dispose(); });
                         var format = PixelFormat.RGBA8888;//色彩格式
                         int orientation = 90;//旋转角度0~360
                         int cameraType = 1;//1为后摄像头,2为前摄像头
                         double timestamp = param.Call<long>("getTimestamp") * 1e-9;
                         var imageWidth = param.Call<int>("getWidth");
                         var imageHeight = param.Call<int>("getHeight");
                         var imageSize = new Vector2(imageWidth, imageHeight);
                         HandleSink(buffer, format, imageSize, orientation, cameraType, timestamp);
                     }
                 });
                if (enabled)
                {
                    OnEnable();
                }
            }));
        }

        public void Close()
        {
            willOpen = false;
            OnDisable();
            if (externalCamera != null)
                externalCamera.Dispose();
        }

        private void HandleSink(Buffer imageBuffer, PixelFormat format, Vector2 imageSize, int orientation, int cameraType, double timestamp)
        {
            using (var cameraParams = CameraParameters.createWithDefaultIntrinsics(new Vec2I((int)imageSize.x, (int)imageSize.y), (CameraDeviceType)cameraType, orientation))
            using (var image = new Image(imageBuffer, format, (int)imageSize.x, (int)imageSize.y))
            using (var frame = InputFrame.createWithImageAndCameraParametersAndTemporal(image, cameraParams, timestamp))
            {
                if (sink != null)
                    sink.handle(frame);
            }
            imageBuffer.Dispose();
        }

        private AndroidJavaObject externalCamera;
        private CameraCallback cameraCallback;

        private class CameraCallback : AndroidJavaProxy
        {
            private Action<AndroidJavaObject> onPreviewFrameCallback;

            public CameraCallback(Action<AndroidJavaObject> onPreviewFrameCallback) : base("com.example.nativelib.ExternalCameraSample$Callback")
            {
                this.onPreviewFrameCallback = onPreviewFrameCallback;
            }

            public void onPreviewFrame(AndroidJavaObject dataWrapper)
            {
                // NOTE: Workaround callback parameter not disposed in some Unity versions like 2022.2.
                //       This looks like a bug in Unity because usually the caller is responsible for disposing the callback parameter.
                //       And the behavior change is not compatible which will cause serious memory leak.
                using (dataWrapper) // workaround
                {
                    onPreviewFrameCallback(dataWrapper);
                }
            }
        }
    }
}

运行效果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【linux 多进程并发】linux下使用常见命令,来解析进程家族体系脉络

0101 Linux进程 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 0101 Li…

ASP.NET Core 打包net8.0框架在Linux CentOS7上部署问题

问题1 libstdc.so.6版本过低。 CentOS7默认安装的gcc版本太低&#xff0c;达不到.net8的启动条件。 /lib64/libstdc.so.6: version GLIBCXX_3.4.20’ not found (required by ./IDT_net) /lib64/libstdc.so.6: version GLIBCXX_3.4.21’ not found (required by ./IDT_net) 解…

恢复丢失的数据:恢复数据库网络解决方案

探索恢复数据库网络的深度对于了解现代企业如何防御其数据不断增长的威胁至关重要。在一个时代&#xff0c;数字证据和取证网络安全在法律和商业领域扮演关键角色&#xff0c;这些网络提供的弹性是不可或缺的。深入研究恢复数据库网络的重要性不仅仅是数据保护&#xff0c;它还…

ubuntu安装mysql 8,mysql密码的修改

目录 1.安装mysql 82.查看当前状态3.手动给数据库设置密码mysql5mysql8 4.直接把数据库验证密码的功能关闭掉 1.安装mysql 8 apt install mysql-server-8.0敲 Y 按回车 table 选ok 2.查看当前状态 service mysql status显示active&#xff08;running&#xff09;证明安装成…

媒界:吉利星瑞百炼成钢,持续引领中国汽车价值向上

秋风送爽绘秋色&#xff0c;出行良辰恰逢时。9月28日至9月29日&#xff0c;2024安行中国汽车安全科技公益巡展迎来尾声&#xff0c;安行中国携手吉利汽车&#xff0c;步履轻盈地踏入苏州星湖天街&#xff0c;共同呈献一场融合环保科技前沿、安全驾驶理念与深厚文化底蕴的48小时…

使用jQuery处理Ajax

使用jQuery处理Ajax HTTP协议 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法 所有的WWW文件都必须遵守这个标准 一次HTTP操作称为一个事务&am…

如何使用 CCF Communicator 框架快速开发设备接口

什么是 CCF Communicator Framework&#xff1f; 通信器框架通过封装 CCF 和设备之间的连接&#xff0c;简化了硬件之间的低级消息处理。 举例来说&#xff0c;考虑一下控制软件和硬件设备之间的连接方式。ASCII 串行连接需要使用 TCP 的套接字连接、用于处理设备发送/接收的…

肺癌类器官培养研究概述

前 言 2023年是类器官被《Science》杂志评为年度十大技术的10周年。10年后类器官技术发展迅猛&#xff0c;犹如一颗璀璨的明珠&#xff0c;不断的为生命科学研究揭示新的奥秘&#xff0c;推动生物医学领域不断前行。肺类器官培养条件也在不断完善&#xff0c;在基础和临床研究…

MySQL面试知识汇总

学习链接 创建索引有哪些注意点&#xff1f; 索引应该建在查询频繁的字段&#xff0c;比如where查询、order排序索引的个数应该适量&#xff08;最多64个&#xff09;&#xff0c;索引需要占用空间&#xff0c;更新时也需要维护区分度低的字段&#xff0c;例如性别&#xff0c…

声阔头戴式耳机怎么样?西圣、jBL、声阔头戴式耳机终极pk测评推荐

我们深知&#xff0c;一款优秀的头戴式耳机&#xff0c;不仅仅是音乐的传递者&#xff0c;更是用户情感与个性的延伸。因此&#xff0c;在设计之初&#xff0c;便将极致的佩戴舒适度视为核心追求&#xff0c;通过人体工学的精准设计与优质材料的精心挑选&#xff0c;力求让每一…

Linux 配置与管理 SWAP(虚拟内存)

Linux 配置与管理 SWAP(虚拟内存&#xff09; 一、作用二、创建交换文件&#xff08;以创建一个2GB的交换文件为例&#xff09;1. 创建交换文件2. 设置文件权限2.1. **关于 sudo chmod 600 /root/swapfile 是否一定要执行**2.2. **关于其他用户启动是否没权限用到交换分区** 3.…

大数据电商数仓项目--实战(一)数据准备

第一章 数仓分层 1.1 为什么要分层 1.2 数仓命名规范 1.2.1 表命名 ODS层命名为ods_表名DIM层命名为dim_表名DWD层命名为dwd_表名DWS层命名为dws_表名DWT层命名为dwt_表名ADS层命名为ads_表名临时表命名为tmp_表名 1.2.2 表字段类型 数量类型为bigint金额类型为decimal(16…

猫咪独自在家可以吗?希喂、美的、有哈宠物空气净化器哪款好?

这不是快要国庆了吗&#xff0c;本来计划去旅游的&#xff0c;结果我妈让我假期回家。收拾行李已经很烦了&#xff0c;行李箱旁的猫咪更是让我头疼。我妈因为之前浮毛过敏的事情&#xff0c;禁止我把猫咪再带回家&#xff0c;朋友们也各有计划&#xff0c;甚至连上门喂养都约满…

设备管理与点巡检系统

在现代企业管理中&#xff0c;设备的高效运作至关重要。为此&#xff0c;我们推出了设备管理与点巡检系统&#xff0c;通过自动化管理提升设备使用效率&#xff0c;保障生产安全。 系统特点 设备全生命周期管理 系统涵盖设备的各个阶段&#xff0c;从设备管理、点检、巡检、保…

计算曲线5s1-2的斜率

在行列可自由变换的条件下&#xff0c;平面上的5点结构只有34个 这次将5点结构通过结构加法化成2点结构5s1-4-3-2&#xff0c;并比较5s1-4-3-2的变化规律。 (A,B)---6*n*2---(0,1)(1,0) 分类A和B&#xff0c;A是34个5点结构&#xff0c;让B全是0。当收敛误差为7e-4&#xff0…

Netty源码解析-响应式实现(Reactor模式)

Netty基本介绍&#xff0c;参考 Netty与网络编程 1、Netty如何支持Reactor模式 1.1 主从Reactor模式 实现这种模式需要定义两个EventLoopGroup&#xff0c;bossGroup就是mainReactor&#xff0c; workerGroup就是subReactor&#xff0c; 接着我们进入下图的b.group方法 1.…

Tomcat部署及其优化

目录 一、Tomcat概述 二、Tomcat的组成 三、Tomcat请求过程 四、Tomcat服务部署 五、/usr/local/tomcat/目录下的主要目录说明 六、Tomcat虚拟主机配置 七、Tomcat优化 1.Tomcat配置文件参数优化 2.Tomcat JVM优化 一、Tomcat概述 Tomcat是基于java语言开发&#xff0c…

传知代码-轻量注意力网络实现苹果叶片识别

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 引言 该系统基于EfficientNet与多头自注意力机制&#xff0c;构建了一个高效、精准的苹果叶片识别模型&#xff0c;能够对不同种类的苹果叶片进行准确分类。通过结合EfficientNet的强大特征提取能力和多头注意力…

Ks渲染做汽车动画吗?汽车本地渲染与云渲染成本分析

Keyshot是一款强大的实时光线追踪和全域光渲染软件&#xff0c;它确实可以用于制作汽车动画&#xff0c;包括汽车模型的渲染和动画展示。Keyshot的动画功能允许用户创建相机移动、物体变化等动态效果&#xff0c;非常适合用于汽车动画的制作。 至于汽车动画的渲染成本&#xff…

Power Platform开发小技巧,一天一个APP, 如何快速搭建二维码识别器

之前&#xff0c;给大家分享了微软Power Platform开发课程——手把手教你搭建二维码生成器&#xff0c;很多小伙伴反馈真好用。这期我们继续为大家分享Power Platform的开发能力与技巧。 今天介绍如何开发⼀个⼆维码识别器。 该应用包含如下功能&#xff1a; 1.⼆维码图片的…