Browse Source

Assemble partial codec setup and test real out of band decoder configuration

master
evgeny 6 days ago
parent
commit
49ca49fa1e
  1. 6
      doc/service_lifecycle.md
  2. 11
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCaptureEngine.kt
  3. 1
      tools/chatgui/tests/test_stream_view.cpp

6
doc/service_lifecycle.md

@ -104,6 +104,12 @@ Live media хранится в RS-кольце с общим индексом PT
destroy отсоединённого cursor не обращается к store. Frontend прекращает audio callback destroy отсоединённого cursor не обращается к store. Frontend прекращает audio callback
и worker до освобождения своего DSP; borrowed handle/RS pointers между потоками не передаются. и worker до освобождения своего DSP; borrowed handle/RS pointers между потоками не передаются.
ACK-история принадлежит ядру и обновляется только при первом найденном подтверждении
пакета. REINIT/close соединения не стирает уже подтверждённые за последнюю секунду байты.
Android publisher владеет отдельным 100ms таймером TX-снимков; close/detach отменяют
его в uasync до уничтожения instance. JNI копирует снимок под mutex без доступа к
живым очередям транспорта из Kotlin. Экран подписан на отдельный StateFlow статистики.
Статус транспорта UP подтверждает восстановление физического пути. READY сессии Статус транспорта UP подтверждает восстановление физического пути. READY сессии
группы означает согласованный JOIN/ACCEPT и завершённый обмен её таблицей; именно группы означает согласованный JOIN/ACCEPT и завершённый обмен её таблицей; именно
его ждёт recovery. REINIT меняет эпохи обмена, сохраняя рабочие маршруты. Эпохи его ждёт recovery. REINIT меняет эпохи обмена, сохраняя рабочие маршруты. Эпохи

11
tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCaptureEngine.kt

@ -62,6 +62,7 @@ class StreamCaptureEngine(
private var originNs = 0L private var originNs = 0L
private var csd = byteArrayOf() private var csd = byteArrayOf()
private val partial = ByteArrayOutputStream() private val partial = ByteArrayOutputStream()
private val partialConfig = ByteArrayOutputStream()
private var partialPts = 0L private var partialPts = 0L
private var partialKey = false private var partialKey = false
private var requireKey = true private var requireKey = true
@ -131,7 +132,7 @@ class StreamCaptureEngine(
preview.register(token, cameraPose) preview.register(token, cameraPose)
LogManager.addLog("DEBUG", "video", "preview camera id=$id revision=$token camera=${mode.camera} " + LogManager.addLog("DEBUG", "video", "preview camera id=$id revision=$token camera=${mode.camera} " +
"sensor=${cameraPose.sensor} front=${cameraPose.front} broadcastRotation=$rotation") "sensor=${cameraPose.sensor} front=${cameraPose.front} broadcastRotation=$rotation")
csd = byteArrayOf(); partial.reset(); requireKey = true; lastKeyRequest = 0; waitingFirst = true csd = byteArrayOf(); partial.reset(); partialConfig.reset(); requireKey = true; lastKeyRequest = 0; waitingFirst = true
confirming = mode.highSpeed; confirmStartNs = 0 confirming = mode.highSpeed; confirmStartNs = 0
reportNs = System.nanoTime(); reportFrames = encodedFrames; reportBytes = bytesSent reportNs = System.nanoTime(); reportFrames = encodedFrames; reportBytes = bytesSent
if (switchStartedNs == 0L) switchStartedNs = reportNs if (switchStartedNs == 0L) switchStartedNs = reportNs
@ -166,7 +167,11 @@ class StreamCaptureEngine(
buffer.position(info.offset); buffer.limit(info.offset + info.size) buffer.position(info.offset); buffer.limit(info.offset + info.size)
val bytes = ByteArray(info.size).also { buffer.get(it) } val bytes = ByteArray(info.size).also { buffer.get(it) }
if (info.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) { if (info.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) {
csd = mode.videoCodec.mergeConfig(csd, bytes) check(partialConfig.size() + bytes.size <= StreamVideoCodec.MAX_CONFIG)
partialConfig.write(bytes)
if (info.flags and MediaCodec.BUFFER_FLAG_PARTIAL_FRAME == 0) {
csd = mode.videoCodec.mergeConfig(csd, partialConfig.toByteArray()); partialConfig.reset()
}
} else encoded(bytes, info, config, rotation, token) } else encoded(bytes, info, config, rotation, token)
} }
if (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) problem = "unexpected encoder EOS" if (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) problem = "unexpected encoder EOS"
@ -352,7 +357,7 @@ class StreamCaptureEngine(
val done = releaseDone ?: return val done = releaseDone ?: return
try { encoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "video", "encoder stop id=$id: ${e.message}") } try { encoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "video", "encoder stop id=$id: ${e.message}") }
try { encoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "video", "encoder release id=$id: ${e.message}") } try { encoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "video", "encoder release id=$id: ${e.message}") }
encoder = null; input?.release(); input = null; partial.reset(); csd = byteArrayOf(); releaseDone = null encoder = null; input?.release(); input = null; partial.reset(); partialConfig.reset(); csd = byteArrayOf(); releaseDone = null
LogManager.addLog("DEBUG", "video", "video resources released id=$id closed=$closed revision=$generation") LogManager.addLog("DEBUG", "video", "video resources released id=$id closed=$closed revision=$generation")
done(closed) done(closed)
} }

1
tools/chatgui/tests/test_stream_view.cpp

@ -265,6 +265,7 @@ private slots:
AVCodecContext* context = avcodec_alloc_context3(encoder); QVERIFY(context); AVCodecContext* context = avcodec_alloc_context3(encoder); QVERIFY(context);
context->width = 160; context->height = 90; context->pix_fmt = AV_PIX_FMT_YUV420P; context->bit_rate = 200000; context->width = 160; context->height = 90; context->pix_fmt = AV_PIX_FMT_YUV420P; context->bit_rate = 200000;
context->time_base = {1, 30}; context->framerate = {30, 1}; context->max_b_frames = 0; context->thread_count = 1; context->gop_size = 1; context->time_base = {1, 30}; context->framerate = {30, 1}; context->max_b_frames = 0; context->thread_count = 1; context->gop_size = 1;
context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
AVDictionary* options = nullptr; AVDictionary* options = nullptr;
if (fixture.type == STREAM_MEDIA_H265) { if (fixture.type == STREAM_MEDIA_H265) {
av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0); av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0);

Loading…
Cancel
Save