mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-10 16:02:25 +01:00
A still car screen gets a fresh frame every second
A surface encoder only makes a frame when the screen changes, and the repeat-last-frame setting should cover a still one. On a Pixel 10 it didn't: a LuChi box got 11 frames, then nothing, and stayed black (#58). When the encoder has been quiet for a second, Gearslip now draws its screen again, which is a new frame for the car.
This commit is contained in:
1 parent
a70dea7bd5
commit
c33875c8f8
5 files changed
+33
No files matched your search
@@ -526,6 +526,10 @@ class GearslipActivity : ComponentActivity(), Projection {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStill() {
|
||||
screenProjector?.redraw()
|
||||
}
|
||||
|
||||
override fun onProjectionStopped() {
|
||||
// Before the car UI goes, while the media connection is still up.
|
||||
app.seb3thehacker.gearslip.car.CarServices.pausePhoneMedia()
|
||||
|
||||
@@ -1189,6 +1189,7 @@ class GearslipRunner(
|
||||
onFrame = { data, presentationTimeUs, keyFrame ->
|
||||
sendVideoFrame(data, presentationTimeUs, keyFrame)
|
||||
},
|
||||
onStill = { projection?.onStill() },
|
||||
).also { source ->
|
||||
runCatching {
|
||||
source.start()
|
||||
|
||||
@@ -24,6 +24,12 @@ interface Projection {
|
||||
fun onTouch(action: Int, actionIndex: Int, points: List<TouchPoint>)
|
||||
|
||||
fun onProjectionStopped()
|
||||
|
||||
/**
|
||||
* The screen has been still for a moment and the encoder has sent nothing since. Draw it once
|
||||
* more, so the car gets a fresh frame (see [VideoSource]'s onStill).
|
||||
*/
|
||||
fun onStill() {}
|
||||
}
|
||||
|
||||
/** One finger on the car's screen: a stable [id] for as long as it stays down, and where it is. */
|
||||
|
||||
@@ -223,6 +223,11 @@ class ScreenProjector(private val context: Context) {
|
||||
* Points the display at another surface, or none, without restarting what it shows. The
|
||||
* preview's SurfaceView comes and goes as the phone turns; the car UI on it carries on.
|
||||
*/
|
||||
/** Draws the screen again as it is, which hands the display's consumer a new frame. */
|
||||
fun redraw() {
|
||||
root?.let { it.post { it.invalidate() } }
|
||||
}
|
||||
|
||||
fun setSurface(surface: Surface?) {
|
||||
virtualDisplay?.surface = surface
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ class VideoSource(
|
||||
private val wireless: Boolean = false,
|
||||
private val onCodecConfig: (ByteArray) -> Unit,
|
||||
private val onFrame: (data: ByteArray, presentationTimeUs: Long, keyFrame: Boolean) -> Unit,
|
||||
/**
|
||||
* SURFACE mode, nothing encoded for [STILL_MS]. The repeat setting below should cover a still
|
||||
* screen, but on a Pixel 10 it didn't: a LuChi box got 11 frames, then none, and stayed black
|
||||
* (issue #58). Whoever draws the screen can draw it again, and that is a frame.
|
||||
*/
|
||||
private val onStill: () -> Unit = {},
|
||||
) {
|
||||
private val log = GearslipLog.tagged("VIDEO")
|
||||
|
||||
@@ -170,6 +176,8 @@ class VideoSource(
|
||||
private fun pump(encoder: MediaCodec) {
|
||||
val info = MediaCodec.BufferInfo()
|
||||
val frameIntervalUs = 1_000_000L / frameRate
|
||||
var lastOutputAt = 0L
|
||||
var lastNudgeAt = 0L
|
||||
|
||||
while (running) {
|
||||
// SURFACE mode has no input buffers to fill - frames arrive from the display.
|
||||
@@ -208,8 +216,16 @@ class VideoSource(
|
||||
}
|
||||
}
|
||||
encoder.releaseOutputBuffer(outIndex, false)
|
||||
lastOutputAt = System.currentTimeMillis()
|
||||
outIndex = encoder.dequeueOutputBuffer(info, 0)
|
||||
}
|
||||
if (mode == Mode.SURFACE && lastOutputAt > 0) {
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastOutputAt > STILL_MS && now - lastNudgeAt > STILL_MS) {
|
||||
lastNudgeAt = now
|
||||
onStill()
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != Mode.SURFACE) Thread.sleep(maxOf(1L, frameIntervalUs / 2000))
|
||||
}
|
||||
@@ -256,6 +272,7 @@ class VideoSource(
|
||||
const val MIME = MediaFormat.MIMETYPE_VIDEO_AVC
|
||||
const val TIMEOUT_US = 10_000L
|
||||
const val SURFACE_TIMEOUT_US = 100_000L
|
||||
private const val STILL_MS = 1_000L
|
||||
|
||||
/**
|
||||
* 800x480 at 30fps gets 8 Mbps: about the most that size can show, enough that a moving
|
||||
|
||||
Reference in new issue
Block a user