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

Add --no-video (to mirror audio only) #3978

Merged
merged 14 commits into from
May 15, 2023
3 changes: 2 additions & 1 deletion app/data/bash-completion/scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ _scrcpy() {
--no-clipboard-autosync
--no-downsize-on-error
-n --no-control
-N --no-display
-N --no-mirror
--no-key-repeat
--no-mipmaps
--no-power-on
--no-video
--otg
-p --port=
--power-off-on-close
Expand Down
3 changes: 2 additions & 1 deletion app/data/zsh-completion/_scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ arguments=(
'--no-clipboard-autosync[Disable automatic clipboard synchronization]'
'--no-downsize-on-error[Disable lowering definition on MediaCodec error]'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-display}'[Do not display device \(during screen recording or when V4L2 sink is enabled\)]'
{-N,--no-mirror}'[Do not mirror device \(only when recording or V4L2 sink is enabled\)]'
'--no-key-repeat[Do not forward repeated key events when a key is held down]'
'--no-mipmaps[Disable the generation of mipmaps]'
'--no-power-on[Do not power on the device on start]'
'--no-video[Disable video forwarding]'
'--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]'
{-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]'
'--power-off-on-close[Turn the device screen off when closing scrcpy]'
Expand Down
4 changes: 2 additions & 2 deletions app/prebuilt-deps/prepare-ffmpeg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ cd "$DIR"
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"

VERSION=6.0-scrcpy-2
VERSION=6.0-scrcpy-3
DEP_DIR="ffmpeg-$VERSION"

FILENAME="$DEP_DIR".7z
SHA256SUM=98ef97f8607c97a5c4f9c5a0a991b78f105d002a3619145011d16ffb92501b14
SHA256SUM=36829d98ac4454d7092c72ddb92faa20b60450bc0fe8873076efb0858cdcbc2c

if [[ -d "$DEP_DIR" ]]
then
Expand Down
8 changes: 6 additions & 2 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ This option disables this behavior.
Disable device control (mirror the device in read\-only).

.TP
.B \-N, \-\-no\-display
Do not display device (only when screen recording is enabled).
.B \-N, \-\-no\-mirror
Do not mirror device video or audio on the computer (only when recording or V4L2 sink is enabled).

.TP
.B \-\-no\-key\-repeat
Expand All @@ -225,6 +225,10 @@ If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically
.B \-\-no\-power\-on
Do not power on the device on start.

.TP
.B \-\-no\-video
Disable video forwarding.

.TP
.B \-\-otg
Run in OTG mode: simulate physical keyboard and mouse, as if the computer keyboard and mouse were plugged directly to the device via an OTG cable.
Expand Down
145 changes: 108 additions & 37 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ enum {
OPT_REQUIRE_AUDIO,
OPT_AUDIO_BUFFER,
OPT_AUDIO_OUTPUT_BUFFER,
OPT_NO_DISPLAY,
OPT_NO_VIDEO,
};

struct sc_option {
Expand Down Expand Up @@ -380,9 +382,14 @@ static const struct sc_option options[] = {
},
{
.shortopt = 'N',
.longopt = "no-mirror",
.text = "Do not mirror device video or audio on the computer (only "
"when recording or V4L2 sink is enabled).",
},
{
// deprecated
.longopt_id = OPT_NO_DISPLAY,
.longopt = "no-display",
.text = "Do not display device (only when screen recording or V4L2 "
"sink is enabled).",
},
{
.longopt_id = OPT_NO_KEY_REPEAT,
Expand All @@ -401,6 +408,11 @@ static const struct sc_option options[] = {
.longopt = "no-power-on",
.text = "Do not power on the device on start.",
},
{
.longopt_id = OPT_NO_VIDEO,
.longopt = "no-video",
.text = "Disable video forwarding.",
},
{
.longopt_id = OPT_OTG,
.longopt = "otg",
Expand Down Expand Up @@ -1467,18 +1479,39 @@ sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
}
#endif

static enum sc_record_format
get_record_format(const char *name) {
if (!strcmp(name, "mp4")) {
return SC_RECORD_FORMAT_MP4;
}
if (!strcmp(name, "mkv")) {
return SC_RECORD_FORMAT_MKV;
}
if (!strcmp(name, "m4a")) {
return SC_RECORD_FORMAT_M4A;
}
if (!strcmp(name, "mka")) {
return SC_RECORD_FORMAT_MKA;
}
if (!strcmp(name, "opus")) {
return SC_RECORD_FORMAT_OPUS;
}
if (!strcmp(name, "aac")) {
return SC_RECORD_FORMAT_AAC;
}
return 0;
}

static bool
parse_record_format(const char *optarg, enum sc_record_format *format) {
if (!strcmp(optarg, "mp4")) {
*format = SC_RECORD_FORMAT_MP4;
return true;
}
if (!strcmp(optarg, "mkv")) {
*format = SC_RECORD_FORMAT_MKV;
return true;
enum sc_record_format fmt = get_record_format(optarg);
if (!fmt) {
LOGE("Unsupported format: %s (expected mp4 or mkv)", optarg);
return false;
}
LOGE("Unsupported format: %s (expected mp4 or mkv)", optarg);
return false;

*format = fmt;
return true;
}

static bool
Expand All @@ -1498,18 +1531,13 @@ parse_port(const char *optarg, uint16_t *port) {

static enum sc_record_format
guess_record_format(const char *filename) {
size_t len = strlen(filename);
if (len < 4) {
const char *dot = strrchr(filename, '.');
if (!dot) {
return 0;
}
const char *ext = &filename[len - 4];
if (!strcmp(ext, ".mp4")) {
return SC_RECORD_FORMAT_MP4;
}
if (!strcmp(ext, ".mkv")) {
return SC_RECORD_FORMAT_MKV;
}
return 0;

const char *ext = dot + 1;
return get_record_format(ext);
}

static bool
Expand Down Expand Up @@ -1642,8 +1670,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case 'n':
opts->control = false;
break;
case OPT_NO_DISPLAY:
LOGW("--no-display is deprecated, use --no-mirror instead.");
// fall through
case 'N':
opts->display = false;
opts->mirror = false;
break;
case 'p':
if (!parse_port_range(optarg, &opts->port_range)) {
Expand Down Expand Up @@ -1788,6 +1819,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_NO_DOWNSIZE_ON_ERROR:
opts->downsize_on_error = false;
break;
case OPT_NO_VIDEO:
opts->video = false;
break;
case OPT_NO_AUDIO:
opts->audio = false;
break;
Expand Down Expand Up @@ -1890,8 +1924,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}

#ifdef HAVE_V4L2
if (!opts->display && !opts->record_filename && !opts->v4l2_device) {
LOGE("-N/--no-display requires either screen recording (-r/--record)"
if (!opts->mirror && !opts->record_filename && !opts->v4l2_device) {
LOGE("-N/--no-mirror requires either screen recording (-r/--record)"
" or sink to v4l2loopback device (--v4l2-sink)");
return false;
}
Expand All @@ -1915,14 +1949,14 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
#else
if (!opts->display && !opts->record_filename) {
LOGE("-N/--no-display requires screen recording (-r/--record)");
if (!opts->mirror && !opts->record_filename) {
LOGE("-N/--no-mirror requires screen recording (-r/--record)");
return false;
}
#endif

if (opts->audio && !opts->display && !opts->record_filename) {
LOGI("No display and no recording: audio disabled");
if (opts->audio && !opts->mirror && !opts->record_filename) {
LOGI("No mirror and no recording: audio disabled");
opts->audio = false;
}

Expand All @@ -1937,19 +1971,41 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}

if (opts->record_filename && !opts->record_format) {
opts->record_format = guess_record_format(opts->record_filename);
if (opts->record_filename) {
if (!opts->record_format) {
LOGE("No format specified for \"%s\" "
"(try with --record-format=mkv)",
opts->record_filename);
opts->record_format = guess_record_format(opts->record_filename);
if (!opts->record_format) {
LOGE("No format specified for \"%s\" "
"(try with --record-format=mkv)",
opts->record_filename);
return false;
}
}

if (opts->audio_codec == SC_CODEC_RAW) {
LOGW("Recording does not support RAW audio codec");
return false;
}
}

if (opts->record_filename && opts->audio_codec == SC_CODEC_RAW) {
LOGW("Recording does not support RAW audio codec");
return false;
if (opts->video
&& sc_record_format_is_audio_only(opts->record_format)) {
LOGE("Audio container does not support video stream");
return false;
}

if (opts->record_format == SC_RECORD_FORMAT_OPUS
&& opts->audio_codec != SC_CODEC_OPUS) {
LOGE("Recording to OPUS file requires an OPUS audio stream "
"(try with --audio-codec=opus)");
return false;
}

if (opts->record_format == SC_RECORD_FORMAT_AAC
&& opts->audio_codec != SC_CODEC_AAC) {
LOGE("Recording to AAC file requires an AAC audio stream "
"(try with --audio-codec=aac)");
return false;
}
}

if (opts->audio_codec == SC_CODEC_RAW) {
Expand Down Expand Up @@ -2032,6 +2088,21 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
#endif

#ifdef HAVE_USB
if (!(opts->mirror && opts->video) && !opts->otg) {
#else
if (!(opts->mirror && opts->video)) {
#endif
// If video mirroring is disabled and OTG are disabled, then there is
// no way to control the device.
opts->control = false;
}

if (!opts->video) {
// If video is disabled, then scrcpy must exit on audio failure.
opts->require_audio = true;
}

return true;
}

Expand Down
3 changes: 2 additions & 1 deletion app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const struct scrcpy_options scrcpy_options_default = {
.fullscreen = false,
.always_on_top = false,
.control = true,
.display = true,
.mirror = true,
.turn_screen_off = false,
.key_inject_mode = SC_KEY_INJECT_MODE_MIXED,
.window_borderless = false,
Expand All @@ -73,6 +73,7 @@ const struct scrcpy_options scrcpy_options_default = {
.cleanup = true,
.start_fps_counter = false,
.power_on = true,
.video = true,
.audio = true,
.require_audio = false,
.list_encoders = false,
Expand Down
15 changes: 14 additions & 1 deletion app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ enum sc_record_format {
SC_RECORD_FORMAT_AUTO,
SC_RECORD_FORMAT_MP4,
SC_RECORD_FORMAT_MKV,
SC_RECORD_FORMAT_M4A,
SC_RECORD_FORMAT_MKA,
SC_RECORD_FORMAT_OPUS,
SC_RECORD_FORMAT_AAC,
};

static inline bool
sc_record_format_is_audio_only(enum sc_record_format fmt) {
return fmt == SC_RECORD_FORMAT_M4A
|| fmt == SC_RECORD_FORMAT_MKA
|| fmt == SC_RECORD_FORMAT_OPUS
|| fmt == SC_RECORD_FORMAT_AAC;
}

enum sc_codec {
SC_CODEC_H264,
SC_CODEC_H265,
Expand Down Expand Up @@ -135,7 +147,7 @@ struct scrcpy_options {
bool fullscreen;
bool always_on_top;
bool control;
bool display;
bool mirror;
bool turn_screen_off;
enum sc_key_inject_mode key_inject_mode;
bool window_borderless;
Expand All @@ -156,6 +168,7 @@ struct scrcpy_options {
bool cleanup;
bool start_fps_counter;
bool power_on;
bool video;
bool audio;
bool require_audio;
bool list_encoders;
Expand Down
Loading