Measure direct preview frame age using camera timestamp clocks
This commit is contained in:
1 parent
30dae7d7ed
commit
78054b2c1d
3 files changed
+12
-7
No files matched your search
@@ -119,7 +119,7 @@ class StreamCaptureEngine(
|
||||
if (previous != null && config.copy(videoBitrate = previous.videoBitrate) == previous) {
|
||||
try {
|
||||
encoder!!.setParameters(Bundle().apply { putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, config.bitrate) })
|
||||
lastGood = config; ready(config, false)
|
||||
lastGood = config; activeConfig = config; ready(config, false)
|
||||
LogManager.addLog("INFO", "video", "bitrate changed id=$id revision=$generation ${previous.bitrate}->${config.bitrate}")
|
||||
return@post
|
||||
} catch (e: Exception) {
|
||||
@@ -256,7 +256,8 @@ class StreamCaptureEngine(
|
||||
}
|
||||
try {
|
||||
if (output != null) {
|
||||
val map = manager.getCameraCharacteristics(mode.camera).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
|
||||
val characteristics = manager.getCameraCharacteristics(mode.camera)
|
||||
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
|
||||
?: error("Missing preview stream map")
|
||||
val size = if (mode.highSpeed) android.util.Size(mode.width, mode.height) else
|
||||
map.getOutputSizes(SurfaceTexture::class.java).orEmpty().filter {
|
||||
@@ -264,11 +265,11 @@ class StreamCaptureEngine(
|
||||
&& it.width.toLong() * mode.height == it.height.toLong() * mode.width
|
||||
}.maxByOrNull { it.width * it.height } ?: error("No compatible camera preview size")
|
||||
output.texture.setDefaultBufferSize(size.width, size.height)
|
||||
format = StreamPreviewFormat(size.width, size.height, cameraPose(mode.camera))
|
||||
format = StreamPreviewFormat(size.width, size.height, cameraPose(mode.camera),
|
||||
characteristics.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE) == CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME)
|
||||
preview.update(format)
|
||||
} else if (withPreview) preview.update(preview.state.value.format)
|
||||
val targets = listOfNotNull(input, output?.surface)
|
||||
check(targets.isNotEmpty()) { "Missing encoder surface" }
|
||||
val targets = listOfNotNull(checkNotNull(input) { "Missing encoder surface" }, output?.surface)
|
||||
val callback = object : CameraCaptureSession.StateCallback() {
|
||||
override fun onConfigured(configured: CameraCaptureSession) {
|
||||
if (!valid()) { configured.close(); return }
|
||||
|
||||
@@ -10,7 +10,7 @@ data class StreamPreviewCamera(val sensor: Int, val front: Boolean) {
|
||||
fun rotation(displayDegrees: Int): Int = (sensor + (if (front) displayDegrees else -displayDegrees) + 360) % 360
|
||||
}
|
||||
|
||||
data class StreamPreviewFormat(val width: Int, val height: Int, val camera: StreamPreviewCamera) {
|
||||
data class StreamPreviewFormat(val width: Int, val height: Int, val camera: StreamPreviewCamera, val realtimeTimestamp: Boolean = false) {
|
||||
fun aspect(displayDegrees: Int): Float = if (camera.rotation(displayDegrees) % 180 == 0) width.toFloat() / height else height.toFloat() / width
|
||||
|
||||
/** Углы TextureView после поворота; размеры view уже учитывают поворот сенсора. */
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.pm.PackageManager
|
||||
import android.content.Context
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.SurfaceTexture
|
||||
import android.os.SystemClock
|
||||
import android.view.Surface
|
||||
import android.view.TextureView
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
@@ -389,13 +390,16 @@ private class CameraPreviewView(context: Context, private val preview: StreamPre
|
||||
return false // Camera session удерживает свою ссылку до onClosed/onConfigureFailed.
|
||||
}
|
||||
override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {
|
||||
val timestamp = texture.timestamp
|
||||
if (timestamp == 0L) return // Очистка TextureView до первого кадра камеры.
|
||||
frames++
|
||||
val now = System.nanoTime()
|
||||
if (frames == 1L || now - reportNs >= 1_000_000_000) {
|
||||
val fps = (frames - reportFrames) * 1_000_000_000.0 / (now - reportNs).coerceAtLeast(1)
|
||||
val age = if (format?.realtimeTimestamp == true) (SystemClock.elapsedRealtimeNanos() - timestamp) / 1000 else null
|
||||
LogManager.addLog(if (frames == 1L) "INFO" else "DEBUG", "video", "direct preview frame " +
|
||||
"target=${target?.let(System::identityHashCode)} frames=$frames fps=${"%.1f".format(fps)} " +
|
||||
"ageUs=${(now - texture.timestamp) / 1000} timestampNs=${texture.timestamp} " +
|
||||
"ageUs=$age timestampNs=$timestamp realtime=${format?.realtimeTimestamp} " +
|
||||
"rotation=${format?.camera?.rotation(displayDegrees)}")
|
||||
reportNs = now; reportFrames = frames
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user