Model capture codec callbacks and Camera2 sessions for host fault tests
This commit is contained in:
1 parent
59d1839d8d
commit
7a2fc0112c
3 files changed
+62
-6
No files matched your search
+27
-4
@@ -14,7 +14,7 @@ class CameraAccessException(val reason: Int) : Exception("camera access failure
|
||||
|
||||
class HostStreamMap {
|
||||
fun getOutputSizes(format: Int) = arrayOf(Size(640, 480))
|
||||
fun getOutputSizes(type: Class<SurfaceTexture>) = arrayOf(Size(640, 480))
|
||||
fun getOutputSizes(type: Class<*>) = arrayOf(Size(640, 480))
|
||||
fun getOutputMinFrameDuration(format: Int, size: Size) = 33_333_333L
|
||||
}
|
||||
class CameraCharacteristics {
|
||||
@@ -41,6 +41,14 @@ class CaptureRequest {
|
||||
fun build() = CaptureRequest()
|
||||
}
|
||||
}
|
||||
object CaptureResult {
|
||||
val CONTROL_AE_TARGET_FPS_RANGE = CameraCharacteristics.Key<Range<Int>>("ae range")
|
||||
val SENSOR_FRAME_DURATION = CameraCharacteristics.Key<Long>("frame duration")
|
||||
val SENSOR_EXPOSURE_TIME = CameraCharacteristics.Key<Long>("exposure")
|
||||
val SENSOR_TIMESTAMP = CameraCharacteristics.Key<Long>("timestamp")
|
||||
}
|
||||
class TotalCaptureResult { fun <T> get(key: CameraCharacteristics.Key<T>): T? = null }
|
||||
class CaptureFailure { val frameNumber = 0L; val reason = 0; fun wasImageCaptured() = false }
|
||||
|
||||
/** Реальные callback-поколения SDK воспроизводит тест; камера/сессия закрываются независимо. */
|
||||
class CameraManager {
|
||||
@@ -86,9 +94,14 @@ class CameraDevice(val id: String, private val owner: CameraManager, private val
|
||||
var closeCalls = 0
|
||||
private set
|
||||
fun createCaptureRequest(type: Int): CaptureRequest.Builder { check("request" !in failures); return CaptureRequest.Builder() }
|
||||
fun createCaptureSession(targets: List<Surface>, callback: CameraCaptureSession.StateCallback, delivery: Handler) {
|
||||
fun createCaptureSession(targets: List<Surface>, callback: CameraCaptureSession.StateCallback, delivery: Handler) =
|
||||
createSession(targets, callback, delivery, highSpeed = false)
|
||||
fun createConstrainedHighSpeedCaptureSession(targets: List<Surface>, callback: CameraCaptureSession.StateCallback, delivery: Handler) =
|
||||
createSession(targets, callback, delivery, highSpeed = true)
|
||||
private fun createSession(targets: List<Surface>, callback: CameraCaptureSession.StateCallback, delivery: Handler, highSpeed: Boolean) {
|
||||
check("session" !in failures) { "session creation failed" }
|
||||
val session = CameraCaptureSession(this, callback, delivery).also { it.failures = owner.sessionFailures.poll() ?: owner.nextSessionFailures }
|
||||
val session = (if (highSpeed) CameraConstrainedHighSpeedCaptureSession(this, callback, delivery)
|
||||
else CameraCaptureSession(this, callback, delivery)).also { it.failures = owner.sessionFailures.poll() ?: owner.nextSessionFailures }
|
||||
owner.sessions.add(session)
|
||||
if (owner.autoConfigured) delivery.post {
|
||||
if ("configure" in session.failures || "session after" in failures) session.failConfiguration()
|
||||
@@ -106,7 +119,11 @@ class CameraDevice(val id: String, private val owner: CameraManager, private val
|
||||
check("close after" !in failures) { "camera close failed after effect" }
|
||||
}
|
||||
}
|
||||
class CameraCaptureSession(val device: CameraDevice, val callback: StateCallback, private val handler: Handler) {
|
||||
open class CameraCaptureSession(val device: CameraDevice, val callback: StateCallback, private val handler: Handler) {
|
||||
open class CaptureCallback {
|
||||
open fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {}
|
||||
open fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failed: CaptureFailure) {}
|
||||
}
|
||||
abstract class StateCallback {
|
||||
abstract fun onConfigured(session: CameraCaptureSession)
|
||||
abstract fun onConfigureFailed(session: CameraCaptureSession)
|
||||
@@ -127,3 +144,9 @@ class CameraCaptureSession(val device: CameraDevice, val callback: StateCallback
|
||||
closed = true; repeating = false; handler.post { callback.onClosed(this) }
|
||||
}
|
||||
}
|
||||
class CameraConstrainedHighSpeedCaptureSession(device: CameraDevice, callback: CameraCaptureSession.StateCallback, handler: Handler) :
|
||||
CameraCaptureSession(device, callback, handler) {
|
||||
fun createHighSpeedRequestList(request: CaptureRequest) = listOf(request)
|
||||
fun setRepeatingBurst(requests: List<CaptureRequest>, callback: CaptureCallback, handler: Handler) =
|
||||
setRepeatingRequest(requests.first(), callback, handler)
|
||||
}
|
||||
@@ -10,18 +10,21 @@ class MediaFormat {
|
||||
companion object {
|
||||
const val KEY_ROTATION = "rotation"; const val KEY_MAX_INPUT_SIZE = "max-input-size"
|
||||
const val KEY_LOW_LATENCY = "low-latency"
|
||||
const val KEY_COLOR_FORMAT = "color"; const val KEY_PROFILE = "profile"; const val KEY_BIT_RATE = "bitrate"
|
||||
const val KEY_FRAME_RATE = "fps"; const val KEY_I_FRAME_INTERVAL = "interval"; const val KEY_MAX_B_FRAMES = "bframes"
|
||||
fun createVideoFormat(mime: String, width: Int, height: Int) = MediaFormat()
|
||||
}
|
||||
val values = mutableMapOf<String, Any>()
|
||||
fun setInteger(key: String, value: Int) { values[key] = value }
|
||||
fun setByteBuffer(key: String, value: ByteBuffer) { values[key] = value }
|
||||
fun getByteBuffer(key: String) = values[key] as? ByteBuffer
|
||||
}
|
||||
class MediaCodecInfo(val name: String = "host.decoder") {
|
||||
val isEncoder = false
|
||||
val supportedTypes = arrayOf("video/avc", "video/hevc")
|
||||
fun getCapabilitiesForType(type: String) = CodecCapabilities()
|
||||
class CodecCapabilities {
|
||||
companion object { const val FEATURE_LowLatency = "low-latency" }
|
||||
companion object { const val FEATURE_LowLatency = "low-latency"; const val COLOR_FormatSurface = 0x7f000789 }
|
||||
fun isFormatSupported(format: MediaFormat) = true
|
||||
fun isFeatureSupported(name: String) = false
|
||||
}
|
||||
@@ -38,6 +41,8 @@ class MediaCodecList(kind: Int) {
|
||||
class MediaCodec private constructor(val name: String) {
|
||||
companion object {
|
||||
const val BUFFER_FLAG_END_OF_STREAM = 4; const val INFO_OUTPUT_FORMAT_CHANGED = -2
|
||||
const val BUFFER_FLAG_CODEC_CONFIG = 2; const val BUFFER_FLAG_PARTIAL_FRAME = 8; const val BUFFER_FLAG_KEY_FRAME = 1
|
||||
const val CONFIGURE_FLAG_ENCODE = 1; const val PARAMETER_KEY_VIDEO_BITRATE = "bitrate"; const val PARAMETER_KEY_REQUEST_SYNC_FRAME = "sync"
|
||||
val created = CopyOnWriteArrayList<MediaCodec>()
|
||||
@Volatile var nextFailures = emptySet<String>()
|
||||
@Volatile var onInput: () -> Unit = {}
|
||||
@@ -46,7 +51,18 @@ class MediaCodec private constructor(val name: String) {
|
||||
return MediaCodec(name).also { created.add(it) }
|
||||
}
|
||||
}
|
||||
class BufferInfo { var size = 0; var flags = 0; var presentationTimeUs = 0L }
|
||||
class BufferInfo { var size = 0; var flags = 0; var presentationTimeUs = 0L; var offset = 0 }
|
||||
class CodecException(val diagnosticInfo: String) : Exception(diagnosticInfo)
|
||||
abstract class Callback {
|
||||
abstract fun onInputBufferAvailable(codec: MediaCodec, index: Int)
|
||||
abstract fun onError(codec: MediaCodec, error: CodecException)
|
||||
abstract fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat)
|
||||
abstract fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: BufferInfo)
|
||||
}
|
||||
var callback: Callback? = null
|
||||
private set
|
||||
private var callbackHandler: android.os.Handler? = null
|
||||
private val encoded = mutableMapOf<Int, ByteArray>()
|
||||
private data class Frame(val pts: Long, val size: Int, val flags: Int)
|
||||
private val queued = ConcurrentLinkedQueue<Frame>()
|
||||
private val pending = mutableMapOf<Int, Frame>()
|
||||
@@ -63,6 +79,8 @@ class MediaCodec private constructor(val name: String) {
|
||||
private set
|
||||
var configuredSurface: Surface? = null
|
||||
private set
|
||||
var inputSurface: Surface? = null
|
||||
private set
|
||||
var renderedPts = ConcurrentLinkedQueue<Long>()
|
||||
val outputFormat = MediaFormat()
|
||||
private fun access(operation: String) {
|
||||
@@ -73,6 +91,19 @@ class MediaCodec private constructor(val name: String) {
|
||||
access("configure"); configuredSurface = surface; access("configure after")
|
||||
}
|
||||
fun start() { access("start"); started = true; access("start after") }
|
||||
fun setCallback(value: Callback, handler: android.os.Handler) {
|
||||
access("callback"); callback = value; callbackHandler = handler; access("callback after")
|
||||
}
|
||||
fun createInputSurface(): Surface { access("input surface"); return Surface().also { inputSurface = it } }
|
||||
fun setParameters(bundle: android.os.Bundle) { access("parameters") }
|
||||
fun emitFormat(format: MediaFormat) { callbackHandler!!.post { callback!!.onOutputFormatChanged(this, format) } }
|
||||
fun emitFrame(data: ByteArray, pts: Long = System.nanoTime() / 1000, flags: Int = BUFFER_FLAG_KEY_FRAME) {
|
||||
callbackHandler!!.post {
|
||||
val index = ++index; encoded[index] = data
|
||||
callback!!.onOutputBufferAvailable(this, index, BufferInfo().also { it.presentationTimeUs = pts; it.size = data.size; it.flags = flags })
|
||||
}
|
||||
}
|
||||
fun getOutputBuffer(index: Int): ByteBuffer = ByteBuffer.wrap(checkNotNull(encoded[index]))
|
||||
fun stop() { stopCalls++; access("stop"); started = false }
|
||||
fun release() {
|
||||
releaseCalls++; check("release" !in failures) { "decoder release failed" }
|
||||
@@ -94,6 +125,7 @@ class MediaCodec private constructor(val name: String) {
|
||||
}
|
||||
fun releaseOutputBuffer(index: Int, render: Boolean) {
|
||||
access("render")
|
||||
if (encoded.remove(index) != null) return
|
||||
val frame = checkNotNull(pending.remove(index))
|
||||
if (render) renderedPts.add(frame.pts)
|
||||
}
|
||||
|
||||
@@ -64,3 +64,4 @@ object SystemClock {
|
||||
}
|
||||
object Build { object VERSION { const val SDK_INT = 36 } }
|
||||
object Process { const val THREAD_PRIORITY_AUDIO = -16; fun setThreadPriority(value: Int) {} }
|
||||
class Bundle { val values = mutableMapOf<String, Int>(); fun putInt(key: String, value: Int) { values[key] = value } }
|
||||
Reference in new issue
Block a user