mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-03 06:39:39 +00:00
Merge ad7212beb1
into a7e41b0f85
This commit is contained in:
commit
42050ba482
4 changed files with 179 additions and 62 deletions
|
@ -149,6 +149,10 @@ executable('scrcpy', src,
|
||||||
install: true,
|
install: true,
|
||||||
c_args: [])
|
c_args: [])
|
||||||
|
|
||||||
|
library('scrcpy', src,
|
||||||
|
dependencies: dependencies,
|
||||||
|
include_directories: src_dir)
|
||||||
|
|
||||||
install_man('scrcpy.1')
|
install_man('scrcpy.1')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#define DECODER_MAX_SINKS 2
|
#define DECODER_MAX_SINKS 5
|
||||||
|
|
||||||
struct decoder {
|
struct decoder {
|
||||||
struct sc_packet_sink packet_sink; // packet sink trait
|
struct sc_packet_sink packet_sink; // packet sink trait
|
||||||
|
|
200
app/src/scrcpy.c
200
app/src/scrcpy.c
|
@ -41,6 +41,24 @@ struct scrcpy {
|
||||||
struct controller controller;
|
struct controller controller;
|
||||||
struct file_handler file_handler;
|
struct file_handler file_handler;
|
||||||
struct input_manager input_manager;
|
struct input_manager input_manager;
|
||||||
|
// do not allocate this on stack, keep it in the struct
|
||||||
|
struct stream_callbacks stream_cbs;
|
||||||
|
|
||||||
|
// status of scrcpy process
|
||||||
|
bool server_started;
|
||||||
|
bool file_handler_initialized;
|
||||||
|
bool recorder_initialized;
|
||||||
|
#ifdef HAVE_V4L2
|
||||||
|
bool v4l2_sink_initialized;
|
||||||
|
#endif
|
||||||
|
bool stream_started;
|
||||||
|
bool controller_initialized;
|
||||||
|
bool controller_started;
|
||||||
|
bool screen_initialized;
|
||||||
|
|
||||||
|
// External sinks- allocated on HEAP. Remember them so that they can be freed later.
|
||||||
|
struct sc_frame_sink *external_sinks[DECODER_MAX_SINKS];
|
||||||
|
unsigned external_sink_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -239,28 +257,16 @@ stream_on_eos(struct stream *stream, void *userdata) {
|
||||||
SDL_PushEvent(&stop_event);
|
SDL_PushEvent(&stop_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
struct scrcpy_process *
|
||||||
scrcpy(const struct scrcpy_options *options) {
|
scrcpy_start(const struct scrcpy_options *options) {
|
||||||
static struct scrcpy scrcpy;
|
struct scrcpy *s = malloc(sizeof(struct scrcpy));
|
||||||
struct scrcpy *s = &scrcpy;
|
struct scrcpy_process *p = malloc(sizeof(struct scrcpy));
|
||||||
|
p->scrcpy_struct = s;
|
||||||
|
|
||||||
if (!server_init(&s->server)) {
|
if (!server_init(&s->server)) {
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
bool server_started = false;
|
|
||||||
bool file_handler_initialized = false;
|
|
||||||
bool recorder_initialized = false;
|
|
||||||
#ifdef HAVE_V4L2
|
|
||||||
bool v4l2_sink_initialized = false;
|
|
||||||
#endif
|
|
||||||
bool stream_started = false;
|
|
||||||
bool controller_initialized = false;
|
|
||||||
bool controller_started = false;
|
|
||||||
bool screen_initialized = false;
|
|
||||||
|
|
||||||
bool record = !!options->record_filename;
|
bool record = !!options->record_filename;
|
||||||
struct server_params params = {
|
struct server_params params = {
|
||||||
.serial = options->serial,
|
.serial = options->serial,
|
||||||
|
@ -281,29 +287,32 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
.power_off_on_close = options->power_off_on_close,
|
.power_off_on_close = options->power_off_on_close,
|
||||||
};
|
};
|
||||||
if (!server_start(&s->server, ¶ms)) {
|
if (!server_start(&s->server, ¶ms)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
server_started = true;
|
s->server_started = true;
|
||||||
|
|
||||||
if (!sdl_init_and_configure(options->display, options->render_driver,
|
if (!sdl_init_and_configure(options->display, options->render_driver,
|
||||||
options->disable_screensaver)) {
|
options->disable_screensaver)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
||||||
struct size frame_size;
|
|
||||||
|
|
||||||
if (!server_connect_to(&s->server, device_name, &frame_size)) {
|
if (!server_connect_to(&s->server, device_name, &p->frame_size)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->display && options->control) {
|
if (options->display && options->control) {
|
||||||
if (!file_handler_init(&s->file_handler, s->server.serial,
|
if (!file_handler_init(&s->file_handler, s->server.serial,
|
||||||
options->push_target)) {
|
options->push_target)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
file_handler_initialized = true;
|
s->file_handler_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct decoder *dec = NULL;
|
struct decoder *dec = NULL;
|
||||||
|
@ -311,6 +320,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
needs_decoder |= !!options->v4l2_device;
|
needs_decoder |= !!options->v4l2_device;
|
||||||
#endif
|
#endif
|
||||||
|
needs_decoder |= options->force_decoder;
|
||||||
if (needs_decoder) {
|
if (needs_decoder) {
|
||||||
decoder_init(&s->decoder);
|
decoder_init(&s->decoder);
|
||||||
dec = &s->decoder;
|
dec = &s->decoder;
|
||||||
|
@ -321,19 +331,19 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
if (!recorder_init(&s->recorder,
|
if (!recorder_init(&s->recorder,
|
||||||
options->record_filename,
|
options->record_filename,
|
||||||
options->record_format,
|
options->record_format,
|
||||||
frame_size)) {
|
p->frame_size)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
rec = &s->recorder;
|
rec = &s->recorder;
|
||||||
recorder_initialized = true;
|
s->recorder_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
av_log_set_callback(av_log_callback);
|
av_log_set_callback(av_log_callback);
|
||||||
|
|
||||||
static const struct stream_callbacks stream_cbs = {
|
// don't allocate callbacks on stack
|
||||||
.on_eos = stream_on_eos,
|
s->stream_cbs.on_eos = stream_on_eos;
|
||||||
};
|
stream_init(&s->stream, s->server.video_socket, &s->stream_cbs, NULL);
|
||||||
stream_init(&s->stream, s->server.video_socket, &stream_cbs, NULL);
|
|
||||||
|
|
||||||
if (dec) {
|
if (dec) {
|
||||||
stream_add_sink(&s->stream, &dec->packet_sink);
|
stream_add_sink(&s->stream, &dec->packet_sink);
|
||||||
|
@ -345,14 +355,16 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
|
|
||||||
if (options->control) {
|
if (options->control) {
|
||||||
if (!controller_init(&s->controller, s->server.control_socket)) {
|
if (!controller_init(&s->controller, s->server.control_socket)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
controller_initialized = true;
|
s->controller_initialized = true;
|
||||||
|
|
||||||
if (!controller_start(&s->controller)) {
|
if (!controller_start(&s->controller)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
controller_started = true;
|
s->controller_started = true;
|
||||||
|
|
||||||
if (options->turn_screen_off) {
|
if (options->turn_screen_off) {
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
|
@ -371,7 +383,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
|
|
||||||
struct screen_params screen_params = {
|
struct screen_params screen_params = {
|
||||||
.window_title = window_title,
|
.window_title = window_title,
|
||||||
.frame_size = frame_size,
|
.frame_size = p->frame_size,
|
||||||
.always_on_top = options->always_on_top,
|
.always_on_top = options->always_on_top,
|
||||||
.window_x = options->window_x,
|
.window_x = options->window_x,
|
||||||
.window_y = options->window_y,
|
.window_y = options->window_y,
|
||||||
|
@ -385,96 +397,168 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!screen_init(&s->screen, &screen_params)) {
|
if (!screen_init(&s->screen, &screen_params)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
screen_initialized = true;
|
s->screen_initialized = true;
|
||||||
|
|
||||||
decoder_add_sink(&s->decoder, &s->screen.frame_sink);
|
decoder_add_sink(&s->decoder, &s->screen.frame_sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (options->v4l2_device) {
|
if (options->v4l2_device) {
|
||||||
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, frame_size,
|
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, p->frame_size,
|
||||||
options->v4l2_buffer)) {
|
options->v4l2_buffer)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder_add_sink(&s->decoder, &s->v4l2_sink.frame_sink);
|
decoder_add_sink(&s->decoder, &s->v4l2_sink.frame_sink);
|
||||||
|
|
||||||
v4l2_sink_initialized = true;
|
s->v4l2_sink_initialized = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// now we consumed the header values, the socket receives the video stream
|
// now we consumed the header values, the socket receives the video stream
|
||||||
// start the stream
|
// start the stream
|
||||||
if (!stream_start(&s->stream)) {
|
if (!stream_start(&s->stream)) {
|
||||||
goto end;
|
scrcpy_stop(p);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
stream_started = true;
|
s->stream_started = true;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
scrcpy_loop(struct scrcpy_process *p, const struct scrcpy_options *options) {
|
||||||
|
struct scrcpy *s = p->scrcpy_struct;
|
||||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, options);
|
input_manager_init(&s->input_manager, &s->controller, &s->screen, options);
|
||||||
|
|
||||||
ret = event_loop(s, options);
|
int ret = event_loop(s, options);
|
||||||
LOGD("quit...");
|
LOGD("quit...");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
scrcpy_stop(struct scrcpy_process *p) {
|
||||||
|
struct scrcpy *s = p->scrcpy_struct;
|
||||||
|
|
||||||
|
if (s->screen_initialized) {
|
||||||
// Close the window immediately on closing, because screen_destroy() may
|
// Close the window immediately on closing, because screen_destroy() may
|
||||||
// only be called once the stream thread is joined (it may take time)
|
// only be called once the stream thread is joined (it may take time)
|
||||||
screen_hide_window(&s->screen);
|
screen_hide_window(&s->screen);
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
|
||||||
// The stream is not stopped explicitly, because it will stop by itself on
|
// The stream is not stopped explicitly, because it will stop by itself on
|
||||||
// end-of-stream
|
// end-of-stream
|
||||||
if (controller_started) {
|
if (s->controller_started) {
|
||||||
controller_stop(&s->controller);
|
controller_stop(&s->controller);
|
||||||
}
|
}
|
||||||
if (file_handler_initialized) {
|
if (s->file_handler_initialized) {
|
||||||
file_handler_stop(&s->file_handler);
|
file_handler_stop(&s->file_handler);
|
||||||
}
|
}
|
||||||
if (screen_initialized) {
|
if (s->screen_initialized) {
|
||||||
screen_interrupt(&s->screen);
|
screen_interrupt(&s->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_started) {
|
if (s->server_started) {
|
||||||
// shutdown the sockets and kill the server
|
// shutdown the sockets and kill the server
|
||||||
server_stop(&s->server);
|
server_stop(&s->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now that the sockets are shutdown, the stream and controller are
|
// now that the sockets are shutdown, the stream and controller are
|
||||||
// interrupted, we can join them
|
// interrupted, we can join them
|
||||||
if (stream_started) {
|
if (s->stream_started) {
|
||||||
stream_join(&s->stream);
|
stream_join(&s->stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (v4l2_sink_initialized) {
|
if (s->v4l2_sink_initialized) {
|
||||||
sc_v4l2_sink_destroy(&s->v4l2_sink);
|
sc_v4l2_sink_destroy(&s->v4l2_sink);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroy the screen only after the stream is guaranteed to be finished,
|
// Destroy the screen only after the stream is guaranteed to be finished,
|
||||||
// because otherwise the screen could receive new frames after destruction
|
// because otherwise the screen could receive new frames after destruction
|
||||||
if (screen_initialized) {
|
if (s->screen_initialized) {
|
||||||
screen_join(&s->screen);
|
screen_join(&s->screen);
|
||||||
screen_destroy(&s->screen);
|
screen_destroy(&s->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller_started) {
|
if (s->controller_started) {
|
||||||
controller_join(&s->controller);
|
controller_join(&s->controller);
|
||||||
}
|
}
|
||||||
if (controller_initialized) {
|
if (s->controller_initialized) {
|
||||||
controller_destroy(&s->controller);
|
controller_destroy(&s->controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorder_initialized) {
|
if (s->recorder_initialized) {
|
||||||
recorder_destroy(&s->recorder);
|
recorder_destroy(&s->recorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_handler_initialized) {
|
if (s->file_handler_initialized) {
|
||||||
file_handler_join(&s->file_handler);
|
file_handler_join(&s->file_handler);
|
||||||
file_handler_destroy(&s->file_handler);
|
file_handler_destroy(&s->file_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
server_destroy(&s->server);
|
server_destroy(&s->server);
|
||||||
|
|
||||||
|
// free up sinks
|
||||||
|
while (s->external_sink_count > 0) {
|
||||||
|
struct sc_frame_sink *sink = s->external_sinks[--s->external_sink_count];
|
||||||
|
const struct sc_frame_sink_ops *ops = sink->ops;
|
||||||
|
free((void*)ops);
|
||||||
|
free(sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// given that these structures were allocated in heap, free them
|
||||||
|
free(s);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
scrcpy(const struct scrcpy_options *options) {
|
||||||
|
struct scrcpy_process *p = scrcpy_start(options);
|
||||||
|
if (p == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ret = scrcpy_loop(p, options);
|
||||||
|
scrcpy_stop(p);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use void* so that external clients don't have to deal with scrcpy internals.
|
||||||
|
// TODO: how do I force JavaCPP AVFrame to use AVFrame type it already has from ffmpeg library mapping?
|
||||||
|
bool
|
||||||
|
scrcpy_add_sink(struct scrcpy_process *p,
|
||||||
|
bool (*open)(void *sink),
|
||||||
|
void (*close)(void *sink),
|
||||||
|
bool (*push)(void *sink, const void *avframe)
|
||||||
|
) {
|
||||||
|
struct scrcpy *s = p->scrcpy_struct;
|
||||||
|
|
||||||
|
if (s->external_sink_count >= DECODER_MAX_SINKS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_frame_sink *sink = malloc(sizeof(struct sc_frame_sink));
|
||||||
|
struct sc_frame_sink_ops *ops = malloc(sizeof(struct sc_frame_sink_ops));
|
||||||
|
|
||||||
|
ops->open = (bool (*)(struct sc_frame_sink *sink)) open;
|
||||||
|
ops->close = (void (*)(struct sc_frame_sink *sink)) close;
|
||||||
|
ops->push = (bool (*)(struct sc_frame_sink *sink, const AVFrame *frame))push;
|
||||||
|
sink->ops = ops;
|
||||||
|
s->external_sinks[s->external_sink_count++] = sink;
|
||||||
|
|
||||||
|
decoder_add_sink(&s->decoder, sink);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scrcpy_push_event(struct scrcpy_process *p,
|
||||||
|
const struct control_msg *msg) {
|
||||||
|
struct scrcpy *s = p->scrcpy_struct;
|
||||||
|
controller_push_msg(&s->controller, msg);
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#define SCRCPY_H
|
#define SCRCPY_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "coords.h"
|
||||||
|
#include "control_msg.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -87,6 +89,7 @@ struct scrcpy_options {
|
||||||
bool always_on_top;
|
bool always_on_top;
|
||||||
bool control;
|
bool control;
|
||||||
bool display;
|
bool display;
|
||||||
|
bool force_decoder; // force scrcpy to always initialize a decoder
|
||||||
bool turn_screen_off;
|
bool turn_screen_off;
|
||||||
bool prefer_text;
|
bool prefer_text;
|
||||||
bool window_borderless;
|
bool window_borderless;
|
||||||
|
@ -150,6 +153,32 @@ struct scrcpy_options {
|
||||||
.power_off_on_close = false, \
|
.power_off_on_close = false, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct scrcpy_process {
|
||||||
|
// To be cast to (struct scrcpy *)
|
||||||
|
// don't use the real struct type, as it's internal and should not be interfered by outside process;
|
||||||
|
// also, JavaCPP is unable to parse struct scrcpy properly.
|
||||||
|
void *scrcpy_struct;
|
||||||
|
// other properties to be available to external systems
|
||||||
|
struct size frame_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scrcpy_process *
|
||||||
|
scrcpy_start(const struct scrcpy_options *options);
|
||||||
|
|
||||||
|
// add an external frame sink.
|
||||||
|
bool
|
||||||
|
scrcpy_add_sink(struct scrcpy_process *p,
|
||||||
|
bool (*open)(void *sink),
|
||||||
|
void (*close)(void *sink),
|
||||||
|
bool (*push)(void *sink, const void *avframe));
|
||||||
|
|
||||||
|
void
|
||||||
|
scrcpy_push_event(struct scrcpy_process *p,
|
||||||
|
const struct control_msg *msg);
|
||||||
|
|
||||||
|
void
|
||||||
|
scrcpy_stop(struct scrcpy_process *p);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
scrcpy(const struct scrcpy_options *options);
|
scrcpy(const struct scrcpy_options *options);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue