Fallback Android video after explicit decoder rejection
This commit is contained in:
1 parent
8703e2678a
commit
f47b7167a2
2 files changed
+28
-4
No files matched your search
@@ -63,6 +63,8 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
private var reportAt = 0L
|
||||
private var encodedBytes = 0L
|
||||
private var encodedFrames = 0
|
||||
private var rejectedGeneration = 0L
|
||||
private var excluded = emptySet<Int>()
|
||||
|
||||
init {
|
||||
handler.post {
|
||||
@@ -96,7 +98,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
|
||||
fun surface(surface: Surface?) = player.surface(surface)
|
||||
fun select(settings: CallVideoSettings) { handler.post {
|
||||
if (!stopped) { wanted = settings; mutable.value = mutable.value.copy(settings = settings) }
|
||||
if (!stopped) { wanted = settings; excluded = emptySet(); mutable.value = mutable.value.copy(settings = settings) }
|
||||
} }
|
||||
fun range(value: CallVideoBitrateRange) { handler.post {
|
||||
if (CallVideoNative.range(id, value.minimum * 1000, value.maximum * 1000)) mutable.value = mutable.value.copy(range = value)
|
||||
@@ -119,11 +121,25 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
val s = CallVideoNative.status(id) ?: return
|
||||
if (s[6] >= 300000 && s[7] >= s[6]) mutable.value = mutable.value.copy(range = CallVideoBitrateRange((s[6] / 1000).toInt(), (s[7] / 1000).toInt()))
|
||||
if (keySerial != s[0]) { keySerial = s[0]; needKey = true }
|
||||
val current = format
|
||||
if (current != null && s[8] == current.generation && s[9] == 0L && rejectedGeneration != s[8]) {
|
||||
rejectedGeneration = s[8]; excluded = excluded + current.codec
|
||||
if (wanted.codec == 0) {
|
||||
report("Receiver rejected video; selecting fallback")
|
||||
val peer = CallVideoNative.peer(id)
|
||||
if (peer != null) change(peer)
|
||||
} else fail("Receiver cannot decode selected codec")
|
||||
}
|
||||
if (s[1] == format?.generation && s[2] > request) {
|
||||
request = s[2]; pendingRequest = request; pendingRate = s[3].toInt()
|
||||
}
|
||||
if (!wanted.enabled) {
|
||||
if (active != null) { closeCamera(); encoder?.close(); encoder = null; active = null; report("Video off") }
|
||||
if (active != null) {
|
||||
val pending = if (confirmingRequest != 0L) confirmingRequest else pendingRequest
|
||||
if (pending != 0L) CallVideoNative.applied(id, format?.generation ?: 0, pending, 0)
|
||||
confirmingRequest = 0; pendingRequest = 0
|
||||
closeCamera(); encoder?.close(); encoder = null; active = null; report("Video off")
|
||||
}
|
||||
} else if (wanted != active && !opening && !closing && (lastKey < 0 || System.nanoTime() / 1000 - lastKey >= 1_000_000)) {
|
||||
val peer = CallVideoNative.peer(id)
|
||||
if (peer != null) change(peer)
|
||||
@@ -141,6 +157,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
val previousName = previous?.name; val previousRate = previous?.bitrate
|
||||
previous?.close(); encoder = null
|
||||
for (codec in if (wanted.codec == 0) listOf(2, 1) else listOf(wanted.codec)) {
|
||||
if (codec in excluded) continue
|
||||
val implementation = encoders[codec] ?: continue
|
||||
val selected = CallVideoNative.choose(caps, peer, desired.copy(codec = codec).array()) ?: continue
|
||||
var candidate: CallVideoCodec? = null
|
||||
@@ -160,7 +177,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
|
||||
LogManager.addLog("WARN", "call", "video mode rejected codec=$codec name=${implementation.name}: ${e.message}")
|
||||
}
|
||||
}
|
||||
if (previousFormat != null && previousSettings != null && previousName != null && previousRate != null) {
|
||||
if (previousFormat != null && previousFormat.codec !in excluded && previousSettings != null && previousName != null && previousRate != null) {
|
||||
encoder = CallVideoCodec(previousFormat, previousRate, previousName).also { it.prepare(); check(it.format == previousFormat) }
|
||||
wanted = previousSettings; needKey = true
|
||||
mutable.value = mutable.value.copy(settings = wanted)
|
||||
|
||||
@@ -18,6 +18,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
var pending: ByteArray? = null; var meta = LongArray(12)
|
||||
var held = -1; var heldPts = -1L; var inflight = 0; var draining = false
|
||||
var sourceEpoch = 0L; var lastOutput = -1L; var lastProgress = System.nanoTime()
|
||||
var reportedGeneration = 0L; var rejectedGeneration = 0L
|
||||
val clock = LongArray(8); val info = MediaCodec.BufferInfo()
|
||||
fun close() {
|
||||
try { decoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder stop: ${e.message}") }
|
||||
@@ -47,6 +48,9 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
} else if (info.size == 0) decoder!!.releaseOutputBuffer(out, false)
|
||||
else {
|
||||
check(info.presentationTimeUs > lastOutput) { "Video decoder reordered PTS" }
|
||||
format?.let { f -> if (reportedGeneration != f.generation) {
|
||||
CallVideoNative.decoded(id, f.generation, true); reportedGeneration = f.generation
|
||||
} }
|
||||
lastOutput = info.presentationTimeUs; inflight = (inflight - 1).coerceAtLeast(0)
|
||||
held = out; heldPts = info.presentationTimeUs; lastProgress = now
|
||||
}
|
||||
@@ -56,6 +60,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
val data = pending
|
||||
if (data != null && surface?.isValid == true && held < 0) {
|
||||
val next = CallVideoFormat.from(meta)
|
||||
if (next.generation == rejectedGeneration) { pending = null; continue }
|
||||
val key = meta[11] != 0L
|
||||
if (format != next && decoder != null) {
|
||||
if (!draining) {
|
||||
@@ -89,7 +94,9 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
if (decoder != null && held < 0 && inflight > 0 && now - lastProgress > 2_000_000_000) error("Video decoder stalled")
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "call", "video receive call=$id: ${e.message}")
|
||||
close(); pending = null; CallVideoNative.requestKey(id); failure(e.message ?: "Video decoder failed")
|
||||
val generation = if (pending != null) meta[0] else format?.generation ?: 0
|
||||
if (generation != 0L) { CallVideoNative.decoded(id, generation, false); rejectedGeneration = generation }
|
||||
close(); pending = null; failure(e.message ?: "Video decoder failed")
|
||||
}
|
||||
Thread.sleep(5)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user