Browse Source

Bound audio writes and release codecs after failed initialization

master
evgeny 1 week ago
parent
commit
a043dc9a42
  1. 3
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamCaptureEngine.kt
  2. 36
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt

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

@ -84,7 +84,8 @@ class StreamCaptureEngine(
}
check(NativeLib.streamOpen(id, group, width = size.width, height = size.height, rotation = rotation, audio = microphone))
originNs = System.nanoTime()
encoder = MediaCodec.createByCodecName(info.name).also { codec ->
encoder = MediaCodec.createByCodecName(info.name)
encoder!!.also { codec ->
codec.setCallback(object : MediaCodec.Callback() {
override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {}
override fun onError(codec: MediaCodec, error: MediaCodec.CodecException) { failed("encoder: ${error.diagnosticInfo}") }

36
tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt

@ -61,13 +61,19 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
var endDrainUntil = 0L
fun resetVideo() {
try { decoder?.stop(); decoder?.release() }
try { decoder?.stop() }
catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder reset id=$id: ${e.message}") }
try { decoder?.release() }
catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder release id=$id: ${e.message}") }
decoder = null; videos.clear(); videoBytes = 0; waitingKey = true
mutableStatus.value = R.string.stream_waiting_key
}
fun resetAudio() {
track?.pause(); track?.flush(); track?.release(); track = null
try { track?.pause(); track?.flush() }
catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio reset id=$id: ${e.message}") }
try { track?.release() }
catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio release id=$id: ${e.message}") }
track = null
if (opus != 0L) NativeLib.streamOpusClose(opus)
opus = 0; audioBase = -1; written = 0; lastHead = -1; audios.clear()
}
@ -85,6 +91,19 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
}
return if (videoBase >= 0) videoBase + (now - videoStartNs) / 1000 else 0
}
fun writePcm(t: AudioTrack, pcm: ShortArray) {
val deadline = System.nanoTime() + 100_000_000
var offset = 0
while (running && offset < pcm.size) {
val count = t.write(pcm, offset, pcm.size - offset, AudioTrack.WRITE_NON_BLOCKING)
check(count >= 0) { "AudioTrack write=$count" }
if (count > 0) { offset += count; written += count }
else {
check(System.nanoTime() < deadline) { "AudioTrack stalled for 100ms" }
Thread.sleep(2)
}
}
}
try {
check(NativeLib.streamOpen(id, group, source)) { "Viewer open refused" }
val openDeadline = System.nanoTime() + 10_000_000_000
@ -159,10 +178,10 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
} else if (missing >= 0) {
repeat(missing) {
val conceal = NativeLib.streamOpusDecode(opus, byteArrayOf()) ?: error("Opus PLC failed")
check(t.write(conceal, 0, conceal.size, AudioTrack.WRITE_BLOCKING) == conceal.size); written += conceal.size
writePcm(t, conceal)
}
val pcm = NativeLib.streamOpusDecode(opus, f.data) ?: error("Opus decode failed")
check(t.write(pcm, 0, pcm.size, AudioTrack.WRITE_BLOCKING) == pcm.size); written += pcm.size
writePcm(t, pcm)
} else LogManager.addLog("DEBUG", "stream", "late audio discarded id=$id pts=${f.pts}")
}
}
@ -178,7 +197,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
setByteBuffer("csd-1", ByteBuffer.wrap(nals.first { it.first == 8 }.second))
setInteger(MediaFormat.KEY_ROTATION, frame.rotation); setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, H264AnnexB.MAX_FRAME)
}
decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC).also { it.configure(format, surface, null, 0); it.start() }
decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
decoder!!.configure(format, surface, null, 0); decoder!!.start()
LogManager.addLog("INFO", "video", "decoder started id=$id ${frame.width}x${frame.height} rotation=${frame.rotation}")
}
val index = decoder!!.dequeueInputBuffer(0)
@ -220,8 +240,10 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
if (running) { mutableStatus.value = R.string.stream_failed; LogManager.addLog("ERROR", "stream", "viewer failed id=$id: ${e.message}") }
} finally {
NativeLib.streamStop(id)
try { decoder?.stop(); decoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder release id=$id: ${e.message}") }
try { track?.stop(); track?.release() } catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio release id=$id: ${e.message}") }
try { decoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder stop id=$id: ${e.message}") }
try { decoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder release id=$id: ${e.message}") }
try { track?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio stop id=$id: ${e.message}") }
try { track?.release() } catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio release id=$id: ${e.message}") }
if (opus != 0L) NativeLib.streamOpusClose(opus)
val deadline = System.nanoTime() + 500_000_000
while (!ended && NativeLib.isReady() && System.nanoTime() < deadline) {

Loading…
Cancel
Save