C# 将字节数组引用从android java返回到csharp unity

C# 将字节数组引用从android java返回到csharp unity,c#,android,unity3d,C#,Android,Unity3d,字节[]字节=调用(“getBytes”);其中getBytes函数返回一个字节[] 调用上述函数以获取csharp中的图像rgb数据。返回的字节[]被深度复制到字节数组中 由于返回字节数组很大,深度复制会增加更多时间 如何使csharp中的字节数组只包含java字节[]的引用 public class TestUtil : MonoBehaviour { public static string TAG = "--------TestUtil------------> ";

字节[]字节=调用(“getBytes”);其中getBytes函数返回一个字节[]

调用上述函数以获取csharp中的图像rgb数据。返回的字节[]被深度复制到字节数组中

由于返回字节数组很大,深度复制会增加更多时间

如何使csharp中的字节数组只包含java字节[]的引用


public class TestUtil : MonoBehaviour
{

    public static string TAG = "--------TestUtil------------> ";

    private static AndroidJavaObject pluginClass;    
    public static List<byte[]> rgbList = new List<byte[]>();

    void Start()
    {


        Debug.Log(TAG + "start called");

        //mainDataArray = new byte[1280*720*4];
        Debug.Log(TAG + "creating java object");

        initializePlayer();

    }

    public void initializePlayer()
    {
        // StreamHandler is the Javaclass. here i am creating a object StreamHandler
        pluginClass = new AndroidJavaObject("com.test.android.decoder.StreamHandler");

       // using this code to get the context
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

        // setting context StreamHandler object
        pluginClass.Call("setActivity", activity);

        // setting the interface object, where java class will call the respective function
        pluginClass.Call("setOnDecodeListener", new AndroidPluginCallback());       

        // initializing the player
        pluginClass.Call("init", ipAddress, port, outWidth, outHeight);

        Debug.Log(TAG + " Initialization done");
    }

    public void quitApplication(string sid)
    {
        Application.Quit();
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.Escape)) {
            Debug.Log(TAG + "Escape");
            quitApplication(sId);
        }        
    }

    int count;
    private void LateUpdate()
    {



            if (0 != rgbList.Count) {
                // here i am updating the rgb texture to Quad gameobject
        }

    }

    class AndroidPluginCallback : AndroidJavaProxy
    {
        public AndroidPluginCallback() : base("com.test.android.OnDecodeListener") { }

        public void success(byte[] videoPath)
        {            
        }

        public void onFrameAvailable()
        {

            // Called when java code successfully generate RGBA data

            long startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();

            // Need to call "getBytes()" to get the RGBA frame. 
            //Note: generally if you call same function from another java class it do shallow copy to byte[] object 
            // but in this case it is doing deep copy or i am not sure whats going on.

            byte[] rawBytes = pluginClass.Call<byte[]>("getBytes"); // width and height of frame is 1088x1088

            rgbList.Add(rawBytes);

            long diff = DateTimeOffset.Now.ToUnixTimeMilliseconds() - startTime;
            Debug.Log(TAG + "Entered into onFrameAvailable callback. time taken to copy to rawbytes: " + diff); // diff is 14ms on average. Not sure why.
        }

        public void fail(string errorMessage)
        {
            Debug.Log(TAG + "ENTER callback onError: " + errorMessage);
        }

    }
}

公共类TestUtil:MonoBehavior
{
公共静态字符串标记=“-----------TestUtil----------------->”;
私有静态AndroidJavaObject插件类;
公共静态列表rgbList=新列表();
void Start()
{
Log(标记+“启动已调用”);
//mainDataArray=新字节[1280*720*4];
Log(标记+“创建java对象”);
初始化图层();
}
public void initializePlayer()
{
//StreamHandler是Javaclass。这里我创建一个对象StreamHandler
pluginClass=newandroidjavaobject(“com.test.android.decoder.StreamHandler”);
//使用此代码获取上下文
AndroidJavaClass unityPlayer=新的AndroidJavaClass(“com.unity3d.player.unityPlayer”);
AndroidJavaObject activity=unityPlayer.GetStatic(“currentActivity”);
//设置上下文StreamHandler对象
pluginClass.Call(“setActivity”,activity);
//设置接口对象,java类将在其中调用相应的函数
调用(“setOnDecodeListener”,新的AndroidPluginCallback());
//初始化播放器
pluginClass.Call(“init”,ipAddress,port,outWidth,outHeight);
Log(标记+“初始化完成”);
}
公共应用程序(字符串sid)
{
Application.Quit();
}
私有void更新()
{
if(Input.GetKey(KeyCode.Escape)){
Log(标记+“转义”);
应用程序(sId);
}        
}
整数计数;
私有更新()
{
如果(0!=rgbList.Count){
//在这里,我将rgb纹理更新为Quad Game Object
}
}
类AndroidPluginCallback:AndroidJavaProxy
{
public AndroidPluginCallback():base(“com.test.android.OnDecodeListener”){}
公共无效成功(字节[]视频路径)
{            
}
public void onFrameAvailable()
{
//当java代码成功生成RGBA数据时调用
long startTime=DateTimeOffset.Now.tounixtimemilures();
//需要调用“getBytes()”来获取RGBA帧。
//注意:通常,如果您从另一个java类调用相同的函数,它会对byte[]对象进行浅拷贝
//但在这种情况下,它正在进行深度复制,或者我不确定发生了什么。
byte[]rawBytes=pluginClass.Call(“getBytes”);//帧的宽度和高度为1088x1088
rgbList.Add(rawBytes);
long diff=DateTimeOffset.Now.tounixtimemilures()-startTime;
Debug.Log(标记+)输入到onFrameAvailable回调中。复制到rawbytes所需的时间:“+diff);//diff平均为14毫秒。不确定原因。
}
公共无效失败(字符串错误消息)
{
Log(标记+”输入回调onError:“+errorMessage”);
}
}
}

AndroidJavaObject
使用JNI(Java本机接口)将数据封送至Java land,或从Java land封送数据。根据Java在内存中存储数组的方式,JNI可能需要进行深度复制以形成C#能够理解的数组,例如JVM最初是否将数组存储在非连续块中

这是:

JNI在Java代码和本机代码之间提供了一个干净的接口。为了保持这种分离,数组作为不透明句柄传递,本机代码必须回调JVM,以便使用set和get调用操纵数组元素Java规范将这些调用是否提供对数组的直接访问或返回数组的副本留给JVM实现。例如,当JVM以不连续存储阵列的方式优化了阵列时,可能会返回副本。

因此,这些调用可能会导致复制被操纵的元素。例如,如果在包含1000个元素的数组上调用GetLongArrayElements(),则可能会导致至少8000个字节的分配和复制(1000个元素*每长8个字节)。然后使用ReleaseLongArrayElements()更新数组的内容时,可能需要另一个8000字节的副本来更新数组。即使使用较新的GetPrimitiveArrayCritical(),该规范仍然允许JVM复制完整阵列


因此,基本上,尽量避免跨JNI编组数组(例如使用
AndroidJavaObject
),因为JNI是否进行深度复制并不取决于C。

AndroidJavaObject
使用JNI(Java本机接口)根据Java在内存中存储数组的方式,JNI可能需要进行深度复制以形成C#能够理解的数组,例如JVM最初是否将数组存储在非连续块中

这是:

JNI在Java代码和本机代码之间提供了一个干净的接口。为了保持这种分离,数组作为不透明句柄传递,本机代码必须回调JVM,以便使用set和get调用操纵数组元素。Java规范将这些调用是否提供直接访问留给JVM实现返回数组或数组的副本。例如,当JVM以不连续存储数组的方式优化了数组时,可能会返回一个副本。

因此,这些调用可能会导致复制被操纵的元素