当前位置: 首页 > news >正文

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);}}}}
}

运行效果

 

相关文章:

  • 【UR #1】外星人(dp思维技巧)
  • Java项目: 基于SpringBoot+mybatis+maven+vue图书进销存管理系统分前后台(含源码+数据库+毕业论文)
  • Linux开机logo设置
  • Unity中分辨率适配
  • AMEYA360:村田电子更适合薄型设计应用场景的3.3V输入、12A输出的DCDC转换IC
  • 如何在 UniApp 中实现地图的视野自适应?
  • HarmonyOS---权限和http/Axios网络请求
  • 在视频上绘制区域:使用Vue和JavaScript实现交互式画布
  • SSH 远程连接到 Linux 服务器上的 SQLite
  • python AutoGen接入开源模型xLAM-7b-fc-r,测试function calling的功能
  • 利用香港多IP服务器建站蜘蛛池执行SEO策略的实践
  • Python注释
  • Redis中String命令的基础操作
  • Chroma 向量数据入门
  • 强化学习在自动驾驶技术中的应用与挑战
  • 【技术性】Search知识
  • docker-consul
  • es6--symbol
  • js中forEach回调同异步问题
  • Linux快速复制或删除大量小文件
  • php中curl和soap方式请求服务超时问题
  • Python socket服务器端、客户端传送信息
  • Spark学习笔记之相关记录
  • SQLServer插入数据
  • Travix是如何部署应用程序到Kubernetes上的
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 彻底搞懂浏览器Event-loop
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 前端攻城师
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 算法---两个栈实现一个队列
  • 用quicker-worker.js轻松跑一个大数据遍历
  • ​​​​​​​​​​​​​​Γ函数
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • #Lua:Lua调用C++生成的DLL库
  • (2)leetcode 234.回文链表 141.环形链表
  • (C++哈希表01)
  • (Charles)如何抓取手机http的报文
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (含笔试题)深度解析数据在内存中的存储
  • (函数)颠倒字符串顺序(C语言)
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (十六)Flask之蓝图
  • (十三)Maven插件解析运行机制
  • (新)网络工程师考点串讲与真题详解
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)Google的Objective-C编码规范
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)我也是一只IT小小鸟
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET CORE Aws S3 使用
  • .Net 垃圾回收机制原理(二)
  • .NET 中创建支持集合初始化器的类型
  • .NET 中什么样的类是可使用 await 异步等待的?