From cd8a5392d5376c48c54b269b5b4c8f66492a9523 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 01:09:30 +0200 Subject: [PATCH] Expose source codec selection and aggregate send telemetry through JNI --- src/Makefile.am | 2 ++ .../main/java/com/utun/chat/data/NativeLib.kt | 14 ++++++----- .../jni_bridge/android_stream_jni.c | 23 +++++++++++++++---- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index c97ae4ac..02e5b7b1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -186,6 +186,8 @@ utun_CORE_SOURCES = \ # libutun: all core sources except main() libutun_a_SOURCES = \ + transport_layer/etcp_tx_stats.c \ + transport_layer/etcp_tx_stats.h \ $(stream_core_files) \ utun_instance.c \ config_parser.c \ diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt index bf208bf6..b28eb8f5 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt @@ -200,12 +200,13 @@ object NativeLib { /* Live media используют отдельную ограниченную очередь, минуя чатовый event bus. */ fun streamOpen(id: Long, group: Long, source: Long = 0, width: Int = 0, height: Int = 0, - rotation: Int = 0, audio: Boolean = true, epoch: Long = 0): Boolean = - nativeStreamOpen(id, group, source, width, height, rotation, audio, epoch) + rotation: Int = 0, audio: Boolean = true, epoch: Long = 0, videoCodec: Int = 1): Boolean = + nativeStreamOpen(id, group, source, width, height, rotation, audio, epoch, videoCodec) fun streamWatch(group: Long): Boolean = nativeStreamWatch(group) fun streamFeed(id: Long, video: Boolean, pts: Long, key: Boolean, data: ByteArray, revision: Long = 0, - width: Int = 0, height: Int = 0, rotation: Int = 0, audio: Boolean = true): Int = - nativeStreamFeed(id, video, pts, key, data, revision, width, height, rotation, audio) + width: Int = 0, height: Int = 0, rotation: Int = 0, audio: Boolean = true, videoCodec: Int = 1): Int = + nativeStreamFeed(id, video, pts, key, data, revision, width, height, rotation, audio, videoCodec) + fun streamTxStats(id: Long): LongArray? = nativeStreamTxStats(id) fun streamStop(id: Long) { nativeStreamStop(id) } fun streamPull(id: Long, positionUs: Long = -1, videoAheadUs: Long = 80000, audioAheadUs: Long = 80000): ByteArray? = nativeStreamPull(id, positionUs, videoAheadUs, audioAheadUs) @@ -225,10 +226,11 @@ object NativeLib { fun streamOpusDecode(handle: Long, packet: ByteArray): ShortArray? = nativeStreamOpusDecode(handle, packet) fun streamOpusClose(handle: Long) { nativeStreamOpusClose(handle) } private external fun nativeStreamOpen(id: Long, group: Long, source: Long, width: Int, height: Int, rotation: Int, - audio: Boolean, epoch: Long): Boolean + audio: Boolean, epoch: Long, videoCodec: Int): Boolean private external fun nativeStreamWatch(group: Long): Boolean private external fun nativeStreamFeed(id: Long, video: Boolean, pts: Long, key: Boolean, data: ByteArray, - revision: Long, width: Int, height: Int, rotation: Int, audio: Boolean): Int + revision: Long, width: Int, height: Int, rotation: Int, audio: Boolean, videoCodec: Int): Int + private external fun nativeStreamTxStats(id: Long): LongArray? private external fun nativeStreamStop(id: Long) private external fun nativeStreamPull(id: Long, position: Long, videoAhead: Long, audioAhead: Long): ByteArray? private external fun nativeStreamStatus(id: Long): LongArray? diff --git a/tools/chatgui-android/jni_bridge/android_stream_jni.c b/tools/chatgui-android/jni_bridge/android_stream_jni.c index 2e8348f9..979753f2 100644 --- a/tools/chatgui-android/jni_bridge/android_stream_jni.c +++ b/tools/chatgui-android/jni_bridge/android_stream_jni.c @@ -26,9 +26,9 @@ struct android_stream_opus { }; JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamOpen( - JNIEnv *env, jobject obj, jlong id, jlong group, jlong source, jint width, jint height, jint rotation, jboolean audio, jlong epoch) { + JNIEnv *env, jobject obj, jlong id, jlong group, jlong source, jint width, jint height, jint rotation, jboolean audio, jlong epoch, jint codec) { (void)env; (void)obj; - return android_stream_open((uint64_t)id, (uint64_t)group, (uint64_t)source, width, height, rotation, audio, (uint64_t)epoch) == 0; + return android_stream_open((uint64_t)id, (uint64_t)group, (uint64_t)source, width, height, rotation, audio, (uint64_t)epoch, codec) == 0; } JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamWatch(JNIEnv *env, jobject obj, jlong group) { @@ -38,7 +38,7 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamWatch(J JNIEXPORT jint JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamFeed( JNIEnv *env, jobject obj, jlong id, jboolean video, jlong pts, jboolean key, jbyteArray data, - jlong revision, jint width, jint height, jint rotation, jboolean audio) { + jlong revision, jint width, jint height, jint rotation, jboolean audio, jint video_codec) { (void)obj; if (!data || pts < 0 || (video && (revision <= 0 || revision > UINT32_MAX || width <= 0 || width > 4096 || height <= 0 || height > 4096 || rotation < 0 || rotation > 270 || rotation % 90))) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream feed missing data/invalid PTS"); return -1; } @@ -46,7 +46,11 @@ JNIEXPORT jint JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamFeed( jbyte *bytes = (*env)->GetByteArrayElements(env, data, NULL); if (!bytes) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream feed array access failed"); return -1; } struct stream_media_format format = {(uint16_t)width, (uint16_t)height, (uint16_t)rotation, - STREAM_MEDIA_H264, audio ? STREAM_MEDIA_OPUS_STEREO : 0, (uint32_t)revision}; + (uint8_t)video_codec, audio ? STREAM_MEDIA_OPUS_STEREO : 0, (uint32_t)revision}; + if (video && (video_codec < STREAM_MEDIA_H264 || video_codec > STREAM_MEDIA_H266)) { + DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI video codec invalid id=%d", video_codec); + (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); return -1; + } int result = android_stream_feed((uint64_t)id, video, (uint64_t)pts, key, (const uint8_t *)bytes, (size_t)length, video ? &format : NULL); (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); return result; @@ -56,6 +60,17 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamStop(JNIEnv (void)env; (void)obj; android_stream_stop((uint64_t)id); } +JNIEXPORT jlongArray JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamTxStats(JNIEnv *env, jobject obj, jlong id) { + (void)obj; struct android_stream_tx_stats s; + if (android_stream_tx_snapshot((uint64_t)id, &s) < 0) return NULL; + jlong values[] = {s.sample_us, s.video_bytes, s.audio_bytes, s.stream_bytes, s.transport_bytes, + s.inflight_bytes, s.acked_1s, s.total_bytes, s.connections}; + jlongArray result = (*env)->NewLongArray(env, 9); + if (result) (*env)->SetLongArrayRegion(env, result, 0, 9, values); + else DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI TX stats allocation failed"); + return result; +} + JNIEXPORT jbyteArray JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamPull( JNIEnv *env, jobject obj, jlong id, jlong position, jlong video_ahead, jlong audio_ahead) { (void)obj;