Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codec options #1325

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ Set the initial window height.

Default is 0 (automatic).\n

.TP
.BI "\-\-\codec\-options " 'options'
options is a list of key=value:type pairs seperated by comma\n
for the device encoder.\n
type is optional, Integer by default.\n
For a list of possible codec options:\n
developer.android.com/reference/android/media/MediaFormat\n

.SH SHORTCUTS

.TP
Expand Down
21 changes: 15 additions & 6 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ scrcpy_print_usage(const char *arg0) {
" Set the initial window width.\n"
" Default is 0 (automatic).\n"
"\n"
" --codec-options 'options'\n"
" 'options' is a list of key=value:type pairs seperated by comma\n"
" for the device encoder.\n"
" 'type' is optional, Integer by default.\n"
" For a list of possible codec options:\n"
" developer.android.com/reference/android/media/MediaFormat\n"
"\n"
"Shortcuts:\n"
"\n"
" " CTRL_OR_CMD "+f\n"
Expand Down Expand Up @@ -468,18 +475,19 @@ guess_record_format(const char *filename) {
#define OPT_ROTATION 1015
#define OPT_RENDER_DRIVER 1016
#define OPT_NO_MIPMAPS 1017
#define OPT_CODEC_OPTIONS 1018

bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
static const struct option long_options[] = {
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
{"bit-rate", required_argument, NULL, 'b'},
{"codec-options", required_argument, NULL, OPT_CODEC_OPTIONS},
{"crop", required_argument, NULL, OPT_CROP},
{"display", required_argument, NULL, OPT_DISPLAY_ID},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"lock-video-orientation", required_argument, NULL,
OPT_LOCK_VIDEO_ORIENTATION},
{"lock-video-orientation", required_argument, NULL, OPT_LOCK_VIDEO_ORIENTATION},
{"max-fps", required_argument, NULL, OPT_MAX_FPS},
{"max-size", required_argument, NULL, 'm'},
{"no-control", no_argument, NULL, 'n'},
Expand All @@ -490,8 +498,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"record", required_argument, NULL, 'r'},
{"record-format", required_argument, NULL, OPT_RECORD_FORMAT},
{"render-driver", required_argument, NULL, OPT_RENDER_DRIVER},
{"render-expired-frames", no_argument, NULL,
OPT_RENDER_EXPIRED_FRAMES},
{"render-expired-frames", no_argument, NULL, OPT_RENDER_EXPIRED_FRAMES},
{"rotation", required_argument, NULL, OPT_ROTATION},
{"serial", required_argument, NULL, 's'},
{"show-touches", no_argument, NULL, 't'},
Expand All @@ -503,8 +510,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"window-y", required_argument, NULL, OPT_WINDOW_Y},
{"window-width", required_argument, NULL, OPT_WINDOW_WIDTH},
{"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT},
{"window-borderless", no_argument, NULL,
OPT_WINDOW_BORDERLESS},
{"window-borderless", no_argument, NULL, OPT_WINDOW_BORDERLESS},
{NULL, 0, NULL, 0 },
};

Expand Down Expand Up @@ -639,6 +645,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_NO_MIPMAPS:
opts->mipmaps = false;
break;
case OPT_CODEC_OPTIONS:
opts->codec_options = optarg;
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ scrcpy(const struct scrcpy_options *options) {
.lock_video_orientation = options->lock_video_orientation,
.control = options->control,
.display_id = options->display_id,
.codec_options = options->codec_options,
};
if (!server_start(&server, options->serial, &params)) {
return false;
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct scrcpy_options {
const char *window_title;
const char *push_target;
const char *render_driver;
const char *codec_options;
enum recorder_format record_format;
struct port_range port_range;
uint16_t max_size;
Expand Down Expand Up @@ -47,6 +48,7 @@ struct scrcpy_options {
.window_title = NULL, \
.push_target = NULL, \
.render_driver = NULL, \
.codec_options = NULL, \
.record_format = RECORDER_FORMAT_AUTO, \
.port_range = { \
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, \
Expand Down
1 change: 1 addition & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ execute_server(struct server *server, const struct server_params *params) {
"true", // always send frame meta (packet boundaries + timestamp)
params->control ? "true" : "false",
display_id_string,
params->codec_options ? params->codec_options : "-",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct server {

struct server_params {
const char *crop;
const char *codec_options;
struct port_range port_range;
uint16_t max_size;
uint32_t bit_rate;
Expand Down
9 changes: 9 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class Options {
private boolean sendFrameMeta; // send PTS so that the client may record properly
private boolean control;
private int displayId;
private String codecOptions;

public int getMaxSize() {
return maxSize;
Expand Down Expand Up @@ -84,4 +85,12 @@ public int getDisplayId() {
public void setDisplayId(int displayId) {
this.displayId = displayId;
}

public String getCodecOptions() {
return codecOptions;
}

public void setCodecOptions(String codecOptions) {
this.codecOptions = codecOptions;
}
}
35 changes: 32 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ public class ScreenEncoder implements Device.RotationListener {
private final AtomicBoolean rotationChanged = new AtomicBoolean();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);

private String codecOptions;
private int bitRate;
private int maxFps;
private boolean sendFrameMeta;
private long ptsOrigin;

public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps) {
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, String codecOptions) {
this.sendFrameMeta = sendFrameMeta;
this.bitRate = bitRate;
this.maxFps = maxFps;
this.codecOptions = codecOptions;
}

@Override
Expand All @@ -49,7 +51,7 @@ public void streamScreen(Device device, FileDescriptor fd) throws IOException {
Workarounds.prepareMainLooper();
Workarounds.fillAppInfo();

MediaFormat format = createFormat(bitRate, maxFps, DEFAULT_I_FRAME_INTERVAL);
MediaFormat format = createFormat(bitRate, maxFps, DEFAULT_I_FRAME_INTERVAL, codecOptions);
device.setRotationListener(this);
boolean alive;
try {
Expand Down Expand Up @@ -139,7 +141,7 @@ private static MediaCodec createCodec() throws IOException {
return MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
}

private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInterval) {
private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInterval, String codecOptions) {
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
Expand All @@ -155,6 +157,33 @@ private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInter
// <https://github.com/Genymobile/scrcpy/issues/488#issuecomment-567321437>
format.setFloat(KEY_MAX_FPS_TO_ENCODER, maxFps);
}
// Adding additional options specified by the user
if(!"-".equals(codecOptions)) {
for (String pair : codecOptions.split(",")) {
String[] option = pair.split("=");
String key = option[0];
if(format.containsKey(key)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, contrary to what I said in a previous PR, this does not do what I expected (in fact it just test whether we already set the key, so in practice this is always false).

if (option.length < 2) {
Ln.w("No value specified for codec option - " + key);
continue;
}
String[] valueAndType = option[1].split(":");
String value = valueAndType[0];
String type = valueAndType.length < 2 ? "" : valueAndType[1].toLowerCase();
if (type.contains("str")) {
format.setString(key, value);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could not pass a String containing a ,.

} else if (type.contains("long")) {
format.setLong(key, Long.parseLong(value));
} else if (type.contains("float")) {
format.setFloat(key, Float.parseFloat(value));
} else {
format.setInteger(key, Integer.parseInt(value));
}
} else {
Ln.w("Codec format doesn't contain the requested codec option - " + key);
}
}
}
return format;
}

Expand Down
9 changes: 6 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private static void scrcpy(Options options) throws IOException {
final Device device = new Device(options);
boolean tunnelForward = options.isTunnelForward();
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps());
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), options.getCodecOptions());

if (options.getControl()) {
Controller controller = new Controller(device, connection);
Expand Down Expand Up @@ -79,8 +79,8 @@ private static Options createOptions(String... args) {
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
}

if (args.length != 10) {
throw new IllegalArgumentException("Expecting 10 parameters");
if (args.length != 11) {
throw new IllegalArgumentException("Expecting 11 parameters");
}

Options options = new Options();
Expand Down Expand Up @@ -113,6 +113,9 @@ private static Options createOptions(String... args) {
int displayId = Integer.parseInt(args[9]);
options.setDisplayId(displayId);

String codecOptions = args[10];
options.setCodecOptions(codecOptions);

return options;
}

Expand Down