将unsigned char *从C ++传递给java作为byte []的最有效方法

时间:2015-10-23 02:59:52

标签: java android c++ android-ndk java-native-interface

我有一个本机函数,签名就像 -

void onRecvData(const unsigned char* pData, int length)

我需要将此pData从C ++传递给Java方法public void OnrecvData(byte[] data)而不复制数据。目前我通过分配jByteBuffer和使用SetByteArrayRegion来实现这一目标。但我认为这种方法并不能避免复制。我正在寻找一种类似于GetDirectBufferAddress的方法,并将指针传递给起始地址和长度以避免复制。

提前致谢!

编辑1

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

编辑2

  

你可以dequeueInputBuffer(),将该缓冲区传递给本机代码,和   memcpy()数据。我99%确定MediaCodec缓冲区是直接的   ByteBuffers,因此您可以使用JNI获取地址和长度   功能。你如何调用dequeueInputBuffer()取决于你。你必须   使用MediaCodec返回的缓冲区,因此一个副本是不可避免的,   但至少它是编码数据。 (对于输出端,你想要   出于多种原因使用Surface。)

我目前的代码是这样的 -

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

此处encodedDatabyte[],它是unsigned char*从本地层接收的,使用一次内存分配并每次都执行SetByteArrayRegion。因此,我需要在我当前的实现中使用一个副本,例如您建议的memcpy方式。这两种方法都需要一个副本,但我目前的实现效率低于建议的方式(发送dequeueInputBuffer地址引用和memcpy)?就像put byte[]ByteBuffer我在Java Layer中做过的那样?

编辑3

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

1 个答案:

答案 0 :(得分:3)

问题稍微复杂一些。

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

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

如果您的数据到达缓冲区而您无法控制(例如视频解码器输出),那么您实际上没有选择。您必须将数据复制到托管byte[],或者在Java端支付每字节开销。 (从理论上讲,JIT可以识别你正在做的事情并对其进行优化,但我不知道这是否已经完成。)

尽量避免分配缓冲区。预分配和汇集可以帮助您提高绩效。