From e522691abbdfd5f324fe16e66d9b5a0e0d4a1c35 Mon Sep 17 00:00:00 2001 From: SendableMetatype <263203301+SendableMetatype@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:11:20 +0200 Subject: [PATCH] feat: add asynchronous sendAsync to RTCDataChannel --- .../src/main/cpp/include/JNI_RTCDataChannel.h | 16 ++++++ .../src/main/cpp/src/JNI_RTCDataChannel.cpp | 56 ++++++++++++++++++- .../dev/onvoid/webrtc/RTCDataChannel.java | 34 +++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/webrtc-jni/src/main/cpp/include/JNI_RTCDataChannel.h b/webrtc-jni/src/main/cpp/include/JNI_RTCDataChannel.h index 12305ec0..8211f00a 100644 --- a/webrtc-jni/src/main/cpp/include/JNI_RTCDataChannel.h +++ b/webrtc-jni/src/main/cpp/include/JNI_RTCDataChannel.h @@ -135,6 +135,22 @@ extern "C" { JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendByteArrayBuffer (JNIEnv *, jobject, jbyteArray, jboolean); + /* + * Class: dev_onvoid_webrtc_RTCDataChannel + * Method: sendDirectBufferAsync + * Signature: (Ljava/nio/ByteBuffer;IIZ)V + */ + JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendDirectBufferAsync + (JNIEnv *, jobject, jobject, jint, jint, jboolean); + + /* + * Class: dev_onvoid_webrtc_RTCDataChannel + * Method: sendByteArrayBufferAsync + * Signature: ([BZ)V + */ + JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendByteArrayBufferAsync + (JNIEnv *, jobject, jbyteArray, jboolean); + #ifdef __cplusplus } #endif diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp index 2bcc8b0c..4afc434c 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp @@ -23,6 +23,7 @@ #include "JavaUtils.h" #include "api/data_channel_interface.h" +#include "rtc_base/logging.h" #include @@ -192,11 +193,64 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendByteArrayBuffer webrtc::CopyOnWriteBuffer data(arrayPtr, arrayLength); env->ReleaseByteArrayElements(jBufferArray, arrayPtr, JNI_ABORT); - + try { channel->Send(webrtc::DataBuffer(data, static_cast(isBinary))); } catch (...) { ThrowCxxJavaException(env); } +} + +// Completion handler shared by the async send paths. Queueing failures are +// logged; on fatal errors WebRTC closes the channel, which the registered +// data channel observer sees as a state change. +static void logSendAsyncError(webrtc::RTCError error) +{ + if (!error.ok()) { + RTC_LOG(LS_WARNING) << "SendAsync failed: " << error.message(); + } +} + +JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendDirectBufferAsync +(JNIEnv * env, jobject caller, jobject jBuffer, jint position, jint length, jboolean isBinary) +{ + webrtc::DataChannelInterface * channel = GetHandle(env, caller); + CHECK_HANDLE(channel); + + uint8_t * address = static_cast(env->GetDirectBufferAddress(jBuffer)); + + if (address != NULL) { + jlong capacity = env->GetDirectBufferCapacity(jBuffer); + + if (position < 0 || length < 0 || static_cast(position) + length > capacity) { + env->Throw(jni::JavaError(env, "Buffer position/length out of bounds")); + return; + } + + // The data is copied into the CopyOnWriteBuffer before this call + // returns, so the caller may reuse the direct buffer immediately. + webrtc::CopyOnWriteBuffer data(address + position, static_cast(length)); + + channel->SendAsync(webrtc::DataBuffer(data, static_cast(isBinary)), &logSendAsyncError); + } + else { + env->Throw(jni::JavaError(env, "Non-direct buffer provided")); + } +} + +JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_sendByteArrayBufferAsync +(JNIEnv * env, jobject caller, jbyteArray jBufferArray, jboolean isBinary) +{ + webrtc::DataChannelInterface * channel = GetHandle(env, caller); + CHECK_HANDLE(channel); + + int8_t * arrayPtr = env->GetByteArrayElements(jBufferArray, nullptr); + size_t arrayLength = env->GetArrayLength(jBufferArray); + + webrtc::CopyOnWriteBuffer data(arrayPtr, arrayLength); + + env->ReleaseByteArrayElements(jBufferArray, arrayPtr, JNI_ABORT); + + channel->SendAsync(webrtc::DataBuffer(data, static_cast(isBinary)), &logSendAsyncError); } \ No newline at end of file diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/RTCDataChannel.java b/webrtc/src/main/java/dev/onvoid/webrtc/RTCDataChannel.java index 87c3ec6b..f579da78 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/RTCDataChannel.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/RTCDataChannel.java @@ -175,4 +175,38 @@ public void send(RTCDataChannelBuffer buffer) throws Exception { private native void sendByteArrayBuffer(byte[] buffer, boolean binary); + /** + * Sends data in the provided buffer to the remote peer without blocking + * the calling thread on the native network thread, unlike + * {@link #send(RTCDataChannelBuffer)} whose call is marshalled + * synchronously. The data is copied out of the buffer before this method + * returns, so the buffer may be reused immediately; only the bytes + * between position and limit are sent. + * + * Errors are reported asynchronously: queueing failures are logged + * natively, and fatal errors close the data channel, which the registered + * {@link RTCDataChannelObserver} sees as a state change. + * + * @param buffer The buffer to be queued for transmission. + */ + public void sendAsync(RTCDataChannelBuffer buffer) { + ByteBuffer data = buffer.data; + + if (data.isDirect()) { + sendDirectBufferAsync(data, data.position(), data.remaining(), buffer.binary); + } + else { + // The byte array path transmits whole arrays, so copy exactly + // the readable window, position to limit; a duplicate leaves + // the caller's position untouched. + byte[] window = new byte[data.remaining()]; + data.duplicate().get(window); + sendByteArrayBufferAsync(window, buffer.binary); + } + } + + private native void sendDirectBufferAsync(ByteBuffer buffer, int position, int length, boolean binary); + + private native void sendByteArrayBufferAsync(byte[] buffer, boolean binary); + }