From 504d6a42d543c83a50e1d24fe247ff1c64563232 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Thu, 12 Mar 2020 15:59:10 +0200 Subject: [PATCH 1/6] Adding codec_profile to scrcpy client default options. If the option is not requested with -c then the codec will be automatically choosen by the MediaCodec. (cherry picked from commit 43aea79f6acc69f68feaee0494f45a05bce0f782) --- app/src/cli.c | 1 + app/src/scrcpy.c | 1 + app/src/scrcpy.h | 2 ++ app/src/server.h | 1 + 4 files changed, 5 insertions(+) diff --git a/app/src/cli.c b/app/src/cli.c index 4b093c49da..df1e54afad 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -431,6 +431,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT}, {"window-borderless", no_argument, NULL, OPT_WINDOW_BORDERLESS}, + {"codec-profile", required_argument, NULL, 'P'}, {NULL, 0, NULL, 0 }, }; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 4d9ad88b0b..0057c75b83 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -286,6 +286,7 @@ scrcpy(const struct scrcpy_options *options) { .max_fps = options->max_fps, .lock_video_orientation = options->lock_video_orientation, .control = options->control, + .codec_profile = options->codec_profile, }; if (!server_start(&server, options->serial, ¶ms)) { return false; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index e29298f2c1..4d5ad9fc34 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -34,6 +34,7 @@ struct scrcpy_options { bool render_expired_frames; bool prefer_text; bool window_borderless; + uint32_t codec_profile; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -64,6 +65,7 @@ struct scrcpy_options { .render_expired_frames = false, \ .prefer_text = false, \ .window_borderless = false, \ + .codec_profile = 0, \ } bool diff --git a/app/src/server.h b/app/src/server.h index d84a5cc8c2..f16ad6b43c 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -44,6 +44,7 @@ struct server_params { uint16_t max_fps; int8_t lock_video_orientation; bool control; + uint32_t codec_profile; }; // init default values From 709a0c327a114857423c3f28eb46d3a1700af6a8 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Thu, 12 Mar 2020 16:52:46 +0200 Subject: [PATCH 2/6] Adding codecProfile to server options (cherry picked from commit d4fb6a0c19af23d0d3c57721f6ccc91e4fad67f3) (cherry picked from commit e7f205ca19033c9b227171915bf9f748b5eefe85) (cherry picked from commit ef5d72cbb785d48d412b437fee95bc8f61829680) --- .../main/java/com/genymobile/scrcpy/Options.java | 9 +++++++++ .../main/java/com/genymobile/scrcpy/Server.java | 15 +++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index d9a29452b0..2b87b952bb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -11,6 +11,7 @@ public class Options { private Rect crop; private boolean sendFrameMeta; // send PTS so that the client may record properly private boolean control; + private int codecProfile; public int getMaxSize() { return maxSize; @@ -75,4 +76,12 @@ public boolean getControl() { public void setControl(boolean control) { this.control = control; } + + public int getCodecProfile() { + return codecProfile; + } + + public void setCodecProfile(int codecProfile) { + this.codecProfile = codecProfile; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 2b0d32a239..c2e501612c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -80,8 +80,8 @@ private static Options createOptions(String... args) { "The server version (" + clientVersion + ") does not match the client " + "(" + BuildConfig.VERSION_NAME + ")"); } - if (args.length != 9) { - throw new IllegalArgumentException("Expecting 9 parameters"); + if (args.length != 10) { + throw new IllegalArgumentException("Expecting 10 parameters"); } Options options = new Options(); @@ -98,17 +98,20 @@ private static Options createOptions(String... args) { int lockedVideoOrientation = Integer.parseInt(args[4]); options.setLockedVideoOrientation(lockedVideoOrientation); + int codecProfile = Integer.parseInt(args[5]); + options.setCodecProfile(codecProfile); + // use "adb forward" instead of "adb tunnel"? (so the server must listen) - boolean tunnelForward = Boolean.parseBoolean(args[5]); + boolean tunnelForward = Boolean.parseBoolean(args[6]); options.setTunnelForward(tunnelForward); - Rect crop = parseCrop(args[6]); + Rect crop = parseCrop(args[7]); options.setCrop(crop); - boolean sendFrameMeta = Boolean.parseBoolean(args[7]); + boolean sendFrameMeta = Boolean.parseBoolean(args[8]); options.setSendFrameMeta(sendFrameMeta); - boolean control = Boolean.parseBoolean(args[8]); + boolean control = Boolean.parseBoolean(args[9]); options.setControl(control); return options; From a9685d3b564a82c43e83b74923e39ccaa2f32fb4 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Thu, 12 Mar 2020 16:56:27 +0200 Subject: [PATCH 3/6] Adding codecProfile and setCodecProfile to the ScreenEncoder (cherry picked from commit 25052420394fda937db5973290f564e5c6b13ba5) --- .../com/genymobile/scrcpy/ScreenEncoder.java | 25 ++++++++++++++++--- .../java/com/genymobile/scrcpy/Server.java | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index 3e9772ee05..b886affb9b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -30,19 +30,21 @@ public class ScreenEncoder implements Device.RotationListener { private int maxFps; private int lockedVideoOrientation; private int iFrameInterval; + private int codecProfile; private boolean sendFrameMeta; private long ptsOrigin; - public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int iFrameInterval) { + public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int codecProfile, int iFrameInterval) { this.sendFrameMeta = sendFrameMeta; this.bitRate = bitRate; this.maxFps = maxFps; this.lockedVideoOrientation = lockedVideoOrientation; + this.codecProfile = codecProfile; this.iFrameInterval = iFrameInterval; } - public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation) { - this(sendFrameMeta, bitRate, maxFps, lockedVideoOrientation, DEFAULT_I_FRAME_INTERVAL); + public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int codecProfile) { + this(sendFrameMeta, bitRate, maxFps, lockedVideoOrientation, codecProfile, DEFAULT_I_FRAME_INTERVAL); } @Override @@ -64,6 +66,7 @@ public void streamScreen(Device device, FileDescriptor fd) throws IOException { try { do { MediaCodec codec = createCodec(); + setCodecProfile(codec, format); IBinder display = createDisplay(); ScreenInfo screenInfo = device.getScreenInfo(); Rect contentRect = screenInfo.getContentRect(); @@ -139,6 +142,22 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo, IO.writeFully(fd, headerBuffer); } + private void setCodecProfile(MediaCodec codec, MediaFormat format) throws IOException { + if(codecProfile == 0) return; + int level = 0; + for (MediaCodecInfo.CodecProfileLevel profileLevel : codec.getCodecInfo().getCapabilitiesForType("video/avc").profileLevels) { + if(profileLevel.profile == codecProfile) { + level = Math.max(level, profileLevel.level); + } + } + if(level == 0) throw new IOException("Device doesn't support the requested codec profile."); + // Profile (SDK Level 21) and Level (SDK Level 23). + format.setInteger(MediaFormat.KEY_PROFILE, codecProfile); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + format.setInteger(MediaFormat.KEY_LEVEL, level); + } + } + private static MediaCodec createCodec() throws IOException { return MediaCodec.createEncoderByType("video/avc"); } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index c2e501612c..5a67acdb8b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -20,7 +20,7 @@ private static void scrcpy(Options options) throws IOException { boolean tunnelForward = options.isTunnelForward(); try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) { ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), - options.getLockedVideoOrientation()); + options.getLockedVideoOrientation(), options.getCodecProfile()); if (options.getControl()) { Controller controller = new Controller(device, connection); From a5d5ea1b73374cf6053a69e0a57e23d4de5bc540 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Mon, 16 Mar 2020 11:23:11 +0200 Subject: [PATCH 4/6] Fix the printed versions (were opposite) (cherry picked from commit 7e36540c21eef4964b73824d8845b2468899981e) (cherry picked from commit ef95046c3e464beb94cb122224ac754f856685b4) (cherry picked from commit fa3644cca2ba2c4a562a203ff773c021f9082175) --- server/src/main/java/com/genymobile/scrcpy/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 5a67acdb8b..71d02abf69 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -77,7 +77,7 @@ private static Options createOptions(String... args) { String clientVersion = args[0]; if (!clientVersion.equals(BuildConfig.VERSION_NAME)) { throw new IllegalArgumentException( - "The server version (" + clientVersion + ") does not match the client " + "(" + BuildConfig.VERSION_NAME + ")"); + "The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")"); } if (args.length != 10) { From 1e3321cf58aecdbb97efabfd251cf5f46ede32d3 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Mon, 16 Mar 2020 15:03:43 +0200 Subject: [PATCH 5/6] Added help and parsing of the codec-profile option (cherry picked from commit e15824304434d1721cf928fde35c870c1fc01698) (cherry picked from commit a4ef318ae3e5764215282a9010d463a1ee271b1a) (cherry picked from commit a5b83c19081d04346ef580dfff09ec4076822302) --- app/scrcpy.1 | 4 ++++ app/src/cli.c | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index b3c57064c5..27ba5d792c 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -151,6 +151,10 @@ Set the initial window height. Default is 0 (automatic).\n +.TP +.BI "\-P, \-\-\codec\-profile " value +Request specific encoding codec profile, see AVC profiles at: https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel for valid codec profile values. Default is 0 (automatic). + .SH SHORTCUTS .TP diff --git a/app/src/cli.c b/app/src/cli.c index df1e54afad..4f998708c5 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -130,6 +130,12 @@ scrcpy_print_usage(const char *arg0) { " Set the initial window width.\n" " Default is 0 (automatic).\n" "\n" + " -P, --codec-profile value\n" + " Request specific encoding codec profile, see AVC profiles at:\n" + " https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel\n" + " for valid codec profile values.\n" + " Default is 0 (automatic).\n" + "\n" "Shortcuts:\n" "\n" " " CTRL_OR_CMD "+f\n" @@ -368,6 +374,35 @@ parse_record_format(const char *optarg, enum recorder_format *format) { return false; } +static bool +parse_codec_profile(const char *optarg, uint32_t *codec_profile) { + long value; + // long may be 32 bits (it is the case on mingw), so do not use more than + // 31 bits (long is signed) + bool ok = parse_integer_arg(optarg, &value, true, 0, 0x7FFFFFFF, "codec-profile"); + if (!ok) { + return false; + } + + switch (value) { + case 0: // Automatic + case 0x01: // AVCProfileBaseline + case 0x02: // AVCProfileMain + case 0x04: // AVCProfileExtended + case 0x08: // AVCProfileHigh + case 0x10: // AVCProfileHigh10 + case 0x20: // AVCProfileHigh422 + case 0x40: // AVCProfileHigh444 + case 0x10000: // AVCProfileConstrainedBaseline + case 0x80000: // AVCProfileConstrainedHigh + *codec_profile = (uint32_t) value; + return true; + default: + LOGE("Invalid codec-profile, Use -h for help"); + return false; + } +} + static enum recorder_format guess_record_format(const char *filename) { size_t len = strlen(filename); @@ -440,7 +475,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { optind = 0; // reset to start from the first argument in tests int c; - while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options, + while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv:P:", long_options, NULL)) != -1) { switch (c) { case 'b': @@ -550,6 +585,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case OPT_PREFER_TEXT: opts->prefer_text = true; break; + case 'P': + if (!parse_codec_profile(optarg, &opts->codec_profile)) { + return false; + } + break; default: // getopt prints the error message on stderr return false; From e49657a5e329d833bca885138867dd2ddcf0cca6 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Mon, 16 Mar 2020 15:04:13 +0200 Subject: [PATCH 6/6] Adding codec-profile option to the server exec params (cherry picked from commit fa56950f43a14a501d03a79c626103ae3fc9f046) --- app/src/server.c | 3 +++ app/src/server.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/src/server.c b/app/src/server.c index 4b2c1866cc..1d9efe8b5a 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -238,6 +238,7 @@ execute_server(struct server *server, const struct server_params *params) { sprintf(bit_rate_string, "%"PRIu32, params->bit_rate); sprintf(max_fps_string, "%"PRIu16, params->max_fps); sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation); + sprintf(codec_profile_string, "%"PRIu32, params->codec_profile); const char *const cmd[] = { "shell", "CLASSPATH=" DEVICE_SERVER_PATH, @@ -254,6 +255,7 @@ execute_server(struct server *server, const struct server_params *params) { bit_rate_string, max_fps_string, lock_video_orientation_string, + codec_profile_string, server->tunnel_forward ? "true" : "false", params->crop ? params->crop : "-", "true", // always send frame meta (packet boundaries + timestamp) @@ -327,6 +329,7 @@ bool server_start(struct server *server, const char *serial, const struct server_params *params) { server->port_range = params->port_range; + server->codec_profile = params->codec_profile; if (serial) { server->serial = SDL_strdup(serial); diff --git a/app/src/server.h b/app/src/server.h index f16ad6b43c..974cb4190a 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -19,6 +19,7 @@ struct server { uint16_t local_port; // selected from port_range bool tunnel_enabled; bool tunnel_forward; // use "adb forward" instead of "adb reverse" + uint32_t codec_profile; }; #define SERVER_INITIALIZER { \ @@ -34,6 +35,7 @@ struct server { .local_port = 0, \ .tunnel_enabled = false, \ .tunnel_forward = false, \ + .codec_profile = 0, } struct server_params {