Skip to content

Commit 78b5fb5

Browse files
authored
fix: fixed avc latency, timeouts, proper shutdown and repeating frames bug
2 parents b2d52eb + e92027a commit 78b5fb5

File tree

1 file changed

+39
-14
lines changed

1 file changed

+39
-14
lines changed

app/src/main/java/com/mobilenext/devicekit/AvcServer.kt

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,12 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
6161
"--fps" -> {
6262
if (i + 1 < args.size) {
6363
val parsedFps = args[i + 1].toIntOrNull()
64-
if (parsedFps == null) {
65-
throw IllegalArgumentException("Invalid fps value: ${args[i + 1]}. Must be an integer between $MIN_FPS and $MAX_FPS")
66-
}
67-
if (parsedFps < MIN_FPS || parsedFps > MAX_FPS) {
68-
throw IllegalArgumentException("fps value out of range: $parsedFps. Must be between $MIN_FPS and $MAX_FPS")
64+
fps = if (parsedFps != null && parsedFps in MIN_FPS..MAX_FPS) {
65+
parsedFps
66+
} else {
67+
Log.w(TAG, "Invalid fps value: ${args[i + 1]}. Using default: $DEFAULT_FPS")
68+
DEFAULT_FPS
6969
}
70-
fps = parsedFps
7170
i++
7271
}
7372
}
@@ -103,6 +102,33 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
103102
shutdownLatch.countDown()
104103
}
105104

105+
private fun cleanupResources(
106+
stdoutChannel: java.nio.channels.FileChannel?,
107+
codec: MediaCodec?,
108+
virtualDisplay: VirtualDisplay?
109+
) {
110+
try {
111+
stdoutChannel?.close()
112+
} catch (e: Exception) {
113+
Log.e(TAG, "Error closing stdout channel", e)
114+
}
115+
try {
116+
codec?.stop()
117+
} catch (e: Exception) {
118+
Log.e(TAG, "Error stopping codec", e)
119+
}
120+
try {
121+
codec?.release()
122+
} catch (e: Exception) {
123+
Log.e(TAG, "Error releasing codec", e)
124+
}
125+
try {
126+
virtualDisplay?.release()
127+
} catch (e: Exception) {
128+
Log.e(TAG, "Error releasing virtual display", e)
129+
}
130+
}
131+
106132
private fun streamAvcFrames() {
107133
val displayInfo = DisplayUtils.getDisplayInfo()
108134
val scaledWidth = (displayInfo.width * scale).toInt()
@@ -163,6 +189,8 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
163189
// Low latency settings
164190
setInteger(MediaFormat.KEY_LATENCY, 0) // Request lowest latency
165191
setInteger(MediaFormat.KEY_PRIORITY, 0) // Realtime priority
192+
// Repeat previous frame after 100ms to keep stream alive when screen is static
193+
setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 100_000L) // 100ms in microseconds
166194
}
167195

168196
Log.d(TAG, "MediaFormat created: $format")
@@ -206,7 +234,7 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
206234
Log.d(TAG, "AVC encoder started")
207235

208236
val bufferInfo = MediaCodec.BufferInfo()
209-
val timeout = 10000L // 10ms timeout for lower latency
237+
val timeout = 100_000L // 100ms timeout for responsive shutdown (matches REPEAT_FRAME_DELAY)
210238

211239
// Get FileChannel for stdout to write directly from ByteBuffer (zero-copy)
212240
val stdoutChannel = FileOutputStream(FileDescriptor.out).channel
@@ -243,9 +271,9 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
243271
}
244272
} catch (e: IOException) {
245273
// Pipe broken - client disconnected
246-
Log.d(TAG, "Output pipe broken, shutting down")
247-
shutdown()
248-
break
274+
Log.d(TAG, "Output pipe broken, cleaning up and exiting")
275+
cleanupResources(stdoutChannel, codec, virtualDisplay)
276+
exitProcess(0)
249277
}
250278

251279
// Log frame info
@@ -297,10 +325,7 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
297325
}
298326
} finally {
299327
Log.d(TAG, "Stopping AVC encoder")
300-
stdoutChannel.close()
301-
codec.stop()
302-
codec.release()
303-
virtualDisplay.release()
328+
cleanupResources(stdoutChannel, codec, virtualDisplay)
304329
}
305330
}
306331
}

0 commit comments

Comments
 (0)