mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
Merge 84d199dc78 into 4671927c34
This commit is contained in:
commit
464fa9d61f
8 changed files with 1383 additions and 5 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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",
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -989,6 +1015,9 @@ end:
|
|||
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);
|
||||
}
|
||||
|
|
@ -1053,6 +1082,13 @@ end:
|
|||
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);
|
||||
|
|
|
|||
1229
app/src/stream_sink.c
Normal file
1229
app/src/stream_sink.c
Normal file
File diff suppressed because it is too large
Load diff
93
app/src/stream_sink.h
Normal file
93
app/src/stream_sink.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef SC_STREAM_SINK_H
|
||||
#define SC_STREAM_SINK_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <libavcodec/packet.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "trait/packet_sink.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/vecdeque.h"
|
||||
|
||||
struct sc_stream_sink_queue SC_VECDEQUE(AVPacket *);
|
||||
|
||||
struct sc_stream_sink_stream {
|
||||
int index;
|
||||
int64_t last_pts;
|
||||
};
|
||||
|
||||
/* Per-connection client state (defined in stream_sink.c). */
|
||||
struct sc_stream_sink_client;
|
||||
|
||||
struct sc_stream_sink {
|
||||
struct sc_packet_sink video_packet_sink;
|
||||
struct sc_packet_sink audio_packet_sink;
|
||||
|
||||
/* The audio flag is unprotected:
|
||||
* - it is initialized from sc_stream_sink_init() from the main thread;
|
||||
* - it may be reset once from the stream sink thread if the audio is
|
||||
* disabled dynamically.
|
||||
*
|
||||
* Therefore, once the stream sink thread is started, only the stream sink
|
||||
* thread may access it without data races.
|
||||
*/
|
||||
bool audio;
|
||||
bool video;
|
||||
|
||||
char *url;
|
||||
|
||||
// Template format context (no pb): holds stream definitions and codec
|
||||
// parameters used to initialise a fresh context for each new connection.
|
||||
AVFormatContext *ctx;
|
||||
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond cond;
|
||||
// set on sc_stream_sink_stop(), packet_sink close or streaming failure
|
||||
atomic_bool stopped;
|
||||
|
||||
// Init-phase queues: used only until template_ready is set.
|
||||
// After that, each sc_stream_sink_client has its own queues.
|
||||
struct sc_stream_sink_queue video_queue;
|
||||
struct sc_stream_sink_queue audio_queue;
|
||||
|
||||
// wake up the stream sink thread once the video or audio codec is known
|
||||
bool video_init;
|
||||
bool audio_init;
|
||||
|
||||
bool audio_expects_config_packet;
|
||||
|
||||
// Stream indices shared by every per-client AVFormatContext (all clients
|
||||
// copy the template streams in the same order).
|
||||
struct sc_stream_sink_stream video_stream;
|
||||
struct sc_stream_sink_stream audio_stream;
|
||||
|
||||
// Set to true once codec params + extradata are applied to the template
|
||||
// context. Before this point packets are buffered in the init-phase queues
|
||||
// above; after this point they are fanned out to active client queues.
|
||||
bool template_ready;
|
||||
|
||||
// Linked list of currently active client connections (protected by mutex).
|
||||
struct sc_stream_sink_client *clients;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_stream_sink_init(struct sc_stream_sink *sink, const char *url,
|
||||
bool video, bool audio);
|
||||
|
||||
bool
|
||||
sc_stream_sink_start(struct sc_stream_sink *sink);
|
||||
|
||||
void
|
||||
sc_stream_sink_stop(struct sc_stream_sink *sink);
|
||||
|
||||
void
|
||||
sc_stream_sink_join(struct sc_stream_sink *sink);
|
||||
|
||||
void
|
||||
sc_stream_sink_destroy(struct sc_stream_sink *sink);
|
||||
|
||||
#endif
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "trait/packet_sink.h"
|
||||
|
||||
#define SC_PACKET_SOURCE_MAX_SINKS 2
|
||||
#define SC_PACKET_SOURCE_MAX_SINKS 3
|
||||
|
||||
/**
|
||||
* Packet source trait
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue