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

/* 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