|
|
|
|
@ -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) { |
|
|
|
|
|