feat: Add FPS Counter to Android (enableFpsGraph) (#2460)

* feat: Add FPS Counter to Android (`enableFpsGraph`)

* feat: Add FPS View

* Update FpsCounterView.kt

* Implement actual graph

* fix layout

* Update FpsGraphView.kt

* Update CameraPage.tsx
This commit is contained in:
Marc Rousavy 2024-01-30 15:28:18 +01:00 committed by GitHub
parent 8c5b60355f
commit 9089014ed8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 143 additions and 2 deletions

View File

@ -81,22 +81,28 @@ class CameraView(context: Context) :
previewView.resizeMode = value previewView.resizeMode = value
field = value field = value
} }
var enableFpsGraph: Boolean = false
set(value) {
field = value
updateFpsGraph()
}
// code scanner // code scanner
var codeScannerOptions: CodeScannerOptions? = null var codeScannerOptions: CodeScannerOptions? = null
// private properties // private properties
private var isMounted = false private var isMounted = false
private val coroutineScope = CoroutineScope(CameraQueues.cameraQueue.coroutineDispatcher)
internal val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager internal val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
// session // session
internal val cameraSession: CameraSession internal val cameraSession: CameraSession
private val previewView: PreviewView private val previewView: PreviewView
private var currentConfigureCall: Long = System.currentTimeMillis() private var currentConfigureCall: Long = System.currentTimeMillis()
internal var frameProcessor: FrameProcessor? = null internal var frameProcessor: FrameProcessor? = null
private val coroutineScope = CoroutineScope(CameraQueues.cameraQueue.coroutineDispatcher) // other
private var fpsGraph: FpsGraphView? = null
init { init {
this.installHierarchyFitter() this.installHierarchyFitter()
@ -225,8 +231,22 @@ class CameraView(context: Context) :
} }
} }
private fun updateFpsGraph() {
if (enableFpsGraph) {
if (fpsGraph != null) return
fpsGraph = FpsGraphView(context)
addView(fpsGraph)
} else {
if (fpsGraph == null) return
removeView(fpsGraph)
fpsGraph = null
}
}
override fun onFrame(frame: Frame) { override fun onFrame(frame: Frame) {
frameProcessor?.call(frame) frameProcessor?.call(frame)
fpsGraph?.onTick()
} }
override fun onError(error: Throwable) { override fun onError(error: Throwable) {

View File

@ -79,6 +79,11 @@ class CameraViewManager : ViewGroupManager<CameraView>() {
view.enableZoomGesture = enableZoomGesture view.enableZoomGesture = enableZoomGesture
} }
@ReactProp(name = "enableFpsGraph")
fun setEnableFpsGraph(view: CameraView, enableFpsGraph: Boolean) {
view.enableFpsGraph = enableFpsGraph
}
@ReactProp(name = "videoStabilizationMode") @ReactProp(name = "videoStabilizationMode")
fun setVideoStabilizationMode(view: CameraView, videoStabilizationMode: String?) { fun setVideoStabilizationMode(view: CameraView, videoStabilizationMode: String?) {
val newMode = VideoStabilizationMode.fromUnionValue(videoStabilizationMode) val newMode = VideoStabilizationMode.fromUnionValue(videoStabilizationMode)

View File

@ -0,0 +1,116 @@
package com.mrousavy.camera
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import kotlin.math.max
import kotlin.math.roundToInt
@SuppressLint("SetTextI18n")
class FpsGraphView(context: Context) : FrameLayout(context) {
private val textView = TextView(context)
private val graph = Graph(context)
init {
textView.textSize = 18f
textView.setTextColor(Color.WHITE)
textView.text = "0 FPS"
graph.callback = object : Graph.Callback {
override fun onAverageFpsChanged(averageFps: Int) {
textView.text = "$averageFps FPS"
}
}
val layoutParams = LayoutParams(300, 150)
layoutParams.setMargins(15, 150, 0, 0)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
addView(graph, layoutParams)
addView(textView, layoutParams)
}
fun onTick() {
graph.onTick()
}
class Graph(context: Context) :
View(context),
Runnable {
private val maxBars = 30
private val ticks = arrayListOf<Double>()
private val bars = arrayListOf<Int>()
private val paint = Paint().apply {
color = Color.RED
strokeWidth = 5f
style = Paint.Style.FILL
}
var callback: Callback? = null
init {
post(this)
}
override fun run() {
val averageFps = getAverageFps(ticks)
ticks.clear()
bars.add(averageFps)
if (bars.size > maxBars) {
bars.removeAt(0)
}
invalidate()
postDelayed(this, 1000)
callback?.onAverageFpsChanged(averageFps)
}
private fun getAverageFps(ticks: List<Double>): Int {
if (ticks.isEmpty()) return 0
if (ticks.size < 2) return 1
val totalDuration = ticks.last() - ticks.first()
val averageFrameDuration = totalDuration / (ticks.size - 1).toDouble()
val double = 1000.0 / averageFrameDuration
return double.roundToInt()
}
fun onTick() {
val currentTick = System.currentTimeMillis()
ticks.add(currentTick.toDouble())
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (bars.size < 2) return
val maxFps = max(bars.max().toFloat(), 60f)
val blockWidth = width.toFloat() / maxBars
for (i in 0..maxBars) {
val fps = bars.getOrNull(i)
if (fps != null) {
val blockHeight = (fps / maxFps) * height
canvas.drawRect(
i * blockWidth,
height - blockHeight,
(i + 1) * blockWidth,
height.toFloat(),
paint
)
}
}
}
interface Callback {
fun onAverageFpsChanged(averageFps: Int)
}
}
}