diff --git a/README.md b/README.md index db531c61..7f6af3dd 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Its features include: ## Requirements -The Android device requires at least API 21 (Android 5.0). +The Android device requires at least API 19 (Android 4.4). Make sure you [enable adb debugging][enable-adb] on your device(s). diff --git a/server/build.gradle b/server/build.gradle index dbc8261f..42387d57 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 31 defaultConfig { applicationId "com.genymobile.scrcpy" - minSdkVersion 21 + minSdkVersion 19 targetSdkVersion 31 versionCode 12400 versionName "1.24" diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index e95896d3..f65fd4a0 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -144,9 +144,11 @@ public class ScreenEncoder implements Device.RotationListener { return 0; } + @SuppressWarnings("deprecation") // Android API 19 requires to call deprecated methods private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException { boolean eof = false; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + ByteBuffer[] cachedOutputBuffers = null; while (!consumeRotationChange() && !eof) { int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1); @@ -157,7 +159,15 @@ public class ScreenEncoder implements Device.RotationListener { break; } if (outputBufferId >= 0) { - ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId); + ByteBuffer codecBuffer; + if (Build.VERSION.SDK_INT >= 21) { + codecBuffer = codec.getOutputBuffer(outputBufferId); + } else { + if (cachedOutputBuffers == null) { + cachedOutputBuffers = codec.getOutputBuffers(); + } + codecBuffer = cachedOutputBuffers[outputBufferId]; + } if (sendFrameMeta) { writeFrameMeta(fd, bufferInfo, codecBuffer.remaining()); @@ -168,6 +178,8 @@ public class ScreenEncoder implements Device.RotationListener { // If this is not a config packet, then it contains a frame firstFrameSent = true; } + } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + cachedOutputBuffers = null; } } finally { if (outputBufferId >= 0) { @@ -201,10 +213,25 @@ public class ScreenEncoder implements Device.RotationListener { IO.writeFully(fd, headerBuffer); } + @SuppressWarnings("deprecation") // Android API 19 requires to call deprecated methods + private static MediaCodecInfo[] getCodecInfosCompat() { + if (Build.VERSION.SDK_INT >= 21) { + MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + return list.getCodecInfos(); + } else { + final int size = MediaCodecList.getCodecCount(); + final MediaCodecInfo[] infos = new MediaCodecInfo[size]; + for (int i = 0; i < size; i++) { + infos[i] = MediaCodecList.getCodecInfoAt(i); + } + return infos; + } + } + private static MediaCodecInfo[] listEncoders() { List result = new ArrayList<>(); - MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS); - for (MediaCodecInfo codecInfo : list.getCodecInfos()) { + MediaCodecInfo[] codecInfos = getCodecInfosCompat(); + for (MediaCodecInfo codecInfo : codecInfos) { if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) { result.add(codecInfo); }