Describe call video JNI buffers, return values and playout handle ownership

This commit is contained in:
evgeny committed 2026-10-29 13:34:24 +01:00
1 parent 000273d451
commit 98cd5bf81c
2 files changed
+29 -4

No files matched your search

@@ -1,33 +1,58 @@
package com.utun.chat.data
/** Тот же C port, что у desktop: один TX mailbox, ограниченный RX, идентификатор звонка в каждой операции. */
/** JNI общего C video port: один TX mailbox, ограниченный RX, call ID в операциях порта.
* CAPS — ByteArray(64), формат — LongArray(9), TX meta — 12 полей, RX meta — 13;
* первые девять полей задаёт CallVideoFormat, далее frameId/ptsUs/flags/receivedUs.
* JNI копирует данные и не хранит заимствованные массивы. null/false означают отказ/неготовность API,
* а исключения JVM передаются caller. Аппаратная проба и порядок остановки остаются у Kotlin-владельцев. */
internal object CallVideoNative {
// Вызывать только после успешных encoder/decoder проб в обеих ориентациях.
/** Ограничить CAPS для обеих ориентаций после аппаратных проб; null — некорректные/несовместимые CAPS. */
external fun rotatableCaps(caps: ByteArray): ByteArray?
init { System.loadLibrary("utun") }
/** Открыть порт; повтор своего id обновляет CAPS, не сбрасывая RX. false — отказ. */
external fun open(id: Long, caps: ByteArray): Boolean
/** Идемпотентно закрыть порт своего id после остановки producers/RX; raw playout handle закрывается отдельно. */
external fun close(id: Long)
/** CAPS пира либо null, пока нет согласования/порта. */
external fun peer(id: Long): ByteArray?
/** 12 полей: keySerial, rateGeneration/request/bps, configuredGeneration, error, min/maxBps,
* decodedGeneration/accepted, canEncode, txIdle; null — порт недоступен. */
external fun status(id: Long): LongArray?
/** Поворот YUV plane: direct sliced buffers с первым пикселем в offset 0, strides в байтах;
* width/height — исходные пиксели, rotation=0/90/180/270. false — некорректная геометрия/буферы. */
external fun rotatePlane(source: java.nio.ByteBuffer, sourceRow: Int, sourcePixel: Int,
destination: java.nio.ByteBuffer, destinationRow: Int, destinationPixel: Int, width: Int, height: Int, rotation: Int): Boolean
/** Выбрать совместимый формат из CAPS, сохранив generation/размер/частоту desired; null — отказ. */
external fun choose(local: ByteArray, peer: ByteArray, desired: LongArray): LongArray?
/** Опубликовать фактический формат и bitrate в бит/с; false — отказ порта/формата. */
external fun format(id: Long, format: LongArray, bps: Int): Boolean
/** Ответить на rate request после IDR; bps=0 означает отказ/отмену. */
external fun applied(id: Long, generation: Long, request: Long, bps: Int)
/** 1 — frame принят, 0 — mailbox занят (повторить тот же frame), <0 — ошибка. */
external fun send(id: Long, meta: LongArray, bytes: ByteArray): Int
/** Извлечь encoded frame и заполнить meta[13]; null — кадра нет/отказ, meta тогда не использовать. */
external fun pull(id: Long, meta: LongArray): ByteArray?
/** Создать независимый playout handle; 0 — ошибка. RX сериализует все обращения, destroy после join допустим у caller. */
external fun playoutCreate(id: Long): Long
/** Освободить handle ровно один раз; после возврата ни одна операция с ним не допустима. */
external fun playoutDestroy(handle: Long)
/** Сбросить таймер/якорь при смене Surface; handle остаётся тем же. */
external fun playoutReset(handle: Long)
/** >0: ждать микросекунды; 0: показать; <0: пропустить только показ. */
/** CLOCK_MONOTONIC в мкс; pts<0 — нет готового output, localQueued/reserve — число кадров.
* >0: ждать микросекунды (100..5000); 0: показать; <0: пропустить только показ. */
external fun playoutStep(handle: Long, pts: Long, received: Long, now: Long, intervalUs: Int,
localQueued: Int, reserve: Int): Long
/** Подтвердить фактический показ pts в Surface; now — MONOTONIC мкс. */
external fun playoutPresented(handle: Long, pts: Long, now: Long)
external fun requestKey(id: Long)
/** Установить границы bitrate в бит/с; false — отказ. */
external fun range(id: Long, minimum: Int, maximum: Int): Boolean
/** Проверить uAV2 KEY: width/height/level/profile/dpb либо null. */
external fun inspect(codec: Int, bytes: ByteArray): LongArray?
/** Синтетический uAV2 KEY для аппаратной пробы decoder; null — неподдерживаемые параметры. */
external fun probeKey(codec: Int, profile: Int, width: Int, height: Int, fps: Int = 30): ByteArray?
external fun supported(local: ByteArray, peer: ByteArray, format: LongArray): Boolean
/** Сообщить принятие/отказ поколения decoder после фактического результата. */
external fun decoded(id: Long, generation: Long, accepted: Boolean)
}
@@ -124,7 +124,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_utun_chat_data_CallVideoNative_pull(JNIEnv
else call_video_port_request_key(id);
u_free((void *)f.data); return out;
}
/* Handle принадлежит только RX worker, включая reset/destroy. */
/* До join handle использует только RX worker; после join caller может завершить отложенный destroy. */
JNIEXPORT jlong JNICALL Java_com_utun_chat_data_CallVideoNative_playoutCreate(JNIEnv *e, jobject o, jlong id) {
(void)e; (void)o;
struct call_video_playout *p = u_malloc(sizeof(*p));