You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
6.8 KiB
122 lines
6.8 KiB
/* 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, jlong epoch) { |
|
(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; |
|
} |
|
|
|
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamWatch(JNIEnv *env, jobject obj, jlong group) { |
|
(void)env; (void)obj; |
|
return android_stream_watch((uint64_t)group) == 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 jstring JNICALL Java_com_utun_chat_data_NativeLib_nativeStreamSources(JNIEnv *env, jobject obj, jlong group) { |
|
(void)obj; |
|
char *json = android_stream_sources((uint64_t)group); |
|
if (!json) return NULL; |
|
jstring output = (*env)->NewStringUTF(env, json); u_free(json); |
|
if (!output) DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "JNI stream source list string allocation failed"); |
|
return output; |
|
} |
|
|
|
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
|
|
|