feat: Use ByteBuffer for much faster toArrayBuffer()

This commit is contained in:
Marc Rousavy 2023-08-23 14:23:31 +02:00
parent 862e05b64f
commit 521d7c8ccf
4 changed files with 26 additions and 29 deletions

View File

@ -98,28 +98,29 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
const jsi::Value& thisArg, const jsi::Value& thisArg,
const jsi::Value* args, const jsi::Value* args,
size_t count) -> jsi::Value { size_t count) -> jsi::Value {
auto buffer = this->frame->toByteArray(); auto buffer = this->frame->toByteBuffer();
auto arraySize = buffer->size(); if (!buffer->isDirect()) {
throw std::runtime_error("Failed to get byte content of Frame - array is not direct ByteBuffer!");
}
auto size = buffer->getDirectSize();
static constexpr auto ARRAYBUFFER_CACHE_PROP_NAME = "__frameArrayBufferCache"; static constexpr auto ARRAYBUFFER_CACHE_PROP_NAME = "__frameArrayBufferCache";
if (!runtime.global().hasProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME)) { if (!runtime.global().hasProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME)) {
vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray> arrayBuffer(runtime, arraySize); vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray> arrayBuffer(runtime, size);
runtime.global().setProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME, arrayBuffer); runtime.global().setProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME, arrayBuffer);
} }
// Get from global JS cache // Get from global JS cache
auto arrayBufferCache = runtime.global().getPropertyAsObject(runtime, ARRAYBUFFER_CACHE_PROP_NAME); auto arrayBufferCache = runtime.global().getPropertyAsObject(runtime, ARRAYBUFFER_CACHE_PROP_NAME);
auto arrayBuffer = vision::getTypedArray(runtime, arrayBufferCache).get<vision::TypedArrayKind::Uint8ClampedArray>(runtime); auto arrayBuffer = vision::getTypedArray(runtime, arrayBufferCache).get<vision::TypedArrayKind::Uint8ClampedArray>(runtime);
if (arrayBuffer.size(runtime) != arraySize) { if (arrayBuffer.size(runtime) != size) {
arrayBuffer = vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray>(runtime, arraySize); arrayBuffer = vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray>(runtime, size);
runtime.global().setProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME, arrayBuffer); runtime.global().setProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME, arrayBuffer);
} }
// directly write to C++ JSI ArrayBuffer // directly write to C++ JSI ArrayBuffer
auto destinationBuffer = arrayBuffer.data(runtime); auto destinationBuffer = arrayBuffer.data(runtime);
buffer->getRegion(0, memcpy(destinationBuffer, buffer->getDirectAddress(), sizeof(uint8_t) * size);
static_cast<jint>(arraySize),
reinterpret_cast<jbyte*>(destinationBuffer));
return arrayBuffer; return arrayBuffer;
}; };

View File

@ -57,9 +57,10 @@ int JFrame::getBytesPerRow() const {
return getBytesPerRowMethod(self()); return getBytesPerRowMethod(self());
} }
local_ref<JArrayByte> JFrame::toByteArray() const { local_ref<JByteBuffer> JFrame::toByteBuffer() const {
static const auto toByteArrayMethod = getClass()->getMethod<JArrayByte()>("toByteArray"); static const auto toByteBufferMethod = getClass()->getMethod<JByteBuffer()>("toByteBuffer");
return toByteArrayMethod(self()); return toByteBufferMethod(self());
}
} }
void JFrame::incrementRefCount() { void JFrame::incrementRefCount() {

View File

@ -25,7 +25,7 @@ struct JFrame : public JavaClass<JFrame> {
jlong getTimestamp() const; jlong getTimestamp() const;
local_ref<JString> getOrientation() const; local_ref<JString> getOrientation() const;
local_ref<JString> getPixelFormat() const; local_ref<JString> getPixelFormat() const;
local_ref<JArrayByte> toByteArray() const; local_ref<JByteBuffer> toByteBuffer() const;
void incrementRefCount(); void incrementRefCount();
void decrementRefCount(); void decrementRefCount();
void close(); void close();

View File

@ -89,36 +89,31 @@ public class Frame {
return image.getPlanes()[0].getRowStride(); return image.getPlanes()[0].getRowStride();
} }
private static byte[] byteArrayCache; private static ByteBuffer byteArrayCache;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@DoNotStrip @DoNotStrip
public byte[] toByteArray() { public ByteBuffer toByteBuffer() {
switch (image.getFormat()) { switch (image.getFormat()) {
case ImageFormat.YUV_420_888: case ImageFormat.YUV_420_888:
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer vuBuffer = image.getPlanes()[2].getBuffer(); ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining(); int ySize = yBuffer.remaining();
int vuSize = vuBuffer.remaining(); int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
int totalSize = ySize + uSize + vSize;
if (byteArrayCache == null || byteArrayCache.length != ySize + vuSize) { if (byteArrayCache == null) {
byteArrayCache = new byte[ySize + vuSize]; byteArrayCache = ByteBuffer.allocate(totalSize);
} }
yBuffer.get(byteArrayCache, 0, ySize); byteArrayCache.rewind();
vuBuffer.get(byteArrayCache, ySize, vuSize); byteArrayCache.put(yBuffer).put(uBuffer).put(vBuffer);
return byteArrayCache; return byteArrayCache;
case ImageFormat.JPEG: case ImageFormat.JPEG:
ByteBuffer rgbBuffer = image.getPlanes()[0].getBuffer(); return image.getPlanes()[0].getBuffer();
int size = rgbBuffer.remaining();
if (byteArrayCache == null || byteArrayCache.length != size) {
byteArrayCache = new byte[size];
}
rgbBuffer.get(byteArrayCache);
return byteArrayCache;
default: default:
throw new RuntimeException("Cannot convert Frame with Format " + image.getFormat() + " to byte array!"); throw new RuntimeException("Cannot convert Frame with Format " + image.getFormat() + " to byte array!");
} }