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

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() {@Overridepublic void onClick(View v) {externalCameraSample.open();externalCameraSample.start(new ExternalCameraSample.Callback() {@Overridepublic 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;@Overridepublic 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) {//实例,返回数组bytekeyjbyteArray  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~360int 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.mzph.cn/news/880421.shtml

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

相关文章

AI数字人直播爆火,数字人虚拟主播成品牌闲时直播最佳选择!

近年来&#xff0c;随着互联网的普及和发展&#xff0c;电商和直播平台在我国迅速崛起。根据中国网络信息中心的数据显示&#xff0c;我国直播用户7.5亿&#xff0c;使用率已经超过70%&#xff0c;直播已经成为企业重要的营销和销售通道。 一、在经历了几年的爆发式增长后&…

日期类的实现- 计算日期之间相差多少天-解决单参数构造

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 这里有坑&#xff0c;这里有坑&#xff0c;这里有坑 首选我们直接上代码&#xff0c;因…

Unity场景内画车道线(根据五阶曲线系数)

之前做过使用Dreamteck Splines插件构建车道线之前需求是给定车道线的点位&#xff0c;根据点位来进行构建。 由于AI识别出来的点位不线性&#xff0c;画出来的车道线经常是歪七扭八&#xff0c;所以使用五阶曲线系数进行构建。 使用在线图形计算器进行测试构建&#xff0c;公式…

【C++】STL标准模板库容器set

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;关联式容器set(集合)简介 &#x1f4cc;set(集合)的使用 &#x1f38f;set(集合)的模板参数列表 &#x1f38f;set(集合)的构造函数 &#x1f38f;set(集合)的迭代…

Vue 项目实战4-无缝轮播图

养成好习惯&#xff0c;先赞后看&#xff0c;感谢对作者大大的支持 一、话不多说&#xff0c;直接上效果图&#xff1a; 完整视频展示链接如下&#xff1a; https://item.taobao.com/item.htm?ftt&id833405684191 二、实现思路 HTML结构 文档头部设置&#xff1a;定义…

C# 委托(Delegate)二

一.委托的多播&#xff08;Multicasting of a Delegate&#xff09;&#xff1a; 委托对象&#xff0c;使用 "" 运算符进行合并&#xff0c;一个合并委托调用它所合并的两个委托。使用"-" 运算符从合并的委托中移除组件委托。 注&#xff1a;只有相同类型…

C语言编译和链接详解(通俗易懂,深入本质)

我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。在 Windows 下,可执行程序的后缀有.exe和.com(其中.exe比较常见);在类 UNIX 系统(Linux、Mac OS 等)下,可执行程序没有特定的后缀,系统根据文件的头部信息来判…

小小扑克牌算法

1.定义一个扑克牌类Card&#xff1a; package democard; public class Card {public String suit;//表示花色public int rank;//表示牌点数Overridepublic String toString() {return "{"suit rank"}";}//实例方法&#xff0c;初始化牌的点数和花色public…

Redis篇(初识Redis)

目录 一、数据库 二、NoSQL 三、认识Redis 三、关系数据库与非关系数据库对比 1. 结构化与非结构化 2. 关联和非关联 3. 查询方式 4. 事务 5. 存储方式 6. 扩展性 7. 总结 7.1. 图形梳理 7.2. 表格梳理 7.3. 优缺点 关系型数据库 非关系型数据库 四、再次认识R…

word中的表格全部设置宽度100%

1、背景 我们用工具将数据库或其他的数据导出成word时&#xff0c;表格有的会大于100%&#xff0c;超过了边界。word没有提供全局修改的方法。如果我们想改成100%。 一种方式是通过宏&#xff0c;全局改。一种是手动改。 2、宏修改 如果表格多&#xff0c;可以通过这种方式。…

【Qt】前后端交互---DataCenter类

设计目的 前后端交互系统中&#xff0c;创建并使用数据核心类的目的就是让该类作为客户端的数据中心&#xff0c;也就是说其负责管理客户端的所有数据与服务器的网络通信。 数据持久化 初始化数据文件 该函数设计的目的就是用于检查所需要的文件和目录是否存在&#xff0c;如…

You are not allowed to push code to this project

原因1 用户权限不够。 具体查看用户权限路径&#xff1a; 原因2 vscode之前都能提交代码&#xff0c;但是突然就提交不上了。 表现为:前端代码能拉取&#xff0c;但是不能提交。使用idea进行前端代码的提交&#xff0c;完全没问题。 解决方案&#xff1a;修改TortoiseG…

新160个crackme - 066-Andrnalin.3

运行分析 文字是德语&#xff0c;需要破解Key PE分析 VB程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 使用 VB Decompiler 静态分析&#xff0c;发现逻辑如下&#xff1a;1、提取Key每个字符 102、计算后的Key要与"kXy^rO|yXom\kMuOn*"相等 算法分析…

25 基于51单片机的温度电流电压检测系统(压力、电压、温度、电流、LCD1602)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;通过DS18B20检测温度&#xff0c;滑动变阻器连接数模转换器模拟电流、电压&#xff0c;通过LCD1602显示&#xff0c;程序里设置温度阈值为40&#xff0c;电流阈值为60&am…

[大语言模型-论文精读] 以《黑神话:悟空》为研究案例探讨VLMs能否玩动作角色扮演游戏?

1. 论文简介 论文《Can VLMs Play Action Role-Playing Games? Take Black Myth Wukong as a Study Case》是阿里巴巴集团的Peng Chen、Pi Bu、Jun Song和Yuan Gao&#xff0c;在2024.09.19提交到arXiv上的研究论文。 论文: https://arxiv.org/abs/2409.12889代码和数据: h…

openKylin--安装 .net6.0

编辑profile文件 cd .. //切换到根目录 cd /etc //切换到etc目录 vim profile //b编辑profile文件 1. 按→键移动到文件末尾 2. 按Insert键进入编辑模式 3. 按Enter另起一行开始编辑 export DOTNET_ROOT/home/dotnetexport PATH$PATH:/home/dotnet 可以通过右键--粘贴 的…

使用Crawler实例进行网页内容抓取

网页内容抓取的背景 随着互联网的快速发展&#xff0c;网页上的信息量日益庞大。如何从海量的网页中快速、准确地抓取所需信息&#xff0c;成为了一个技术挑战。网页内容抓取技术通过自动化的方式&#xff0c;模拟用户浏览网页的过程&#xff0c;获取网页上的文本、图片、链接…

通信工程学习:什么是FDD频分双工

FDD:频分双工 FDD(频分双工,Frequency Division Duplexing)是一种无线通信技术,它通过将频谱划分为上行和下行两个不重叠的频段来实现同时双向通信。以下是FDD频分双工的详细解释: 一、定义与原理 定义: FDD是一种无线通信系统的工作模式,其中上行链路(从移动…

开源链动 2+1 模式 S2B2C 商城小程序:激活 KOC,开启商业新征程

摘要&#xff1a;本文深入探讨了 KOC 在立体连接中的重要性&#xff0c;以及如何通过开源链动 21 模式 S2B2C 商城小程序发现和找到更多的 KOC。强调了历史积累强关系和快速强化强关系的方法&#xff0c;并阐述了该商城小程序在推动商业发展中的关键作用。 一、引言 在当今竞争…

webpack4 target:“electron-renderer“ 打包加速配置

背景 昨天写得一篇Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题-CSDN博客文章浏览阅读754次&#xff0c;点赞19次&#xff0c;收藏11次。因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vu…