Trace stream startup decoding and presentation delays
This commit is contained in:
1 parent
f38aa3b08d
commit
1b036e4ea9
3 files changed
+90
-20
No files matched your search
@@ -51,6 +51,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
private val mutableStatus = MutableStateFlow(R.string.stream_connecting)
|
||||
val status: StateFlow<Int> = mutableStatus
|
||||
private val id = StreamController.nextId()
|
||||
private var requestedAtUs = 0L
|
||||
@Volatile private var running = false
|
||||
@Volatile var drainTimeoutMs = 2000L
|
||||
private set
|
||||
@@ -67,6 +68,9 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
return
|
||||
}
|
||||
players.add(this); mutableViewing.value = true
|
||||
requestedAtUs = System.nanoTime() / 1000
|
||||
LogManager.addLog("INFO", "stream", "android viewer requested id=$id source=${source.toULong().toString(16)} " +
|
||||
"epoch=${epoch.toULong().toString(16)}")
|
||||
running = true
|
||||
worker = Thread({
|
||||
try { if (running) play(surface) }
|
||||
@@ -111,10 +115,14 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
var deadline = System.nanoTime() + 10_000_000_000
|
||||
val info = MediaCodec.BufferInfo()
|
||||
var clockState: LongArray? = null
|
||||
var firstInputAtUs = 0L
|
||||
var firstShown = false
|
||||
var lastShownPts = -1L
|
||||
|
||||
fun createVideoPump() = StreamDecodePump<Frame>(
|
||||
submit = { frame ->
|
||||
if (decoder == null) {
|
||||
val openingAtUs = System.nanoTime() / 1000
|
||||
check(frame.key)
|
||||
val format = frame.codec.decoderFormat(frame.width, frame.height, frame.rotation, frame.config)
|
||||
val codecs = MediaCodecList(MediaCodecList.REGULAR_CODECS)
|
||||
@@ -133,8 +141,23 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
if (Build.VERSION.SDK_INT >= 30) format.setInteger(MediaFormat.KEY_LOW_LATENCY, if (lowLatency) 1 else 0)
|
||||
attempt = MediaCodec.createByCodecName(candidate.name)
|
||||
attempt.configure(format, surface, null, 0); attempt.start(); decoder = attempt
|
||||
var firstRendered = true
|
||||
val codecRevision = frame.revision
|
||||
try {
|
||||
attempt.setOnFrameRenderedListener({ _, pts, renderedNs ->
|
||||
if (firstRendered) {
|
||||
firstRendered = false
|
||||
LogManager.addLog("INFO", "stream", "android first surface frame id=$id revision=$codecRevision " +
|
||||
"PTS=$pts elapsedUs=${renderedNs / 1000 - requestedAtUs} " +
|
||||
"callbackDelayUs=${System.nanoTime() / 1000 - renderedNs / 1000}")
|
||||
}
|
||||
}, main)
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("WARN", "stream", "viewer presentation diagnostics unavailable id=$id: ${e.message}")
|
||||
}
|
||||
LogManager.addLog("INFO", "video", "viewer codec id=$id type=${frame.codec.label} decoder=${candidate.name} " +
|
||||
"config=${frame.config.size} lowLatency=$lowLatency revision=$videoRevision")
|
||||
"config=${frame.config.size} lowLatency=$lowLatency revision=$videoRevision " +
|
||||
"elapsedUs=${System.nanoTime() / 1000 - requestedAtUs} openUs=${System.nanoTime() / 1000 - openingAtUs}")
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
failure = e
|
||||
@@ -153,7 +176,9 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
if (index < 0) false else {
|
||||
val input = decoder!!.getInputBuffer(index) ?: error("Missing decoder input")
|
||||
check(frame.data.size <= input.capacity()) { "Decoder input is too small: ${input.capacity()}/${frame.data.size}" }
|
||||
input.clear(); input.put(frame.data); decoder!!.queueInputBuffer(index, 0, frame.data.size, frame.pts, 0); true
|
||||
input.clear(); input.put(frame.data)
|
||||
if (firstInputAtUs == 0L) firstInputAtUs = System.nanoTime() / 1000
|
||||
decoder!!.queueInputBuffer(index, 0, frame.data.size, frame.pts, 0); true
|
||||
}
|
||||
},
|
||||
receive = {
|
||||
@@ -177,7 +202,17 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
},
|
||||
release = { output, render ->
|
||||
decoder!!.releaseOutputBuffer(output.index, render)
|
||||
if (render) mutableStatus.value = R.string.stream_live
|
||||
if (render) {
|
||||
lastShownPts = output.pts
|
||||
if (!firstShown) {
|
||||
firstShown = true
|
||||
LogManager.addLog("INFO", "stream", "android first frame id=$id stage=display_submit " +
|
||||
"elapsedUs=${System.nanoTime() / 1000 - requestedAtUs} PTS=${output.pts} clockUs=$position " +
|
||||
"readyUs=$ready readyAheadUs=${ready - output.pts} avUs=${output.pts - position} " +
|
||||
"deviceLeadUs=${playback?.leadUs}")
|
||||
}
|
||||
mutableStatus.value = R.string.stream_live
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -188,13 +223,15 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
audioEnabled = meta[5] and 2 != 0L
|
||||
playback = StreamMixedPlayback(id, meta[3], audioEnabled)
|
||||
position = meta[3]; generation = meta[4]
|
||||
LogManager.addLog("INFO", "stream", "playback starts id=$id PTS=$position generation=$generation fixedPrefill=0")
|
||||
LogManager.addLog("INFO", "stream", "playback starts id=$id PTS=$position generation=$generation fixedPrefill=0 " +
|
||||
"elapsedUs=${System.nanoTime() / 1000 - requestedAtUs} readyUs=$ready videoUs=${meta[1]} audioUs=${meta[2]} " +
|
||||
"initialReserveUs=${ready - position} audio=$audioEnabled")
|
||||
}
|
||||
|
||||
fun resetVideo() {
|
||||
try { decoder?.stop(); decoder?.release() }
|
||||
catch (e: Exception) { LogManager.addLog("WARN", "video", "decoder reset id=$id: ${e.message}") }
|
||||
decoder = null; video = createVideoPump()
|
||||
decoder = null; video = createVideoPump(); firstInputAtUs = 0
|
||||
waitingKey = true; eosQueued = false
|
||||
mutableStatus.value = R.string.stream_waiting_key
|
||||
}
|
||||
@@ -243,7 +280,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
waitingKey = false
|
||||
mutableAspect.value = if (broadcastRotation % 180 == 0) width.toFloat() / height else height.toFloat() / width
|
||||
mutableStatus.value = R.string.stream_waiting_decoder
|
||||
LogManager.addLog("INFO", "video", "viewer KEY accepted id=$id revision=$revision PTS=$pts bytes=$size")
|
||||
LogManager.addLog("INFO", "video", "viewer KEY accepted id=$id revision=$revision PTS=$pts bytes=$size " +
|
||||
"elapsedUs=${System.nanoTime() / 1000 - requestedAtUs} readyAheadUs=${ready - pts}")
|
||||
}
|
||||
video.offer(Frame(pts, key, width, height, broadcastRotation, revision, codec, access.first, access.second),
|
||||
System.nanoTime() / 1000)
|
||||
@@ -272,7 +310,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
if (!playbackStarted && video.firstOutputPts != null) {
|
||||
playbackStarted = true
|
||||
LogManager.addLog("INFO", "video", "viewer first decoded frame id=$id revision=$videoRevision " +
|
||||
"PTS=${video.firstOutputPts} inputs=${video.inputCount} decoderLeadUs=${video.decoderLeadUs}")
|
||||
"PTS=${video.firstOutputPts} inputs=${video.inputCount} decoderLeadUs=${video.decoderLeadUs} " +
|
||||
"elapsedUs=${System.nanoTime() / 1000 - requestedAtUs} decodeUs=${System.nanoTime() / 1000 - firstInputAtUs}")
|
||||
}
|
||||
if (playbackStarted && playback != null && ready >= 0) {
|
||||
val lateVideo = !video.ended && (video.firstOutputPts == null
|
||||
@@ -305,7 +344,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
"output=${video.output?.pts} inflight=${video.inflight} decoderLeadUs=${video.decoderLeadUs} " +
|
||||
"clock=$position ready=$ready audio=$audioEnabled audioInputs=$audioInputs audioPTS=$lastAudioPts needsAudio=$needsAudio " +
|
||||
"reserve=${clockState?.get(1)} minimum=${clockState?.get(2)} " +
|
||||
"tempo=${clockState?.get(3)} ${playback?.description} waitingKey=$waitingKey")
|
||||
"tempo=${clockState?.get(3)} ${playback?.description} waitingKey=$waitingKey " +
|
||||
"shownPTS=$lastShownPts displayLagUs=${if (lastShownPts >= 0) position - lastShownPts else -1}")
|
||||
}
|
||||
Thread.sleep(5)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user