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* args,
size_t count) -> jsi::Value {
auto buffer = this->frame->toByteArray();
auto arraySize = buffer->size();
auto buffer = this->frame->toByteBuffer();
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";
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);
}
// Get from global JS cache
auto arrayBufferCache = runtime.global().getPropertyAsObject(runtime, ARRAYBUFFER_CACHE_PROP_NAME);
auto arrayBuffer = vision::getTypedArray(runtime, arrayBufferCache).get<vision::TypedArrayKind::Uint8ClampedArray>(runtime);
if (arrayBuffer.size(runtime) != arraySize) {
arrayBuffer = vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray>(runtime, arraySize);
if (arrayBuffer.size(runtime) != size) {
arrayBuffer = vision::TypedArray<vision::TypedArrayKind::Uint8ClampedArray>(runtime, size);
runtime.global().setProperty(runtime, ARRAYBUFFER_CACHE_PROP_NAME, arrayBuffer);
}
// directly write to C++ JSI ArrayBuffer
auto destinationBuffer = arrayBuffer.data(runtime);
buffer->getRegion(0,
static_cast<jint>(arraySize),
reinterpret_cast<jbyte*>(destinationBuffer));
memcpy(destinationBuffer, buffer->getDirectAddress(), sizeof(uint8_t) * size);
return arrayBuffer;
};

View File

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

View File

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

View File

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