将无符号字符* 作为 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
你可以取消排队输入缓冲区((,将该缓冲区传递到本机代码中,然后 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 可以识别你在做什么并优化它,但我不知道这是否正在完成。
尽量避免分配缓冲区。 预分配和池化有助于提高绩效。
- Android:使用 c++ 中的 byte[] 参数调用 java 方法
- 如何在JNI中从线程内部调用JAVA方法
- Qt 调用具有 1 个以上参数的 java 方法
- Android 无法从本机代码调用 Java 方法 JNI
- 是否可以在SO库中公开C API以访问Android JAVA方法
- 如何从 NDK(JNI) 调用特定的 Java 方法
- 从 JNI 调用 getWritableDatabase (SQLCipher java 方法)
- 使用C 修改Java方法/字节码
- 使用 JNI 从 cpp 调用 java 方法时出现异常
- 在Qt中从C ++调用Java方法
- 使用C 获取Java对象来调用Java方法
- JNI:从C++调用JAVA方法,返回对象,引用和GC
- 从 Java 调用 c++,然后从相同的 c++ 方法调用 java 方法
- 使用jni.h在C++中编译java方法时出现构建错误
- 如何从C++/Qt调用QtActivity中的非静态Java方法
- 从Android NDK SIGSEGV调用Java方法
- 从c++调用非静态java方法时出错
- 使本机代码访问java方法和数据成员
- Android NDK调用java方法表单cpp
- 当我从C++调用Java方法时,我应该调用PushLocalFrame和PopLocalFrame吗