简体   繁体   中英

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

I have a native function which signature is like -

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

I need to pass this pData from C++ to a Java method public void OnrecvData(byte[] data) without copying the data. Currently I am doing this by allocating jByteBuffer and using SetByteArrayRegion . But I think this approach doesn't avoid copy. I am looking for an approach like the other way GetDirectBufferAddress and pass a pointer to a starting address and length to avoid copy.

Thanks in advance!

EDIT 1

I won't modify the data in Java layer. Read-only access will be okay.

EDIT 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.)

My current code is like this -

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

Here encodedData is byte[] which is being received from native layer from unsigned char* using one time memory allocation and doing SetByteArrayRegion every time. So I am requiring one copy in my current implementation like memcpy way you suggested. Both these approach require one copy, but is my current implementation is less efficient than your suggested one (sending dequeueInputBuffer address reference and memcpy )? Like put ing byte[] onto ByteBuffer I did in Java Layer?

EDIT 3

Well, ByteBuffer.put(byte[], offset, offsetLength) seems like copying whole byte[] array into ByteBuffer like memcpy . So its another copy in Java layer also. I am going to implement your idea now. Thanks :)

The question is slightly more complicated than it seems.

The fastest way to make the data visible in Java-language code is to pre-allocate a "direct" ByteBuffer, and receive the data there. The data is immediately accessible from native or managed code. However, "accessible" just means that the Java-language code can get at it, not that it can get at individual bytes quickly .

If you allocate the direct ByteBuffer from JNI (using 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() . With that, code written in Java can just access it like any other byte[] , rather than having to call a method for every access.

If your data is arriving in buffers you don't control (eg video decoder output), then you don't really have a choice. You either have to copy the data into a managed byte[] , or pay a per-byte overhead on the Java side. (In theory the JIT can recognize what you're doing and optimize it, but I don't know if this is being done.)

Do try to avoid allocating buffers. Pre-allocation and pooling can help your performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM