将无符号字符* 作为 byte[] 从 C++ 传递到 java 的最有效方法

Most efficient way to pass unsigned char* from C++ to java as byte[]

本文关键字:java 方法 有效 C++ 字符 无符号 作为 byte      更新时间:2023-10-16

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

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

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

提前感谢!

编辑 1

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

编辑 2

你可以取消排队输入缓冲区((,将该缓冲区传递到本机代码中,然后 memcpy(( 数据。我 99% 确定媒体编解码器缓冲区是直接的 ByteBuffers,因此您可以使用JNI获取地址和长度 功能。如何调用 dequeueInputBuffer(( 取决于您。你必须 使用媒体编解码器返回的缓冲区,因此一个副本是不可避免的, 但至少它是编码的数据。(对于输出端,您希望 出于多种原因使用 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);
        }
    }

这里encodedData byte[]从本机层接收unsigned char*使用一次性内存分配并每次执行SetByteArrayRegion。所以我在当前的实现中需要一个副本memcpy就像你建议的那样。这两种方法都需要一个副本,但我目前的实现是否比您建议的实现效率低(发送dequeueInputBuffer地址引用和memcpy(?就像我在Java Layer中所做的ByteBuffer put byte[]一样?

编辑 3

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

这个问题比看起来稍微复杂一些。

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

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

如果你的数据到达你无法控制的缓冲区(例如视频解码器输出(,那么你真的别无选择。 您要么必须将数据复制到托管byte[]中,要么在 Java 端支付每字节开销。 (理论上,JIT 可以识别你在做什么并优化它,但我不知道这是否正在完成。

尽量避免分配缓冲区。 预分配和池化有助于提高绩效。