3 changed files with 110 additions and 1 deletions
@ -0,0 +1,108 @@
|
||||
/* JNI live media: session IDs для ядра, codec handles принадлежат ровно одному audio worker. */ |
||||
#ifdef __ANDROID__ |
||||
#include <jni.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "../../../lib/debug_config.h" |
||||
#include "../../../lib/mem.h" |
||||
#include "../../../lib/opus_codec.h" |
||||
#include "../libutun_lite/stream_bridge.h" |
||||
|
||||
struct android_stream_opus { opus_codec_encoder_t *encoder; opus_codec_decoder_t *decoder; }; |
||||
|
||||
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) { |
||||
(void)env; (void)obj; |
||||
return android_stream_open((uint64_t)id, (uint64_t)group, (uint64_t)source, width, height, rotation, audio) == 0; |
||||
} |
||||
|
||||
JNIEXPORT jint JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamFeed( |
||||
JNIEnv *env, jobject obj, jlong id, jboolean video, jlong pts, jboolean key, jbyteArray data) { |
||||
(void)obj; |
||||
if (!data || pts < 0) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream feed missing data/invalid PTS"); return -1; } |
||||
jsize length = (*env)->GetArrayLength(env, data); |
||||
jbyte *bytes = (*env)->GetByteArrayElements(env, data, NULL); |
||||
if (!bytes) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream feed array access failed"); return -1; } |
||||
int result = android_stream_feed((uint64_t)id, video, (uint64_t)pts, key, (const uint8_t *)bytes, (size_t)length); |
||||
(*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); |
||||
return result; |
||||
} |
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamStop(JNIEnv *env, jobject obj, jlong id) { |
||||
(void)env; (void)obj; android_stream_stop((uint64_t)id); |
||||
} |
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamPull(JNIEnv *env, jobject obj, jlong id) { |
||||
(void)obj; |
||||
size_t length = 0; uint8_t *data = android_stream_pull((uint64_t)id, &length); |
||||
if (!data) return NULL; |
||||
jbyteArray output = (*env)->NewByteArray(env, (jsize)length); |
||||
if (output) (*env)->SetByteArrayRegion(env, output, 0, (jsize)length, (const jbyte *)data); |
||||
else DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream pull array allocation failed bytes=%zu", length); |
||||
u_free(data); return output; |
||||
} |
||||
|
||||
/* Opus state существует только в audio worker; encode/decode не занимают uasync. */ |
||||
JNIEXPORT jlong JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamOpusCreate(JNIEnv *env, jobject obj, jboolean encode) { |
||||
(void)env; (void)obj; |
||||
struct android_stream_opus *codec = u_calloc(1, sizeof(*codec)); |
||||
if (!codec) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus allocation failed"); return 0; } |
||||
if (encode) { |
||||
codec->encoder = opus_codec_encoder_create(48000, 1); |
||||
if (!codec->encoder || opus_codec_encoder_application_set(codec->encoder, OPUS_CODEC_APP_AUDIO) < 0 |
||||
|| opus_codec_encoder_bitrate_set(codec->encoder, 64000) < 0) goto fail; |
||||
} else { |
||||
codec->decoder = opus_codec_decoder_create(48000, 1); |
||||
if (!codec->decoder) goto fail; |
||||
} |
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "JNI stream Opus created encode=%d rate=48000 channels=1 frame=960", encode); |
||||
return (jlong)(intptr_t)codec; |
||||
fail: |
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus initialization failed encode=%d", encode); |
||||
opus_codec_encoder_destroy(codec->encoder); opus_codec_decoder_destroy(codec->decoder); u_free(codec); return 0; |
||||
} |
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamOpusEncode( |
||||
JNIEnv *env, jobject obj, jlong handle, jshortArray samples) { |
||||
(void)obj; |
||||
struct android_stream_opus *codec = (struct android_stream_opus *)(intptr_t)handle; |
||||
if (!codec || !codec->encoder || !samples || (*env)->GetArrayLength(env, samples) != 960) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus encode invalid handle/frame"); return NULL; |
||||
} |
||||
int16_t pcm[960]; uint8_t packet[1275]; |
||||
(*env)->GetShortArrayRegion(env, samples, 0, 960, pcm); |
||||
if ((*env)->ExceptionCheck(env)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream PCM read failed"); return NULL; } |
||||
int length = opus_codec_encode(codec->encoder, pcm, 960, packet, sizeof(packet)); |
||||
if (length < 0) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus encode failed result=%d", length); return NULL; } |
||||
jbyteArray output = (*env)->NewByteArray(env, length); |
||||
if (output) (*env)->SetByteArrayRegion(env, output, 0, length, (const jbyte *)packet); |
||||
else DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus output allocation failed"); |
||||
return output; |
||||
} |
||||
|
||||
JNIEXPORT jshortArray JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamOpusDecode( |
||||
JNIEnv *env, jobject obj, jlong handle, jbyteArray packet) { |
||||
(void)obj; |
||||
struct android_stream_opus *codec = (struct android_stream_opus *)(intptr_t)handle; |
||||
if (!codec || !codec->decoder || !packet || (*env)->GetArrayLength(env, packet) > 1275) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus decode invalid handle/packet"); return NULL; |
||||
} |
||||
jsize length = (*env)->GetArrayLength(env, packet); uint8_t bytes[1275]; int16_t pcm[960]; |
||||
(*env)->GetByteArrayRegion(env, packet, 0, length, (jbyte *)bytes); |
||||
if ((*env)->ExceptionCheck(env)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus packet read failed"); return NULL; } |
||||
int count = opus_codec_decode(codec->decoder, length ? bytes : NULL, length, pcm, 960); |
||||
if (count != 960) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus decode failed count=%d", count); return NULL; } |
||||
jshortArray output = (*env)->NewShortArray(env, count); |
||||
if (output) (*env)->SetShortArrayRegion(env, output, 0, count, pcm); |
||||
else DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream Opus PCM allocation failed"); |
||||
return output; |
||||
} |
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamOpusClose(JNIEnv *env, jobject obj, jlong handle) { |
||||
(void)env; (void)obj; |
||||
struct android_stream_opus *codec = (struct android_stream_opus *)(intptr_t)handle; |
||||
if (!codec) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "JNI stream Opus release encode=%d", codec->encoder != NULL); |
||||
opus_codec_encoder_destroy(codec->encoder); opus_codec_decoder_destroy(codec->decoder); u_free(codec); |
||||
} |
||||
#endif |
||||
Loading…
Reference in new issue