Verify hardware rate changes on continuous noisy video
This commit is contained in:
1 parent
1b0b429835
commit
607952b709
1 file changed
+58
-3
@@ -1,6 +1,10 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import android.app.Instrumentation
|
||||
import android.graphics.ImageFormat
|
||||
import android.media.Image
|
||||
import android.media.ImageReader
|
||||
import android.media.ImageWriter
|
||||
import android.os.Bundle
|
||||
|
||||
/** Запускается только явно через adb am instrument; камера и реальные звонки не затрагиваются. */
|
||||
@@ -26,9 +30,7 @@ class CallVideoProbe : Instrumentation() {
|
||||
check(codecs.isNotEmpty() && decoders.isNotEmpty()) { "No verified encoders or decoders" }
|
||||
for ((codec, entry) in codecs) {
|
||||
val f = CallVideoFormat(1, 640, 480, codec = codec, level = entry.level, profile = entry.profile)
|
||||
for (rate in listOf(4_000_000, 300_000, 4_000_000)) {
|
||||
CallVideoCodec(f, rate, entry.name).use { it.prepare() }
|
||||
}
|
||||
verifyRateChanges(f, entry.name)
|
||||
CallVideoCodec(f, 1_000_000, entry.name).use { encoder ->
|
||||
encoder.prepare(); var key: ByteArray? = null; var input = 0
|
||||
val deadline = System.nanoTime() + 3_000_000_000
|
||||
@@ -45,4 +47,57 @@ class CallVideoProbe : Instrumentation() {
|
||||
finish(1, Bundle().apply { putString("error", e.toString()) })
|
||||
}
|
||||
}
|
||||
|
||||
/** Шум требует заданную полосу: на однотонных пробах нельзя обнаружить проигнорированный bitrate. */
|
||||
private fun verifyRateChanges(format: CallVideoFormat, name: String) {
|
||||
ImageReader.newInstance(format.width, format.height, ImageFormat.YUV_420_888, 2).use { reader ->
|
||||
ImageWriter.newInstance(reader.surface, 1).use { writer ->
|
||||
writer.dequeueInputImage().use { image ->
|
||||
CallVideoCodec(format, 4_000_000, name).use { encoder ->
|
||||
encoder.prepare(); var random = 1
|
||||
for ((phase, rate) in listOf(4_000_000, 300_000, 4_000_000).withIndex()) {
|
||||
check(encoder.setBitrate(rate)) { "In-place bitrate unsupported: $name" }
|
||||
var input = 0; var output = 0; var bytes = 0L; var prepared = false; var key = false
|
||||
val base = 10_000_000L + phase * 4_000_000L
|
||||
val deadline = System.nanoTime() + 15_000_000_000L
|
||||
while (output < 120 && System.nanoTime() < deadline) {
|
||||
if (input < 120) {
|
||||
if (!prepared) { random = fillNoise(image, random); prepared = true }
|
||||
if (encoder.submit(image, base + input * 1_000_000L / 30, input % 30 == 0)) {
|
||||
input++; prepared = false
|
||||
}
|
||||
}
|
||||
for (packet in encoder.drain()) {
|
||||
check(packet.pts == base + output * 1_000_000L / 30) { "Lost/reordered rate-change frame" }
|
||||
if (output == 0) key = packet.key
|
||||
if (output >= 60) bytes += packet.bytes.size
|
||||
output++
|
||||
}
|
||||
Thread.sleep(2)
|
||||
}
|
||||
val measured = bytes * 8 / 2
|
||||
val result = "name=$name phase=$phase target=$rate measured=$measured input=$input output=$output key=$key"
|
||||
android.util.Log.i("CallVideoProbe", "rate check $result")
|
||||
check(input == 120 && output == 120 && key) { "Incomplete rate check: $result" }
|
||||
check(measured > rate / 2 && measured < rate.toLong() * 3 / 2) { "Bitrate change ineffective: $result" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillNoise(image: Image, seed: Int): Int {
|
||||
var random = seed
|
||||
for ((index, plane) in image.planes.withIndex()) {
|
||||
val width = image.width shr (if (index == 0) 0 else 1)
|
||||
val height = image.height shr (if (index == 0) 0 else 1)
|
||||
val buffer = plane.buffer.duplicate(); val start = buffer.position()
|
||||
for (y in 0 until height) for (x in 0 until width) {
|
||||
random = random * 1664525 + 1013904223
|
||||
buffer.put(start + y * plane.rowStride + x * plane.pixelStride, if (index == 0) (random ushr 24).toByte() else 128.toByte())
|
||||
}
|
||||
}
|
||||
return random
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user