@@ -84,6 +84,40 @@ private static int clampBitrate(int bps) {
8484 return Math .max (MIN_BITRATE , Math .min (MAX_BITRATE , bps ));
8585 }
8686
87+ // H.264 encoders only accept dimensions that are a multiple of their
88+ // alignment (usually 2); a raw (display * scale) can be odd. Round down.
89+ private static int alignDown (float size , int alignment ) {
90+ int align = Math .max (alignment , 2 );
91+ return ((int ) size / align ) * align ;
92+ }
93+
94+ /**
95+ * Returns the largest {width, height} the encoder supports that is no bigger
96+ * than the one asked for and keeps its aspect ratio, or null if there is none.
97+ * Encoders often report their limits for landscape only (e.g. 4096x2176), so a
98+ * tall portrait screen can exceed the maximum height while its area is well
99+ * within what the encoder handles; it is then scaled down to fit.
100+ */
101+ static int [] fitToEncoder (MediaCodecInfo .VideoCapabilities caps , float width , float height ) {
102+ int maxWidth = caps .getSupportedWidths ().getUpper ();
103+ int maxHeight = caps .getSupportedHeights ().getUpper ();
104+ float factor = Math .min (1.0f , Math .min (maxWidth / width , maxHeight / height ));
105+ // past the width/height limits there can still be a block count or aspect
106+ // ratio limit; shrink a little at a time until the encoder takes it
107+ for (int attempt = 0 ; attempt < 100 ; attempt ++) {
108+ int w = alignDown (width * factor , caps .getWidthAlignment ());
109+ int h = alignDown (height * factor , caps .getHeightAlignment ());
110+ if (w <= 0 || h <= 0 ) {
111+ return null ;
112+ }
113+ if (caps .isSizeSupported (w , h )) {
114+ return new int [] {w , h };
115+ }
116+ factor *= 0.95f ;
117+ }
118+ return null ;
119+ }
120+
87121 private void startControlServer (MediaCodec codec , DisplayUtils .DisplayMirror mirror ) {
88122 new JsonRpcSocketServer (CONTROL_SOCKET , (method , params ) -> {
89123 switch (method ) {
@@ -127,23 +161,27 @@ private void stream() throws Exception {
127161 MediaCodecInfo .VideoCapabilities caps = codec .getCodecInfo ()
128162 .getCapabilitiesForType (MediaFormat .MIMETYPE_VIDEO_AVC ).getVideoCapabilities ();
129163
130- // H.264 encoders only accept dimensions that are a multiple of their
131- // alignment (usually 2); a raw (display * scale) can be odd. Round down.
132- int widthAlign = Math .max (caps .getWidthAlignment (), 2 );
133- int heightAlign = Math .max (caps .getHeightAlignment (), 2 );
134- int width = ((int ) (display .width * scale ) / widthAlign ) * widthAlign ;
135- int height = ((int ) (display .height * scale ) / heightAlign ) * heightAlign ;
136-
137- if (width <= 0 || height <= 0 ) {
164+ int requestedWidth = alignDown (display .width * scale , caps .getWidthAlignment ());
165+ int requestedHeight = alignDown (display .height * scale , caps .getHeightAlignment ());
166+ if (requestedWidth <= 0 || requestedHeight <= 0 ) {
138167 codec .release ();
139- throw new IllegalArgumentException ("Invalid dimensions: " + width + "x" + height );
168+ throw new IllegalArgumentException ("Invalid dimensions: " + requestedWidth + "x" + requestedHeight );
140169 }
141- if (!caps .isSizeSupported (width , height )) {
170+
171+ int [] size = fitToEncoder (caps , display .width * scale , display .height * scale );
172+ if (size == null ) {
142173 String max = caps .getSupportedWidths ().getUpper () + "x" + caps .getSupportedHeights ().getUpper ();
143174 codec .release ();
144- throw new IllegalArgumentException ("Video dimensions " + width + "x" + height
145- + " exceed codec capabilities. Maximum supported: " + max
146- + ". Try using --scale parameter to reduce resolution (e.g., --scale 0.5)" );
175+ throw new IllegalArgumentException ("Video dimensions " + requestedWidth + "x" + requestedHeight
176+ + " are not supported by the encoder, and no smaller size with the same aspect ratio is."
177+ + " Maximum supported: " + max );
178+ }
179+ int width = size [0 ];
180+ int height = size [1 ];
181+ if (width != requestedWidth || height != requestedHeight ) {
182+ // not on stderr: adb exec-out merges it into the video stream
183+ Log .i (TAG , "Encoder does not support " + requestedWidth + "x" + requestedHeight
184+ + ", encoding at " + width + "x" + height );
147185 }
148186
149187 MediaFormat format = MediaFormat .createVideoFormat (MediaFormat .MIMETYPE_VIDEO_AVC , width , height );
0 commit comments