从C+;传递无符号字符*的最有效方法+;将java转换为字节[]

从C+;传递无符号字符*的最有效方法+;将java转换为字节[],java,android,c++,android-ndk,java-native-interface,Java,Android,C++,Android Ndk,Java Native Interface,我有一个本机函数,其签名类似于- void onRecvData(const unsigned char* pData, int length) 我需要把这个代码> Pdata >代码从C++传递到java方法 public空洞OnReVData(字节[]数据)< /Cord>,而不复制数据。目前,我通过分配jByteBuffer并使用SetByteArrayRegion来实现这一点。但我认为这种方法不能避免复制。我正在寻找一种类似于另一种方法的方法GetDirectBufferAddress

我有一个本机函数,其签名类似于-

void onRecvData(const unsigned char* pData, int length)
我需要把这个代码> Pdata >代码从C++传递到java方法<代码> public空洞OnReVData(字节[]数据)< /Cord>,而不复制数据。目前,我通过分配
jByteBuffer
并使用
SetByteArrayRegion
来实现这一点。但我认为这种方法不能避免复制。我正在寻找一种类似于另一种方法的方法
GetDirectBufferAddress
,并将指针传递到起始地址和长度以避免复制

提前谢谢

编辑1

我不会修改Java层中的数据。只读访问就可以了

编辑2

您可以退出QueueInputBuffer(),将该缓冲区传递到本机代码中,然后 memcpy()保存数据。我99%确信MediaCodec缓冲区是直接的 ByteBuffers,因此您可以使用JNI获取地址和长度 功能。如何调用dequeueInputBuffer()取决于您。你必须 使用MediaCodec返回的缓冲区,因此一个副本是不可避免的, 但至少是编码数据。(对于输出端,您希望 出于多种原因,请使用曲面。)

我现在的代码是这样的-

    // ..................
    // ..................
    int inIndex = mMediaCodec.dequeueInputBuffer(mTimeoutUs);
    if (inIndex >= 0) {
        ByteBuffer buffer;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            buffer = mMediaCodec.getInputBuffers()[inIndex];
            buffer.clear();
        } else {
            buffer = mMediaCodec.getInputBuffer(inIndex);
        }
        if (buffer != null) {
            buffer.put(encodedData, 0, encodedDataLength);
            long presentationTimeUs = System.nanoTime() / 1000l;
            mMediaCodec.queueInputBuffer(inIndex, 0, encodedDataLength, presentationTimeUs, 0);
        }
    }
/。。。。。。。。。。。。。。。。。。
// ..................
int INININDEX=mMediaCodec.dequeueInputBuffer(mTimeoutUs);
如果(inIndex>=0){
ByteBuffer缓冲器;
if(Build.VERSION.SDK\u INT
这里的
encodedData
byte[]
,它是使用一次性内存分配并每次执行
SetByteArrayRegion
从本机层从
unsigned char*
接收的。因此,在我当前的实现中,我需要一个副本,就像您建议的
memcpy
一样。这两种方法都需要一个副本,但我当前的实现效率是否低于您建议的(发送
dequeueInputBuffer
地址引用和
memcpy
)?像我在Java层中做的那样,把
ing
byte[]
放到
ByteBuffer

编辑3


嗯,
ByteBuffer.put(byte[],offset,offsetLength)
似乎将整个
byte[]
数组复制到
ByteBuffer
中,就像
memcpy
一样。所以它也是Java层中的另一个副本。我现在要实施你的想法。谢谢:)

这个问题比看起来要复杂一些

使数据在Java语言代码中可见的最快方法是预先分配一个“direct”ByteBuffer,并在那里接收数据。可以立即从本机代码或托管代码访问数据。然而,“可访问”只是意味着Java语言代码可以获取它,而不是它可以快速获取单个字节

如果从JNI(使用)分配直接ByteBuffer,则可以向其传递任意缓冲区,但这意味着不能有
字节[]
支持存储。如果您使用托管代码分配它,最新版本的Android将为您提供一个位于托管堆上的直接缓冲区,这意味着它也可以通过。这样,用Java编写的代码就可以像任何其他
字节[]
一样访问它,而不必为每次访问调用一个方法

如果您的数据到达您无法控制的缓冲区(例如视频解码器输出),那么您真的没有选择。您要么将数据复制到托管的
字节[]
,要么在Java端支付每个字节的开销。(从理论上讲,JIT可以识别您正在做什么并对其进行优化,但我不知道是否正在这样做。)


尽量避免分配缓冲区。预分配和池可以帮助您提高性能。

虽然您标记了JNI,但最好在此处指定您正在使用JNI,以避免混淆。谢谢您的回答!在我的例子中,我将C++的编码数据传递到java层,用于解码使用<代码> MyAccoDEC < /C>。因此,我认为我无法控制缓冲区,因此,我没有机会避免复制(正如您所说)。那么,我应该使用JNI调用
MediaCodec
函数吗?哪个更有效1)将1280×720视频缓冲区从C++复制到java,在java层中进行解码,避免JNI调用2),虽然我不确定是否可以避免拷贝,但是如果选择JNI CalsSe,可以选择< Java> DEQueLeNePuffor()/Cuff>,将该缓冲区传递到本机代码中,并将代码缓存到“代码> MyCyPy.())/Cube >数据。我99%确定MediaCodec缓冲区是直接字节缓冲区,因此可以使用JNI函数获取地址和长度。如何调用
dequeueInputBuffer()
取决于您。您必须使用MediaCodec返回的缓冲区,因此一个副本是不可避免的,但至少是编码数据。(对于输出端,出于多种原因,您希望使用曲面。)感谢您的建议。是否可以发送由
getInputBuffer(mMediaCodec.dequeueInputBuffer(mTimeoutUs))
返回的
ByteBuffer
,并在本机层而不是
memcpy
,以这种方式更改起始地址和长度:?可能有效,但可能无效。我的期望是MediaCodec本机代码正在分配缓冲区并将指针放入ByteBuffers。MediaCodec实现稍后可能会从ByteBuffer中读回指针,但更可能的情况是,当您释放缓冲区N时,您指的是它分配的内存。所以你可以试试,但我会担心它的可移植性