Skip to content
Open
1 change: 1 addition & 0 deletions app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ src = [
'src/receiver.c',
'src/recorder.c',
'src/scrcpy.c',
'src/stream_sink.c',
'src/screen.c',
'src/server.c',
'src/version.c',
Expand Down
25 changes: 21 additions & 4 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ enum {
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY,
OPT_STREAM_SINK,
};

struct sc_option {
Expand Down Expand Up @@ -956,6 +957,17 @@ static const struct sc_option options[] = {
"Default is info.",
#endif
},
{
.longopt_id = OPT_STREAM_SINK,
.longopt = "stream-sink",
.argdesc = "url",
.text = "Stream the device video and audio as MPEG-TS to the given URL.\n"
"Supported protocols are srt, udp and tcp.\n"
"The URL is passed to the FFmpeg muxer, so it may contain "
"additional options (e.g. srt://HOST:PORT?latency=200).\n"
"For faster startup of clients, you may want to set "
"--video-codec-options=i-frame-interval:float=1.0."
},
{
.longopt_id = OPT_V4L2_SINK,
.longopt = "v4l2-sink",
Expand Down Expand Up @@ -2686,6 +2698,9 @@ 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_SINK:
opts->stream_sink = optarg;
break;
case OPT_V4L2_SINK:
#ifdef HAVE_V4L2
opts->v4l2_device = optarg;
Expand Down Expand Up @@ -2876,13 +2891,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->stream_sink) {
LOGI("No video playback, no recording, no V4L2 sink, no stream 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->stream_sink) {
LOGI("No audio playback, no recording, no stream sink: audio disabled");
opts->audio = false;
}

Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const struct scrcpy_options scrcpy_options_default = {
.v4l2_device = NULL,
.v4l2_buffer = 0,
#endif
.stream_sink = NULL,
#ifdef HAVE_USB
.otg = false,
#endif
Expand Down
1 change: 1 addition & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ struct scrcpy_options {
const char *v4l2_device;
sc_tick v4l2_buffer;
#endif
const char *stream_sink;
#ifdef HAVE_USB
bool otg;
#endif
Expand Down
36 changes: 36 additions & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "recorder.h"
#include "screen.h"
#include "server.h"
#include "stream_sink.h"
#include "uhid/gamepad_uhid.h"
#include "uhid/keyboard_uhid.h"
#include "uhid/mouse_uhid.h"
Expand Down Expand Up @@ -54,6 +55,7 @@ struct scrcpy {
struct sc_decoder video_decoder;
struct sc_decoder audio_decoder;
struct sc_recorder recorder;
struct sc_stream_sink stream_sink;
struct sc_delay_buffer video_buffer;
#ifdef HAVE_V4L2
struct sc_v4l2_sink v4l2_sink;
Expand Down Expand Up @@ -400,6 +402,8 @@ scrcpy(struct scrcpy_options *options) {
bool file_pusher_initialized = false;
bool recorder_initialized = false;
bool recorder_started = false;
bool stream_sink_initialized = false;
bool stream_sink_started = false;
#ifdef HAVE_V4L2
bool v4l2_sink_initialized = false;
#endif
Expand Down Expand Up @@ -632,6 +636,28 @@ scrcpy(struct scrcpy_options *options) {
}
}

if (options->stream_sink) {
if (!sc_stream_sink_init(&s->stream_sink, options->stream_sink,
options->video, options->audio)) {
goto end;
}
stream_sink_initialized = true;

if (!sc_stream_sink_start(&s->stream_sink)) {
goto end;
}
stream_sink_started = true;

if (options->video) {
sc_packet_source_add_sink(&s->video_demuxer.packet_source,
&s->stream_sink.video_packet_sink);
}
if (options->audio) {
sc_packet_source_add_sink(&s->audio_demuxer.packet_source,
&s->stream_sink.audio_packet_sink);
}
}

struct sc_controller *controller = NULL;
struct sc_key_processor *kp = NULL;
struct sc_mouse_processor *mp = NULL;
Expand Down Expand Up @@ -989,6 +1015,9 @@ scrcpy(struct scrcpy_options *options) {
if (recorder_initialized) {
sc_recorder_stop(&s->recorder);
}
if (stream_sink_initialized) {
sc_stream_sink_stop(&s->stream_sink);
}
if (screen_initialized) {
sc_screen_interrupt(&s->screen);
}
Expand Down Expand Up @@ -1053,6 +1082,13 @@ scrcpy(struct scrcpy_options *options) {
sc_recorder_destroy(&s->recorder);
}

if (stream_sink_started) {
sc_stream_sink_join(&s->stream_sink);
}
if (stream_sink_initialized) {
sc_stream_sink_destroy(&s->stream_sink);
}

if (file_pusher_initialized) {
sc_file_pusher_join(&s->file_pusher);
sc_file_pusher_destroy(&s->file_pusher);
Expand Down
Loading