mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-11 00:12:24 +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() {
|
override fun onProjectionStopped() {
|
||||||
// Before the car UI goes, while the media connection is still up.
|
// Before the car UI goes, while the media connection is still up.
|
||||||
app.seb3thehacker.gearslip.car.CarServices.pausePhoneMedia()
|
app.seb3thehacker.gearslip.car.CarServices.pausePhoneMedia()
|
||||||
|
|||||||
@@ -1189,6 +1189,7 @@ class GearslipRunner(
|
|||||||
onFrame = { data, presentationTimeUs, keyFrame ->
|
onFrame = { data, presentationTimeUs, keyFrame ->
|
||||||
sendVideoFrame(data, presentationTimeUs, keyFrame)
|
sendVideoFrame(data, presentationTimeUs, keyFrame)
|
||||||
},
|
},
|
||||||
|
onStill = { projection?.onStill() },
|
||||||
).also { source ->
|
).also { source ->
|
||||||
runCatching {
|
runCatching {
|
||||||
source.start()
|
source.start()
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ interface Projection {
|
|||||||
fun onTouch(action: Int, actionIndex: Int, points: List<TouchPoint>)
|
fun onTouch(action: Int, actionIndex: Int, points: List<TouchPoint>)
|
||||||
|
|
||||||
fun onProjectionStopped()
|
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. */
|
/** 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
|
* 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.
|
* 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?) {
|
fun setSurface(surface: Surface?) {
|
||||||
virtualDisplay?.surface = surface
|
virtualDisplay?.surface = surface
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ class VideoSource(
|
|||||||
private val wireless: Boolean = false,
|
private val wireless: Boolean = false,
|
||||||
private val onCodecConfig: (ByteArray) -> Unit,
|
private val onCodecConfig: (ByteArray) -> Unit,
|
||||||
private val onFrame: (data: ByteArray, presentationTimeUs: Long, keyFrame: Boolean) -> 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")
|
private val log = GearslipLog.tagged("VIDEO")
|
||||||
|
|
||||||
@@ -170,6 +176,8 @@ class VideoSource(
|
|||||||
private fun pump(encoder: MediaCodec) {
|
private fun pump(encoder: MediaCodec) {
|
||||||
val info = MediaCodec.BufferInfo()
|
val info = MediaCodec.BufferInfo()
|
||||||
val frameIntervalUs = 1_000_000L / frameRate
|
val frameIntervalUs = 1_000_000L / frameRate
|
||||||
|
var lastOutputAt = 0L
|
||||||
|
var lastNudgeAt = 0L
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
// SURFACE mode has no input buffers to fill - frames arrive from the display.
|
// SURFACE mode has no input buffers to fill - frames arrive from the display.
|
||||||
@@ -208,8 +216,16 @@ class VideoSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
encoder.releaseOutputBuffer(outIndex, false)
|
encoder.releaseOutputBuffer(outIndex, false)
|
||||||
|
lastOutputAt = System.currentTimeMillis()
|
||||||
outIndex = encoder.dequeueOutputBuffer(info, 0)
|
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))
|
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 MIME = MediaFormat.MIMETYPE_VIDEO_AVC
|
||||||
const val TIMEOUT_US = 10_000L
|
const val TIMEOUT_US = 10_000L
|
||||||
const val SURFACE_TIMEOUT_US = 100_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
|
* 800x480 at 30fps gets 8 Mbps: about the most that size can show, enough that a moving
|
||||||
|
|||||||
Reference in new issue
Block a user