Skip to content
This repository was archived by the owner on Sep 15, 2026. It is now read-only.

Commit b753c38

Browse files
authored
fix(avc): align scaled capture dimensions to encoder alignment (#42)
1 parent 7976626 commit b753c38

1 file changed

Lines changed: 17 additions & 11 deletions

File tree

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,26 +194,32 @@ class AvcServer(private val bitrate: Int, private val scale: Float, private val
194194

195195
private fun streamAvcFrames() {
196196
val displayInfo = DisplayUtils.getDisplayInfo()
197-
val scaledWidth = (displayInfo.width * scale).toInt()
198-
val scaledHeight = (displayInfo.height * scale).toInt()
197+
198+
// Check codec capabilities before attempting to configure
199+
val codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
200+
val codecInfo = codec.codecInfo
201+
val capabilities = codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC)
202+
val videoCapabilities = capabilities.videoCapabilities
203+
204+
// H.264 encoders only accept dimensions that are a multiple of their
205+
// alignment (usually 2). A raw (display * scale).toInt() can be odd —
206+
// e.g. Pixel 10 Pro's 2410-high display at scale 0.5 gives 1205 — and
207+
// isSizeSupported() then rejects the size outright, killing the stream.
208+
// Round each dimension down to the codec's alignment.
209+
val widthAlign = maxOf(videoCapabilities.widthAlignment, 2)
210+
val heightAlign = maxOf(videoCapabilities.heightAlignment, 2)
211+
val scaledWidth = ((displayInfo.width * scale).toInt() / widthAlign) * widthAlign
212+
val scaledHeight = ((displayInfo.height * scale).toInt() / heightAlign) * heightAlign
199213

200214
Log.d(TAG, "Starting AVC stream: ${displayInfo.width}x${displayInfo.height} -> ${scaledWidth}x${scaledHeight}")
201215
Log.d(TAG, "Configuration: bitrate=$bitrate, fps=$fps, I-frame interval=${I_FRAME_INTERVAL}s")
202216
Log.d(TAG, "Scaled dimensions: width=$scaledWidth, height=$scaledHeight")
203217

204218
// Validate dimensions
205219
if (scaledWidth <= 0 || scaledHeight <= 0) {
220+
codec.release()
206221
throw IllegalArgumentException("Invalid dimensions: ${scaledWidth}x${scaledHeight}")
207222
}
208-
if (scaledWidth % 2 != 0 || scaledHeight % 2 != 0) {
209-
Log.w(TAG, "Warning: Dimensions not divisible by 2, may cause issues: ${scaledWidth}x${scaledHeight}")
210-
}
211-
212-
// Check codec capabilities before attempting to configure
213-
val codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
214-
val codecInfo = codec.codecInfo
215-
val capabilities = codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC)
216-
val videoCapabilities = capabilities.videoCapabilities
217223

218224
Log.d(TAG, "Codec capabilities:")
219225
Log.d(TAG, " Supported widths: ${videoCapabilities.supportedWidths}")

0 commit comments

Comments
 (0)