将 unsigned char* 作为 byte[] 从 C++ 传递到 java 的最有效方法
Most efficient way to pass unsigned char* from C++ to java as byte[]
我有一个本机函数,其签名类似于 -
void onRecvData(const unsigned char* pData, int length)
我需要将此 pData
从 C++ 传递到 Java 方法 public void OnrecvData(byte[] data)
而无需复制数据。目前我通过分配 jByteBuffer
和使用 SetByteArrayRegion
来做到这一点。但我认为这种方法并不能避免复制。我正在寻找一种类似于另一种方法的方法 GetDirectBufferAddress
并将指针传递给起始地址和长度以避免复制。
提前致谢!
编辑 1
我不会修改Java层的数据。只读权限就可以了。
编辑 2
You can dequeueInputBuffer(), pass that buffer into native code, and
memcpy() the data. I'm 99% sure the MediaCodec buffers are direct
ByteBuffers, so you can get the address and length using JNI
functions. How you call dequeueInputBuffer() is up to you. You have to
use the buffers returned by MediaCodec, so one copy is unavoidable,
but at least it's the encoded data. (For the output side, you want to
use a Surface, for multiple reasons.)
我现在的代码是这样的 -
// ..................
// ..................
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
)?就像 put
ing byte[]
到 ByteBuffer
我在 Java 层做的一样?
编辑 3
好吧,ByteBuffer.put(byte[], offset, offsetLength)
就像是将整个 byte[]
数组复制到 ByteBuffer
中,就像 memcpy
一样。所以它也在 Java 层中的另一个副本。我现在要实现你的想法。谢谢:)
这个问题比看起来稍微复杂一些。
使数据在 Java 语言代码中可见的最快方法是预先分配一个 "direct" 字节缓冲区,并在那里接收数据。可以从本机代码或托管代码立即访问数据。但是,"accessible" 只是意味着 Java 语言代码可以获取它,而不是它可以获取单个字节 quickly.
如果您从 JNI 分配直接 ByteBuffer(使用 NewDirectByteBuffer
), you can pass it an arbitrary buffer, but that means there can't be a byte[]
backing the storage. If you allocate it from managed code using ByteBuffer#allocateDirect()
, recent versions of Android will give you a direct buffer that lives on the managed heap, which means it is also accessible through array()
。这样,用 Java 编写的代码就可以像访问任何其他 byte[]
一样访问它,而不必调用每次访问的方法。
如果您的数据到达您无法控制的缓冲区(例如视频解码器输出),那么您真的别无选择。您要么必须将数据复制到托管的 byte[]
,要么在 Java 端支付每字节的开销。 (理论上JIT可以识别你在做什么并优化它,但我不知道是否正在这样做。)
尽量避免分配缓冲区。预分配和池可以帮助您提高性能。
我有一个本机函数,其签名类似于 -
void onRecvData(const unsigned char* pData, int length)
我需要将此 pData
从 C++ 传递到 Java 方法 public void OnrecvData(byte[] data)
而无需复制数据。目前我通过分配 jByteBuffer
和使用 SetByteArrayRegion
来做到这一点。但我认为这种方法并不能避免复制。我正在寻找一种类似于另一种方法的方法 GetDirectBufferAddress
并将指针传递给起始地址和长度以避免复制。
提前致谢!
编辑 1
我不会修改Java层的数据。只读权限就可以了。
编辑 2
You can dequeueInputBuffer(), pass that buffer into native code, and memcpy() the data. I'm 99% sure the MediaCodec buffers are direct ByteBuffers, so you can get the address and length using JNI functions. How you call dequeueInputBuffer() is up to you. You have to use the buffers returned by MediaCodec, so one copy is unavoidable, but at least it's the encoded data. (For the output side, you want to use a Surface, for multiple reasons.)
我现在的代码是这样的 -
// ..................
// ..................
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
)?就像 put
ing byte[]
到 ByteBuffer
我在 Java 层做的一样?
编辑 3
好吧,ByteBuffer.put(byte[], offset, offsetLength)
就像是将整个 byte[]
数组复制到 ByteBuffer
中,就像 memcpy
一样。所以它也在 Java 层中的另一个副本。我现在要实现你的想法。谢谢:)
这个问题比看起来稍微复杂一些。
使数据在 Java 语言代码中可见的最快方法是预先分配一个 "direct" 字节缓冲区,并在那里接收数据。可以从本机代码或托管代码立即访问数据。但是,"accessible" 只是意味着 Java 语言代码可以获取它,而不是它可以获取单个字节 quickly.
如果您从 JNI 分配直接 ByteBuffer(使用 NewDirectByteBuffer
), you can pass it an arbitrary buffer, but that means there can't be a byte[]
backing the storage. If you allocate it from managed code using ByteBuffer#allocateDirect()
, recent versions of Android will give you a direct buffer that lives on the managed heap, which means it is also accessible through array()
。这样,用 Java 编写的代码就可以像访问任何其他 byte[]
一样访问它,而不必调用每次访问的方法。
如果您的数据到达您无法控制的缓冲区(例如视频解码器输出),那么您真的别无选择。您要么必须将数据复制到托管的 byte[]
,要么在 Java 端支付每字节的开销。 (理论上JIT可以识别你在做什么并优化它,但我不知道是否正在这样做。)
尽量避免分配缓冲区。预分配和池可以帮助您提高性能。