Add --no-video

Similar to --no-audio, add --no-video to play audio only.

Fixes #3842 <https://github.com/Genymobile/scrcpy/issues/3842>
PR #3978 <https://github.com/Genymobile/scrcpy/pull/3978>
This commit is contained in:
Romain Vimont
2023-05-07 12:08:50 +02:00
parent e89e772c7c
commit 8c650e53cd
17 changed files with 243 additions and 96 deletions

View File

@@ -73,6 +73,7 @@ enum {
OPT_AUDIO_BUFFER,
OPT_AUDIO_OUTPUT_BUFFER,
OPT_NO_DISPLAY,
OPT_NO_VIDEO,
};
struct sc_option {
@@ -407,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",
@@ -1797,6 +1803,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;
@@ -2042,14 +2051,20 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
#endif
#ifdef HAVE_USB
if (!opts->mirror && opts->control && !opts->otg) {
if (!(opts->mirror && opts->video) && !opts->otg) {
#else
if (!opts->mirror && opts->control) {
if (!(opts->mirror && opts->video)) {
#endif
LOGD("Mirroring is disabled, force --no-control");
// 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;
}

View File

@@ -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,

View File

@@ -156,6 +156,7 @@ struct scrcpy_options {
bool cleanup;
bool start_fps_counter;
bool power_on;
bool video;
bool audio;
bool require_audio;
bool list_encoders;

View File

@@ -152,7 +152,7 @@ sc_recorder_close_output_file(struct sc_recorder *recorder) {
static inline bool
sc_recorder_has_empty_queues(struct sc_recorder *recorder) {
if (sc_vecdeque_is_empty(&recorder->video_queue)) {
if (recorder->video && sc_vecdeque_is_empty(&recorder->video_queue)) {
// The video queue is empty
return true;
}
@@ -176,7 +176,7 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
sc_cond_wait(&recorder->stream_cond, &recorder->mutex);
}
if (sc_vecdeque_is_empty(&recorder->video_queue)) {
if (recorder->video && sc_vecdeque_is_empty(&recorder->video_queue)) {
assert(recorder->stopped);
// If the recorder is stopped, don't process anything if there are not
// at least video packets
@@ -184,7 +184,11 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
return false;
}
AVPacket *video_pkt = sc_vecdeque_pop(&recorder->video_queue);
AVPacket *video_pkt = NULL;
if (!sc_vecdeque_is_empty(&recorder->video_queue)) {
assert(recorder->video);
video_pkt = sc_vecdeque_pop(&recorder->video_queue);
}
AVPacket *audio_pkt = NULL;
if (!sc_vecdeque_is_empty(&recorder->audio_queue)) {
@@ -196,17 +200,19 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
int ret = false;
if (video_pkt->pts != AV_NOPTS_VALUE) {
LOGE("The first video packet is not a config packet");
goto end;
}
if (video_pkt) {
if (video_pkt->pts != AV_NOPTS_VALUE) {
LOGE("The first video packet is not a config packet");
goto end;
}
assert(recorder->video_stream_index >= 0);
AVStream *video_stream =
recorder->ctx->streams[recorder->video_stream_index];
bool ok = sc_recorder_set_extradata(video_stream, video_pkt);
if (!ok) {
goto end;
assert(recorder->video_stream_index >= 0);
AVStream *video_stream =
recorder->ctx->streams[recorder->video_stream_index];
bool ok = sc_recorder_set_extradata(video_stream, video_pkt);
if (!ok) {
goto end;
}
}
if (audio_pkt) {
@@ -218,13 +224,13 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
assert(recorder->audio_stream_index >= 0);
AVStream *audio_stream =
recorder->ctx->streams[recorder->audio_stream_index];
ok = sc_recorder_set_extradata(audio_stream, audio_pkt);
bool ok = sc_recorder_set_extradata(audio_stream, audio_pkt);
if (!ok) {
goto end;
}
}
ok = avformat_write_header(recorder->ctx, NULL) >= 0;
bool ok = avformat_write_header(recorder->ctx, NULL) >= 0;
if (!ok) {
LOGE("Failed to write header to %s", recorder->filename);
goto end;
@@ -233,7 +239,9 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
ret = true;
end:
av_packet_free(&video_pkt);
if (video_pkt) {
av_packet_free(&video_pkt);
}
if (audio_pkt) {
av_packet_free(&audio_pkt);
}
@@ -263,7 +271,8 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
sc_mutex_lock(&recorder->mutex);
while (!recorder->stopped) {
if (!video_pkt && !sc_vecdeque_is_empty(&recorder->video_queue)) {
if (recorder->video && !video_pkt &&
!sc_vecdeque_is_empty(&recorder->video_queue)) {
// A new packet may be assigned to video_pkt and be processed
break;
}
@@ -278,6 +287,11 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
// If stopped is set, continue to process the remaining events (to
// finish the recording) before actually stopping.
// If there is no video, then the video_queue will remain empty forever
// and video_pkt will always be NULL.
assert(recorder->video || (!video_pkt
&& sc_vecdeque_is_empty(&recorder->video_queue)));
// If there is no audio, then the audio_queue will remain empty forever
// and audio_pkt will always be NULL.
assert(recorder->audio || (!audio_pkt
@@ -319,6 +333,9 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
if (!recorder->audio) {
assert(video_pkt);
pts_origin = video_pkt->pts;
} else if (!recorder->video) {
assert(audio_pkt);
pts_origin = audio_pkt->pts;
} else if (video_pkt && audio_pkt) {
pts_origin = MIN(video_pkt->pts, audio_pkt->pts);
} else if (recorder->stopped) {
@@ -639,7 +656,7 @@ sc_recorder_audio_packet_sink_disable(struct sc_packet_sink *sink) {
bool
sc_recorder_init(struct sc_recorder *recorder, const char *filename,
enum sc_record_format format, bool audio,
enum sc_record_format format, bool video, bool audio,
const struct sc_recorder_callbacks *cbs, void *cbs_userdata) {
recorder->filename = strdup(filename);
if (!recorder->filename) {
@@ -662,6 +679,8 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
goto error_queue_cond_destroy;
}
assert(video || audio);
recorder->video = video;
recorder->audio = audio;
sc_vecdeque_init(&recorder->video_queue);
@@ -680,13 +699,15 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
recorder->cbs = cbs;
recorder->cbs_userdata = cbs_userdata;
static const struct sc_packet_sink_ops video_ops = {
.open = sc_recorder_video_packet_sink_open,
.close = sc_recorder_video_packet_sink_close,
.push = sc_recorder_video_packet_sink_push,
};
if (video) {
static const struct sc_packet_sink_ops video_ops = {
.open = sc_recorder_video_packet_sink_open,
.close = sc_recorder_video_packet_sink_close,
.push = sc_recorder_video_packet_sink_push,
};
recorder->video_packet_sink.ops = &video_ops;
recorder->video_packet_sink.ops = &video_ops;
}
if (audio) {
static const struct sc_packet_sink_ops audio_ops = {

View File

@@ -27,6 +27,7 @@ struct sc_recorder {
* may access it without data races.
*/
bool audio;
bool video;
char *filename;
enum sc_record_format format;
@@ -59,7 +60,7 @@ struct sc_recorder_callbacks {
bool
sc_recorder_init(struct sc_recorder *recorder, const char *filename,
enum sc_record_format format, bool audio,
enum sc_record_format format, bool video, bool audio,
const struct sc_recorder_callbacks *cbs, void *cbs_userdata);
bool

View File

@@ -345,6 +345,7 @@ scrcpy(struct scrcpy_options *options) {
.lock_video_orientation = options->lock_video_orientation,
.control = options->control,
.display_id = options->display_id,
.video = options->video,
.audio = options->audio,
.show_touches = options->show_touches,
.stay_awake = options->stay_awake,
@@ -389,7 +390,7 @@ scrcpy(struct scrcpy_options *options) {
sdl_set_hints(options->render_driver);
// Initialize SDL video and audio in addition if mirroring is enabled
if (SDL_Init(SDL_INIT_VIDEO)) {
if (options->video && SDL_Init(SDL_INIT_VIDEO)) {
LOGE("Could not initialize SDL video: %s", SDL_GetError());
goto end;
}
@@ -436,11 +437,13 @@ scrcpy(struct scrcpy_options *options) {
file_pusher_initialized = true;
}
static const struct sc_demuxer_callbacks video_demuxer_cbs = {
.on_ended = sc_video_demuxer_on_ended,
};
sc_demuxer_init(&s->video_demuxer, "video", s->server.video_socket,
&video_demuxer_cbs, NULL);
if (options->video) {
static const struct sc_demuxer_callbacks video_demuxer_cbs = {
.on_ended = sc_video_demuxer_on_ended,
};
sc_demuxer_init(&s->video_demuxer, "video", s->server.video_socket,
&video_demuxer_cbs, NULL);
}
if (options->audio) {
static const struct sc_demuxer_callbacks audio_demuxer_cbs = {
@@ -450,7 +453,7 @@ scrcpy(struct scrcpy_options *options) {
&audio_demuxer_cbs, options);
}
bool needs_video_decoder = options->mirror;
bool needs_video_decoder = options->mirror && options->video;
bool needs_audio_decoder = options->mirror && options->audio;
#ifdef HAVE_V4L2
needs_video_decoder |= !!options->v4l2_device;
@@ -471,8 +474,8 @@ scrcpy(struct scrcpy_options *options) {
.on_ended = sc_recorder_on_ended,
};
if (!sc_recorder_init(&s->recorder, options->record_filename,
options->record_format, options->audio,
&recorder_cbs, NULL)) {
options->record_format, options->video,
options->audio, &recorder_cbs, NULL)) {
goto end;
}
recorder_initialized = true;
@@ -482,8 +485,10 @@ scrcpy(struct scrcpy_options *options) {
}
recorder_started = true;
sc_packet_source_add_sink(&s->video_demuxer.packet_source,
&s->recorder.video_packet_sink);
if (options->video) {
sc_packet_source_add_sink(&s->video_demuxer.packet_source,
&s->recorder.video_packet_sink);
}
if (options->audio) {
sc_packet_source_add_sink(&s->audio_demuxer.packet_source,
&s->recorder.audio_packet_sink);
@@ -671,11 +676,6 @@ aoa_hid_end:
.start_fps_counter = options->start_fps_counter,
};
if (!sc_screen_init(&s->screen, &screen_params)) {
goto end;
}
screen_initialized = true;
struct sc_frame_source *src = &s->video_decoder.frame_source;
if (options->display_buffer) {
sc_delay_buffer_init(&s->display_buffer, options->display_buffer,
@@ -684,7 +684,14 @@ aoa_hid_end:
src = &s->display_buffer.frame_source;
}
sc_frame_source_add_sink(src, &s->screen.frame_sink);
if (options->video) {
if (!sc_screen_init(&s->screen, &screen_params)) {
goto end;
}
screen_initialized = true;
sc_frame_source_add_sink(src, &s->screen.frame_sink);
}
if (options->audio) {
sc_audio_player_init(&s->audio_player, options->audio_buffer,
@@ -713,12 +720,15 @@ aoa_hid_end:
}
#endif
// now we consumed the header values, the socket receives the video stream
// start the video demuxer
if (!sc_demuxer_start(&s->video_demuxer)) {
goto end;
// Now that the header values have been consumed, the socket(s) will
// receive the stream(s). Start the demuxer(s).
if (options->video) {
if (!sc_demuxer_start(&s->video_demuxer)) {
goto end;
}
video_demuxer_started = true;
}
video_demuxer_started = true;
if (options->audio) {
if (!sc_demuxer_start(&s->audio_demuxer)) {

View File

@@ -226,6 +226,9 @@ execute_server(struct sc_server *server,
ADD_PARAM("scid=%08x", params->scid);
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
if (!params->video) {
ADD_PARAM("video=false");
}
if (params->video_bit_rate) {
ADD_PARAM("video_bit_rate=%" PRIu32, params->video_bit_rate);
}
@@ -464,6 +467,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
const char *serial = server->serial;
assert(serial);
bool video = server->params.video;
bool audio = server->params.audio;
bool control = server->params.control;
@@ -471,9 +475,12 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
sc_socket audio_socket = SC_SOCKET_NONE;
sc_socket control_socket = SC_SOCKET_NONE;
if (!tunnel->forward) {
video_socket = net_accept_intr(&server->intr, tunnel->server_socket);
if (video_socket == SC_SOCKET_NONE) {
goto fail;
if (video) {
video_socket =
net_accept_intr(&server->intr, tunnel->server_socket);
if (video_socket == SC_SOCKET_NONE) {
goto fail;
}
}
if (audio) {
@@ -504,35 +511,45 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
unsigned attempts = 100;
sc_tick delay = SC_TICK_FROM_MS(100);
video_socket = connect_to_server(server, attempts, delay, tunnel_host,
tunnel_port);
if (video_socket == SC_SOCKET_NONE) {
sc_socket first_socket = connect_to_server(server, attempts, delay,
tunnel_host, tunnel_port);
if (first_socket == SC_SOCKET_NONE) {
goto fail;
}
if (video) {
video_socket = first_socket;
}
if (audio) {
audio_socket = net_socket();
if (audio_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, audio_socket, tunnel_host,
tunnel_port);
if (!ok) {
goto fail;
if (!video) {
audio_socket = first_socket;
} else {
audio_socket = net_socket();
if (audio_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, audio_socket, tunnel_host,
tunnel_port);
if (!ok) {
goto fail;
}
}
}
if (control) {
// we know that the device is listening, we don't need several
// attempts
control_socket = net_socket();
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, control_socket,
tunnel_host, tunnel_port);
if (!ok) {
goto fail;
if (!video && !audio) {
control_socket = first_socket;
} else {
control_socket = net_socket();
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, control_socket,
tunnel_host, tunnel_port);
if (!ok) {
goto fail;
}
}
}
}
@@ -541,13 +558,17 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
sc_adb_tunnel_close(tunnel, &server->intr, serial,
server->device_socket_name);
sc_socket first_socket = video ? video_socket
: audio ? audio_socket
: control_socket;
// The sockets will be closed on stop if device_read_info() fails
bool ok = device_read_info(&server->intr, video_socket, info);
bool ok = device_read_info(&server->intr, first_socket, info);
if (!ok) {
goto fail;
}
assert(video_socket != SC_SOCKET_NONE);
assert(!video || video_socket != SC_SOCKET_NONE);
assert(!audio || audio_socket != SC_SOCKET_NONE);
assert(!control || control_socket != SC_SOCKET_NONE);
@@ -931,8 +952,11 @@ run_server(void *data) {
sc_mutex_unlock(&server->mutex);
// Interrupt sockets to wake up socket blocking calls on the server
assert(server->video_socket != SC_SOCKET_NONE);
net_interrupt(server->video_socket);
if (server->video_socket != SC_SOCKET_NONE) {
// There is no video_socket if --no-video is set
net_interrupt(server->video_socket);
}
if (server->audio_socket != SC_SOCKET_NONE) {
// There is no audio_socket if --no-audio is set

View File

@@ -41,6 +41,7 @@ struct sc_server_params {
int8_t lock_video_orientation;
bool control;
uint32_t display_id;
bool video;
bool audio;
bool show_touches;
bool stay_awake;