From eb585c2c4f6fc9dd4a1ae4bf9af4f700e26ee0b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:43:53 +0000 Subject: [PATCH] Rename --stream-port to --srt-sink (SRT protocol), fix video/audio assertion with --no-playback Co-authored-by: yeicor <4929005+yeicor@users.noreply.github.com> --- app/src/cli.c | 36 +++++++++++++++++-------------- app/src/options.c | 2 +- app/src/options.h | 2 +- app/src/scrcpy.c | 4 ++-- app/src/stream_sink.c | 49 +++++++++++++++++++++++++++++-------------- app/src/stream_sink.h | 5 ++--- 6 files changed, 59 insertions(+), 39 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index b7ececbe..06c6db0b 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -114,7 +114,7 @@ enum { OPT_NO_VD_SYSTEM_DECORATIONS, OPT_NO_VD_DESTROY_CONTENT, OPT_DISPLAY_IME_POLICY, - OPT_STREAM_PORT, + OPT_SRT_SINK, }; struct sc_option { @@ -958,13 +958,17 @@ static const struct sc_option options[] = { #endif }, { - .longopt_id = OPT_STREAM_PORT, - .longopt = "stream-port", - .argdesc = "port", - .text = "Start a TCP server that streams the device video (and audio, " - "if enabled) as MPEG-TS on the given port. " - "Once started, connect with any compatible player using " - "tcp://127.0.0.1: (e.g. in OBS Media Source or VLC).", + .longopt_id = OPT_SRT_SINK, + .longopt = "srt-sink", + .argdesc = "url", + .text = "Stream the device video (and audio, if enabled) as MPEG-TS " + "over SRT to the given URL.\n" + "Example: srt://0.0.0.0:8080\n" + "scrcpy acts as the SRT listener (server) by default; " + "?mode=listener is appended automatically if not present.\n" + "Connect with any SRT-compatible player, e.g.:\n" + " VLC: srt://127.0.0.1:8080\n" + " ffplay: -i srt://127.0.0.1:8080", }, { .longopt_id = OPT_V4L2_SINK, @@ -2696,10 +2700,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], LOGE("OTG mode (--otg) is disabled."); return false; #endif - case OPT_STREAM_PORT: - if (!parse_port(optarg, &opts->stream_port)) { - return false; - } + case OPT_SRT_SINK: + opts->srt_sink = optarg; break; case OPT_V4L2_SINK: #ifdef HAVE_V4L2 @@ -2891,13 +2893,15 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], } if (opts->video && !opts->video_playback && !opts->record_filename - && !v4l2) { - LOGI("No video playback, no recording, no V4L2 sink: video disabled"); + && !v4l2 && !opts->srt_sink) { + LOGI("No video playback, no recording, no V4L2 sink, no SRT sink: " + "video disabled"); opts->video = false; } - if (opts->audio && !opts->audio_playback && !opts->record_filename) { - LOGI("No audio playback, no recording: audio disabled"); + if (opts->audio && !opts->audio_playback && !opts->record_filename + && !opts->srt_sink) { + LOGI("No audio playback, no recording, no SRT sink: audio disabled"); opts->audio = false; } diff --git a/app/src/options.c b/app/src/options.c index a1ce74d9..77768c0e 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -71,7 +71,7 @@ const struct scrcpy_options scrcpy_options_default = { .v4l2_device = NULL, .v4l2_buffer = 0, #endif - .stream_port = 0, + .srt_sink = NULL, #ifdef HAVE_USB .otg = false, #endif diff --git a/app/src/options.h b/app/src/options.h index 88ab7cfd..138992de 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -281,7 +281,7 @@ struct scrcpy_options { const char *v4l2_device; sc_tick v4l2_buffer; #endif - uint16_t stream_port; // 0 means disabled + const char *srt_sink; #ifdef HAVE_USB bool otg; #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 5674eae2..20e52e9a 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -636,8 +636,8 @@ scrcpy(struct scrcpy_options *options) { } } - if (options->stream_port) { - if (!sc_stream_sink_init(&s->stream_sink, options->stream_port, + if (options->srt_sink) { + if (!sc_stream_sink_init(&s->stream_sink, options->srt_sink, options->video, options->audio)) { goto end; } diff --git a/app/src/stream_sink.c b/app/src/stream_sink.c index fc72c7ab..38ec8a9c 100644 --- a/app/src/stream_sink.c +++ b/app/src/stream_sink.c @@ -181,26 +181,37 @@ sc_stream_sink_process_header(struct sc_stream_sink *sink) { } { - // Open the TCP server: this blocks until a client connects (or - // sink->stopped is set, via the interrupt callback) - char url[64]; - snprintf(url, sizeof(url), - "tcp://0.0.0.0:%" PRIu16 "?listen=1", sink->port); + // Build the SRT listener URL. If the user already specified + // mode=, use the URL as-is; otherwise append ?mode=listener + // so that scrcpy acts as the SRT server waiting for a player. + const char *connect_url = sink->url; + char *alloc_url = NULL; + if (!strstr(sink->url, "mode=")) { + const char *sep = strchr(sink->url, '?') ? "&" : "?"; + const char *suffix = "mode=listener"; + size_t len = strlen(sink->url) + strlen(sep) + strlen(suffix) + 1; + alloc_url = malloc(len); + if (!alloc_url) { + LOG_OOM(); + goto end; + } + snprintf(alloc_url, len, "%s%s%s", sink->url, sep, suffix); + connect_url = alloc_url; + } AVIOInterruptCB int_cb = { .callback = sc_stream_sink_interrupt_cb, .opaque = sink, }; - LOGI("Stream sink: waiting for client on tcp://127.0.0.1:%" PRIu16, - sink->port); + LOGI("SRT sink: waiting for client on %s", sink->url); - int r = avio_open2(&sink->ctx->pb, url, AVIO_FLAG_WRITE, + int r = avio_open2(&sink->ctx->pb, connect_url, AVIO_FLAG_WRITE, &int_cb, NULL); + free(alloc_url); if (r < 0) { if (!sink->stopped) { - LOGE("Failed to open stream server on port %" PRIu16, - sink->port); + LOGE("Failed to open SRT server on %s", sink->url); } goto end; } @@ -238,8 +249,7 @@ sc_stream_sink_process_packets(struct sc_stream_sink *sink) { return false; } - LOGI("Stream sink: streaming started on tcp://127.0.0.1:%" PRIu16, - sink->port); + LOGI("SRT sink: streaming started on %s", sink->url); AVPacket *video_pkt = NULL; AVPacket *audio_pkt = NULL; @@ -635,17 +645,21 @@ sc_stream_sink_audio_packet_sink_disable(struct sc_packet_sink *sink) { } bool -sc_stream_sink_init(struct sc_stream_sink *sink, uint16_t port, +sc_stream_sink_init(struct sc_stream_sink *sink, const char *url, bool video, bool audio) { assert(video || audio); - sink->port = port; + sink->url = strdup(url); + if (!sink->url) { + LOG_OOM(); + return false; + } sink->video = video; sink->audio = audio; bool ok = sc_mutex_init(&sink->mutex); if (!ok) { - return false; + goto error_url_free; } ok = sc_cond_init(&sink->cond); @@ -666,7 +680,7 @@ sc_stream_sink_init(struct sc_stream_sink *sink, uint16_t port, sc_stream_sink_stream_init(&sink->video_stream); sc_stream_sink_stream_init(&sink->audio_stream); - // Allocate the output format context with mpegts (ideal for TCP streaming) + // Allocate the output format context with mpegts (for network streaming) const AVOutputFormat *oformat = av_guess_format("mpegts", NULL, NULL); if (!oformat) { LOGE("Could not find mpegts muxer"); @@ -712,6 +726,8 @@ error_cond_destroy: sc_cond_destroy(&sink->cond); error_mutex_destroy: sc_mutex_destroy(&sink->mutex); +error_url_free: + free(sink->url); return false; } @@ -746,4 +762,5 @@ sc_stream_sink_destroy(struct sc_stream_sink *sink) { sc_cond_destroy(&sink->cond); sc_mutex_destroy(&sink->mutex); avformat_free_context(sink->ctx); + free(sink->url); } diff --git a/app/src/stream_sink.h b/app/src/stream_sink.h index f07f0ea4..7ac3c5ec 100644 --- a/app/src/stream_sink.h +++ b/app/src/stream_sink.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include #include @@ -34,7 +33,7 @@ struct sc_stream_sink { bool audio; bool video; - uint16_t port; + char *url; AVFormatContext *ctx; @@ -57,7 +56,7 @@ struct sc_stream_sink { }; bool -sc_stream_sink_init(struct sc_stream_sink *sink, uint16_t port, +sc_stream_sink_init(struct sc_stream_sink *sink, const char *url, bool video, bool audio); bool