Keep codec cleanup diagnostics independent of released SDK handles
This commit is contained in:
1 parent
b519536237
commit
d9959e5176
2 files changed
+38
-38
No files matched your search
@@ -20,7 +20,7 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
private set
|
||||
data class Packet(val pts: Long, val key: Boolean, val bytes: ByteArray)
|
||||
private val type = StreamVideoCodec.fromId(format.codec)
|
||||
private val resources = VideoCodecResources(MediaCodec.createByCodecName(name))
|
||||
private val resources = VideoCodecResources(MediaCodec.createByCodecName(name), name)
|
||||
private val codec = resources.codec
|
||||
private var config = byteArrayOf()
|
||||
private val partial = java.io.ByteArrayOutputStream()
|
||||
@@ -73,36 +73,36 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
val acquired: Long
|
||||
val copied: Long
|
||||
try {
|
||||
check(input.width >= format.width && input.height >= format.height)
|
||||
val crop = image?.cropRect
|
||||
if (crop != null) {
|
||||
check(crop.left % 2 == 0 && crop.top % 2 == 0)
|
||||
check((if (rotation % 180 == 0) crop.width() else crop.height()) == format.width
|
||||
&& (if (rotation % 180 == 0) crop.height() else crop.width()) == format.height) { "YUV input geometry mismatch" }
|
||||
}
|
||||
acquired = System.nanoTime()
|
||||
for (plane in 0..2) {
|
||||
val w = format.width shr (if (plane == 0) 0 else 1)
|
||||
val h = format.height shr (if (plane == 0) 0 else 1)
|
||||
val dst = input.planes[plane]; val src = image?.planes?.get(plane)
|
||||
if (src != null && crop != null) {
|
||||
val shift = if (plane == 0) 0 else 1
|
||||
val source = src.buffer.duplicate().apply {
|
||||
position(position() + (crop.top shr shift) * src.rowStride + (crop.left shr shift) * src.pixelStride)
|
||||
}.slice()
|
||||
check(CallVideoNative.rotatePlane(source, src.rowStride, src.pixelStride, dst.buffer.slice(), dst.rowStride,
|
||||
dst.pixelStride, crop.width() shr shift, crop.height() shr shift, rotation)) { "YUV plane rotation failed" }
|
||||
continue
|
||||
check(input.width >= format.width && input.height >= format.height)
|
||||
val crop = image?.cropRect
|
||||
if (crop != null) {
|
||||
check(crop.left % 2 == 0 && crop.top % 2 == 0)
|
||||
check((if (rotation % 180 == 0) crop.width() else crop.height()) == format.width
|
||||
&& (if (rotation % 180 == 0) crop.height() else crop.width()) == format.height) { "YUV input geometry mismatch" }
|
||||
}
|
||||
val row = ByteArray(w)
|
||||
for (y in 0 until h) {
|
||||
row.fill(if (plane == 0) 64 else 128.toByte())
|
||||
val buffer = dst.buffer.duplicate(); val start = buffer.position() + y * dst.rowStride
|
||||
if (dst.pixelStride == 1) { buffer.position(start); buffer.put(row) }
|
||||
else for (x in 0 until w) buffer.put(start + x * dst.pixelStride, row[x])
|
||||
acquired = System.nanoTime()
|
||||
for (plane in 0..2) {
|
||||
val w = format.width shr (if (plane == 0) 0 else 1)
|
||||
val h = format.height shr (if (plane == 0) 0 else 1)
|
||||
val dst = input.planes[plane]; val src = image?.planes?.get(plane)
|
||||
if (src != null && crop != null) {
|
||||
val shift = if (plane == 0) 0 else 1
|
||||
val source = src.buffer.duplicate().apply {
|
||||
position(position() + (crop.top shr shift) * src.rowStride + (crop.left shr shift) * src.pixelStride)
|
||||
}.slice()
|
||||
check(CallVideoNative.rotatePlane(source, src.rowStride, src.pixelStride, dst.buffer.slice(), dst.rowStride,
|
||||
dst.pixelStride, crop.width() shr shift, crop.height() shr shift, rotation)) { "YUV plane rotation failed" }
|
||||
continue
|
||||
}
|
||||
val row = ByteArray(w)
|
||||
for (y in 0 until h) {
|
||||
row.fill(if (plane == 0) 64 else 128.toByte())
|
||||
val buffer = dst.buffer.duplicate(); val start = buffer.position() + y * dst.rowStride
|
||||
if (dst.pixelStride == 1) { buffer.position(start); buffer.put(row) }
|
||||
else for (x in 0 until w) buffer.put(start + x * dst.pixelStride, row[x])
|
||||
}
|
||||
}
|
||||
}
|
||||
copied = System.nanoTime()
|
||||
copied = System.nanoTime()
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "call", "video YUV input failed name=$name pts=$pts rotation=$rotation: $e")
|
||||
try { resources.closeImage(input) } catch (cleanup: Exception) { e.addSuppressed(cleanup) }
|
||||
@@ -399,7 +399,7 @@ internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name
|
||||
keepRunning: () -> Boolean = { true }): Int {
|
||||
val type = StreamVideoCodec.fromId(f.codec)
|
||||
val config = type.unpackKey(packets.first().bytes).first
|
||||
return VideoCodecResources(MediaCodec.createByCodecName(name)).use { resources ->
|
||||
return VideoCodecResources(MediaCodec.createByCodecName(name), name).use { resources ->
|
||||
val decoder = resources.codec
|
||||
decoder.configure(decoderFormat(f.copy(rotation = 0), decoder, config), null, null, 0); decoder.start()
|
||||
var sent = 0; var decoded = 0; var pipeline = 0
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
* Ошибка close сохраняет владельца независимо от вызывающего кода, включая неудавшийся конструктор.
|
||||
* Retry использует только Android-ресурсы и не обращается к остановленному native-ядру.
|
||||
*/
|
||||
internal class VideoCodecResources(val codec: MediaCodec) : AutoCloseable {
|
||||
internal class VideoCodecResources(val codec: MediaCodec, private val name: String) : AutoCloseable {
|
||||
companion object { private val retained = ConcurrentHashMap.newKeySet<VideoCodecResources>() }
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val images = linkedSetOf<Image>()
|
||||
@@ -29,7 +29,7 @@ internal class VideoCodecResources(val codec: MediaCodec) : AutoCloseable {
|
||||
@Synchronized fun closeImage(image: Image) {
|
||||
if (image !in images) return
|
||||
try { image.close(); images.remove(image) } catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "video", "codec input Image close failed name=${codec.name}; retained: $e")
|
||||
LogManager.addLog("ERROR", "video", "codec input Image close failed name=$name; retained: $e")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -39,24 +39,24 @@ internal class VideoCodecResources(val codec: MediaCodec) : AutoCloseable {
|
||||
for (image in images.toList()) try { closeImage(image) } catch (e: Exception) { failure = e }
|
||||
if (!released) {
|
||||
if (!stopped) try { codec.stop(); stopped = true } catch (e: Exception) {
|
||||
LogManager.addLog("WARN", "video", "codec stop failed name=${codec.name}; attempting release: $e")
|
||||
LogManager.addLog("WARN", "video", "codec stop failed name=$name; attempting release: $e")
|
||||
}
|
||||
try { codec.release(); released = true } catch (e: Exception) {
|
||||
failure = e
|
||||
LogManager.addLog("ERROR", "video", "codec release failed name=${codec.name}; retained: $e")
|
||||
LogManager.addLog("ERROR", "video", "codec release failed name=$name; retained: $e")
|
||||
}
|
||||
}
|
||||
if (released && images.isEmpty()) {
|
||||
handler.removeCallbacks(retry); scheduled = false
|
||||
if (retained.remove(this)) LogManager.addLog("INFO", "video", "codec cleanup confirmed name=${codec.name}")
|
||||
if (retained.remove(this)) LogManager.addLog("INFO", "video", "codec cleanup confirmed name=$name")
|
||||
return
|
||||
}
|
||||
retained.add(this)
|
||||
if (!scheduled) {
|
||||
scheduled = handler.postDelayed(retry, delay)
|
||||
if (!scheduled) LogManager.addLog("ERROR", "video", "codec cleanup retry rejected name=${codec.name}; owner retained")
|
||||
else { LogManager.addLog("DEBUG", "video", "codec cleanup scheduled name=${codec.name} delayMs=$delay"); delay = (delay * 2).coerceAtMost(30_000) }
|
||||
if (!scheduled) LogManager.addLog("ERROR", "video", "codec cleanup retry rejected name=$name; owner retained")
|
||||
else { LogManager.addLog("DEBUG", "video", "codec cleanup scheduled name=$name delayMs=$delay"); delay = (delay * 2).coerceAtMost(30_000) }
|
||||
}
|
||||
throw IllegalStateException("Codec cleanup pending name=${codec.name} released=$released images=${images.size}", failure)
|
||||
throw IllegalStateException("Codec cleanup pending name=$name released=$released images=${images.size}", failure)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user