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>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-13 09:43:53 +00:00
parent 2fcba7e5b3
commit eb585c2c4f
6 changed files with 59 additions and 39 deletions

View file

@ -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:<port> (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;
}

View file

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

View file

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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -4,7 +4,6 @@
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <libavcodec/packet.h>
#include <libavformat/avformat.h>
@ -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