2018-01-23 23:32:29 +08:00
|
|
|
#include "scrcpy.h"
|
2017-12-12 22:12:07 +08:00
|
|
|
|
2018-01-23 23:32:29 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2017-12-12 22:12:07 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <libavformat/avformat.h>
|
2018-01-23 23:32:29 +08:00
|
|
|
#include <sys/time.h>
|
2017-12-12 22:12:07 +08:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
2020-05-08 20:54:33 +08:00
|
|
|
#ifdef _WIN32
|
2020-06-20 04:04:06 +08:00
|
|
|
// not needed here, but winsock2.h must never be included AFTER windows.h
|
|
|
|
# include <winsock2.h>
|
2020-05-08 20:54:33 +08:00
|
|
|
# include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2018-02-08 18:26:31 +08:00
|
|
|
#include "controller.h"
|
2018-01-23 23:32:29 +08:00
|
|
|
#include "decoder.h"
|
2018-02-08 22:42:49 +08:00
|
|
|
#include "device.h"
|
2018-01-23 23:32:29 +08:00
|
|
|
#include "events.h"
|
2018-08-12 10:13:49 +08:00
|
|
|
#include "file_handler.h"
|
2018-08-15 23:01:54 +08:00
|
|
|
#include "fps_counter.h"
|
|
|
|
#include "input_manager.h"
|
2018-11-09 19:21:17 +08:00
|
|
|
#include "recorder.h"
|
2018-02-08 18:14:13 +08:00
|
|
|
#include "screen.h"
|
2018-01-23 23:32:29 +08:00
|
|
|
#include "server.h"
|
2019-03-02 23:43:43 +08:00
|
|
|
#include "stream.h"
|
2018-08-15 23:01:54 +08:00
|
|
|
#include "tiny_xpm.h"
|
2019-11-24 18:53:00 +08:00
|
|
|
#include "util/log.h"
|
|
|
|
#include "util/net.h"
|
2021-04-04 06:10:44 +08:00
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
# include "v4l2_sink.h"
|
|
|
|
#endif
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
static struct server server;
|
2021-02-16 01:28:41 +08:00
|
|
|
static struct screen screen;
|
2019-06-07 22:55:19 +08:00
|
|
|
static struct fps_counter fps_counter;
|
2019-03-02 23:43:43 +08:00
|
|
|
static struct stream stream;
|
2018-01-23 23:32:29 +08:00
|
|
|
static struct decoder decoder;
|
2019-03-02 23:43:43 +08:00
|
|
|
static struct recorder recorder;
|
2021-04-04 06:10:44 +08:00
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
static struct sc_v4l2_sink v4l2_sink;
|
|
|
|
#endif
|
2018-01-23 23:32:29 +08:00
|
|
|
static struct controller controller;
|
2018-08-12 10:13:49 +08:00
|
|
|
static struct file_handler file_handler;
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2018-02-15 19:07:47 +08:00
|
|
|
static struct input_manager input_manager = {
|
|
|
|
.controller = &controller,
|
2021-02-20 03:56:09 +08:00
|
|
|
.fps_counter = &fps_counter,
|
2018-02-15 19:07:47 +08:00
|
|
|
.screen = &screen,
|
2020-06-11 16:40:52 +08:00
|
|
|
.repeat = 0,
|
2020-07-17 06:00:42 +08:00
|
|
|
|
|
|
|
// initialized later
|
|
|
|
.prefer_text = false,
|
|
|
|
.sdl_shortcut_mods = {
|
|
|
|
.data = {0},
|
|
|
|
.count = 0,
|
|
|
|
},
|
2018-02-15 19:07:47 +08:00
|
|
|
};
|
|
|
|
|
2020-05-08 20:54:33 +08:00
|
|
|
#ifdef _WIN32
|
2020-05-11 07:32:54 +08:00
|
|
|
BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
2020-05-08 20:54:33 +08:00
|
|
|
if (ctrl_type == CTRL_C_EVENT) {
|
|
|
|
SDL_Event event;
|
|
|
|
event.type = SDL_QUIT;
|
|
|
|
SDL_PushEvent(&event);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
|
2019-03-03 08:40:03 +08:00
|
|
|
// init SDL and set appropriate hints
|
|
|
|
static bool
|
2020-06-13 20:10:41 +08:00
|
|
|
sdl_init_and_configure(bool display, const char *render_driver,
|
|
|
|
bool disable_screensaver) {
|
2019-03-03 08:40:03 +08:00
|
|
|
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
|
|
|
|
if (SDL_Init(flags)) {
|
|
|
|
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
atexit(SDL_Quit);
|
|
|
|
|
2020-05-08 20:54:33 +08:00
|
|
|
#ifdef _WIN32
|
|
|
|
// Clean up properly on Ctrl+C on Windows
|
|
|
|
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
|
|
|
|
if (!ok) {
|
|
|
|
LOGW("Could not set Ctrl+C handler");
|
|
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
|
2019-03-03 08:40:03 +08:00
|
|
|
if (!display) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-11 20:34:41 +08:00
|
|
|
if (render_driver && !SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver)) {
|
|
|
|
LOGW("Could not set render driver");
|
|
|
|
}
|
|
|
|
|
2020-02-25 19:18:49 +08:00
|
|
|
// Linear filtering
|
|
|
|
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
|
|
|
|
LOGW("Could not enable linear filtering");
|
2019-03-03 08:40:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SCRCPY_SDL_HAS_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
|
|
|
// Handle a click to gain focus as any other click
|
|
|
|
if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) {
|
|
|
|
LOGW("Could not enable mouse focus clickthrough");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-05-04 23:48:20 +08:00
|
|
|
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
|
|
|
// Disable compositor bypassing on X11
|
|
|
|
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
|
|
|
|
LOGW("Could not disable X11 compositor bypass");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-05-31 21:35:53 +08:00
|
|
|
// Do not minimize on focus loss
|
|
|
|
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
|
|
|
|
LOGW("Could not disable minimize on focus loss");
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:10:41 +08:00
|
|
|
if (disable_screensaver) {
|
|
|
|
LOGD("Screensaver disabled");
|
|
|
|
SDL_DisableScreenSaver();
|
|
|
|
} else {
|
|
|
|
LOGD("Screensaver enabled");
|
|
|
|
SDL_EnableScreenSaver();
|
|
|
|
}
|
2019-03-03 08:40:03 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
static bool
|
2019-03-03 03:09:56 +08:00
|
|
|
is_apk(const char *file) {
|
2018-08-12 10:40:00 +08:00
|
|
|
const char *ext = strrchr(file, '.');
|
|
|
|
return ext && !strcmp(ext, ".apk");
|
|
|
|
}
|
|
|
|
|
2019-03-03 04:49:39 +08:00
|
|
|
enum event_result {
|
|
|
|
EVENT_RESULT_CONTINUE,
|
|
|
|
EVENT_RESULT_STOPPED_BY_USER,
|
|
|
|
EVENT_RESULT_STOPPED_BY_EOS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum event_result
|
2020-07-27 14:26:25 +08:00
|
|
|
handle_event(SDL_Event *event, const struct scrcpy_options *options) {
|
2019-03-03 04:49:39 +08:00
|
|
|
switch (event->type) {
|
|
|
|
case EVENT_STREAM_STOPPED:
|
|
|
|
LOGD("Video stream stopped");
|
|
|
|
return EVENT_RESULT_STOPPED_BY_EOS;
|
|
|
|
case SDL_QUIT:
|
|
|
|
LOGD("User requested to quit");
|
|
|
|
return EVENT_RESULT_STOPPED_BY_USER;
|
|
|
|
case SDL_DROPFILE: {
|
2020-07-27 14:26:25 +08:00
|
|
|
if (!options->control) {
|
2019-03-03 05:40:51 +08:00
|
|
|
break;
|
|
|
|
}
|
2021-01-24 22:14:53 +08:00
|
|
|
char *file = strdup(event->drop.file);
|
|
|
|
SDL_free(event->drop.file);
|
|
|
|
if (!file) {
|
|
|
|
LOGW("Could not strdup drop filename\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-03-03 04:49:39 +08:00
|
|
|
file_handler_action_t action;
|
2021-01-24 22:14:53 +08:00
|
|
|
if (is_apk(file)) {
|
2019-03-03 04:49:39 +08:00
|
|
|
action = ACTION_INSTALL_APK;
|
|
|
|
} else {
|
|
|
|
action = ACTION_PUSH_FILE;
|
|
|
|
}
|
2021-01-24 22:14:53 +08:00
|
|
|
file_handler_request(&file_handler, action, file);
|
2021-04-14 04:17:46 +08:00
|
|
|
goto end;
|
2019-03-03 04:49:39 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-16 01:44:53 +08:00
|
|
|
|
|
|
|
bool consumed = screen_handle_event(&screen, event);
|
2021-02-16 01:53:23 +08:00
|
|
|
if (consumed) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
consumed = input_manager_handle_event(&input_manager, event);
|
2021-02-16 01:44:53 +08:00
|
|
|
(void) consumed;
|
|
|
|
|
2021-02-16 01:53:23 +08:00
|
|
|
end:
|
2019-03-03 04:49:39 +08:00
|
|
|
return EVENT_RESULT_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
static bool
|
2020-07-27 14:26:25 +08:00
|
|
|
event_loop(const struct scrcpy_options *options) {
|
2018-01-23 23:32:29 +08:00
|
|
|
SDL_Event event;
|
|
|
|
while (SDL_WaitEvent(&event)) {
|
2020-07-27 14:26:25 +08:00
|
|
|
enum event_result result = handle_event(&event, options);
|
2019-03-03 04:49:39 +08:00
|
|
|
switch (result) {
|
|
|
|
case EVENT_RESULT_STOPPED_BY_USER:
|
2019-03-03 06:52:22 +08:00
|
|
|
return true;
|
2019-03-03 04:49:39 +08:00
|
|
|
case EVENT_RESULT_STOPPED_BY_EOS:
|
2019-11-10 04:13:20 +08:00
|
|
|
LOGW("Device disconnected");
|
2019-03-03 06:52:22 +08:00
|
|
|
return false;
|
2019-03-03 04:49:39 +08:00
|
|
|
case EVENT_RESULT_CONTINUE:
|
2018-04-29 06:17:34 +08:00
|
|
|
break;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
}
|
2019-03-03 06:52:22 +08:00
|
|
|
return false;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static SDL_LogPriority
|
|
|
|
sdl_priority_from_av_level(int level) {
|
2019-02-09 19:06:21 +08:00
|
|
|
switch (level) {
|
|
|
|
case AV_LOG_PANIC:
|
|
|
|
case AV_LOG_FATAL:
|
|
|
|
return SDL_LOG_PRIORITY_CRITICAL;
|
|
|
|
case AV_LOG_ERROR:
|
|
|
|
return SDL_LOG_PRIORITY_ERROR;
|
|
|
|
case AV_LOG_WARNING:
|
|
|
|
return SDL_LOG_PRIORITY_WARN;
|
|
|
|
case AV_LOG_INFO:
|
|
|
|
return SDL_LOG_PRIORITY_INFO;
|
|
|
|
}
|
|
|
|
// do not forward others, which are too verbose
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
|
2019-11-26 16:08:12 +08:00
|
|
|
(void) avcl;
|
2019-02-09 19:06:21 +08:00
|
|
|
SDL_LogPriority priority = sdl_priority_from_av_level(level);
|
|
|
|
if (priority == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2021-01-24 22:14:53 +08:00
|
|
|
char *local_fmt = malloc(strlen(fmt) + 10);
|
2019-02-09 19:06:21 +08:00
|
|
|
if (!local_fmt) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGC("Could not allocate string");
|
2019-02-09 19:06:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// strcpy is safe here, the destination is large enough
|
|
|
|
strcpy(local_fmt, "[FFmpeg] ");
|
|
|
|
strcpy(local_fmt + 9, fmt);
|
|
|
|
SDL_LogMessageV(SDL_LOG_CATEGORY_VIDEO, priority, local_fmt, vl);
|
2021-01-24 22:14:53 +08:00
|
|
|
free(local_fmt);
|
2019-02-09 19:06:21 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
bool
|
2019-03-03 03:09:56 +08:00
|
|
|
scrcpy(const struct scrcpy_options *options) {
|
2021-01-01 23:34:47 +08:00
|
|
|
if (!server_init(&server)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-04 05:40:33 +08:00
|
|
|
bool ret = false;
|
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
bool server_started = false;
|
|
|
|
bool fps_counter_initialized = false;
|
|
|
|
bool file_handler_initialized = false;
|
|
|
|
bool recorder_initialized = false;
|
2021-04-04 06:10:44 +08:00
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
bool v4l2_sink_initialized = false;
|
|
|
|
#endif
|
2021-01-01 23:34:47 +08:00
|
|
|
bool stream_started = false;
|
|
|
|
bool controller_initialized = false;
|
|
|
|
bool controller_started = false;
|
2021-04-11 19:07:44 +08:00
|
|
|
bool screen_initialized = false;
|
2021-01-01 23:34:47 +08:00
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
bool record = !!options->record_filename;
|
2019-06-05 05:59:55 +08:00
|
|
|
struct server_params params = {
|
2020-05-25 03:51:40 +08:00
|
|
|
.log_level = options->log_level,
|
2019-06-05 05:59:55 +08:00
|
|
|
.crop = options->crop,
|
2019-12-10 04:16:09 +08:00
|
|
|
.port_range = options->port_range,
|
2019-06-05 05:59:55 +08:00
|
|
|
.max_size = options->max_size,
|
|
|
|
.bit_rate = options->bit_rate,
|
2019-11-18 05:07:19 +08:00
|
|
|
.max_fps = options->max_fps,
|
2020-02-16 19:30:36 +08:00
|
|
|
.lock_video_orientation = options->lock_video_orientation,
|
2019-06-05 03:31:46 +08:00
|
|
|
.control = options->control,
|
2020-02-24 19:16:38 +08:00
|
|
|
.display_id = options->display_id,
|
2020-05-02 05:49:37 +08:00
|
|
|
.show_touches = options->show_touches,
|
2020-05-02 07:54:48 +08:00
|
|
|
.stay_awake = options->stay_awake,
|
2020-04-26 20:22:08 +08:00
|
|
|
.codec_options = options->codec_options,
|
2020-10-12 17:23:06 +08:00
|
|
|
.encoder_name = options->encoder_name,
|
2020-05-25 05:27:34 +08:00
|
|
|
.force_adb_forward = options->force_adb_forward,
|
2021-02-21 08:42:04 +08:00
|
|
|
.power_off_on_close = options->power_off_on_close,
|
2019-06-05 05:59:55 +08:00
|
|
|
};
|
|
|
|
if (!server_start(&server, options->serial, ¶ms)) {
|
2021-01-01 23:34:47 +08:00
|
|
|
goto end;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
server_started = true;
|
2018-02-09 19:59:36 +08:00
|
|
|
|
2020-06-13 20:10:41 +08:00
|
|
|
if (!sdl_init_and_configure(options->display, options->render_driver,
|
|
|
|
options->disable_screensaver)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
Improve startup time
On startup, the client has to:
1. listen on a port
2. push and start the server to the device
3. wait for the server to connect (accept)
4. read device name and size
5. initialize SDL
6. initialize the window and renderer
7. show the window
From the execution of the app_process command to start the server on the
device, to the execution of the java main method, it takes ~800ms. As a
consequence, step 3 also takes ~800ms on the client.
Once complete, the client initializes SDL, which takes ~500ms.
These two expensive actions are executed sequentially:
HOST DEVICE
listen on port | |
push/start the server |----------------->|| app_process loads the jar
accept the connection . ^ ||
. | ||
. | WASTE ||
. | OF ||
. | TIME ||
. | ||
. | ||
. v X execution of our java main
connection accepted |<-----------------| connect to the host
init SDL || |
|| ,----------------| send frames
|| |,---------------|
|| ||,--------------|
|| |||,-------------|
|| ||||,------------|
init window/renderer | |||||,-----------|
display frames |<++++++-----------|
(many frames skipped)
The rationale for step 3 occuring before step 5 is that initializing
SDL replaces the SIGTERM handler to receive the event in the event loop,
so pressing Ctrl+C during step 5 would not work (since it blocks the
event loop).
But this is not so important; let's parallelize the SDL initialization
with the app_process execution (we'll just add a timeout to the
connection):
HOST DEVICE
listen on port | |
push/start the server |----------------->||app_process loads the jar
init SDL || ||
|| ||
|| ||
|| ||
|| ||
|| ||
accept the connection . ||
. X execution of our java main
connection accepted |<-----------------| connect to the host
init window/renderer | |
display frames |<-----------------| send frames
|<-----------------|
In addition, show the window only once the first frame is available to
avoid flickering (opening a black window for 100~200ms).
Note: the window and renderer are initialized after the connection is
accepted because they use the device information received from the
device.
2018-02-09 20:50:54 +08:00
|
|
|
}
|
|
|
|
|
2019-05-28 19:41:19 +08:00
|
|
|
if (!server_connect_to(&server)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
2018-02-08 18:14:13 +08:00
|
|
|
struct size frame_size;
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
// screenrecord does not send frames when the screen content does not
|
|
|
|
// change therefore, we transmit the screen size before the video stream,
|
|
|
|
// to be able to init the window immediately
|
2019-05-29 03:03:54 +08:00
|
|
|
if (!device_read_info(server.video_socket, device_name, &frame_size)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2019-06-05 03:49:26 +08:00
|
|
|
if (options->display) {
|
2019-06-07 22:55:19 +08:00
|
|
|
if (!fps_counter_init(&fps_counter)) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
fps_counter_initialized = true;
|
|
|
|
|
2019-06-05 03:49:26 +08:00
|
|
|
if (options->control) {
|
2019-07-31 07:48:32 +08:00
|
|
|
if (!file_handler_init(&file_handler, server.serial,
|
|
|
|
options->push_target)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
file_handler_initialized = true;
|
2019-03-03 01:06:29 +08:00
|
|
|
}
|
2021-04-04 06:10:44 +08:00
|
|
|
}
|
2018-04-29 06:17:34 +08:00
|
|
|
|
2021-04-04 06:10:44 +08:00
|
|
|
struct decoder *dec = NULL;
|
|
|
|
bool needs_decoder = options->display;
|
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
needs_decoder |= !!options->v4l2_device;
|
|
|
|
#endif
|
|
|
|
if (needs_decoder) {
|
2021-04-11 21:01:05 +08:00
|
|
|
decoder_init(&decoder);
|
2019-03-03 01:06:29 +08:00
|
|
|
dec = &decoder;
|
|
|
|
}
|
2019-03-02 23:43:43 +08:00
|
|
|
|
2018-11-09 19:21:17 +08:00
|
|
|
struct recorder *rec = NULL;
|
2019-03-03 01:18:12 +08:00
|
|
|
if (record) {
|
2019-02-09 22:20:07 +08:00
|
|
|
if (!recorder_init(&recorder,
|
|
|
|
options->record_filename,
|
|
|
|
options->record_format,
|
|
|
|
frame_size)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2018-11-09 19:21:17 +08:00
|
|
|
}
|
|
|
|
rec = &recorder;
|
2019-05-30 17:18:54 +08:00
|
|
|
recorder_initialized = true;
|
2018-11-09 19:21:17 +08:00
|
|
|
}
|
|
|
|
|
2019-02-09 19:06:21 +08:00
|
|
|
av_log_set_callback(av_log_callback);
|
|
|
|
|
2021-04-11 21:01:05 +08:00
|
|
|
stream_init(&stream, server.video_socket);
|
|
|
|
|
|
|
|
if (dec) {
|
|
|
|
stream_add_sink(&stream, &dec->packet_sink);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rec) {
|
|
|
|
stream_add_sink(&stream, &rec->packet_sink);
|
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-06-05 03:49:26 +08:00
|
|
|
if (options->display) {
|
|
|
|
if (options->control) {
|
2019-05-29 03:03:54 +08:00
|
|
|
if (!controller_init(&controller, server.control_socket)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2019-03-03 05:40:51 +08:00
|
|
|
}
|
2019-06-07 06:03:21 +08:00
|
|
|
controller_initialized = true;
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!controller_start(&controller)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2019-03-03 05:40:51 +08:00
|
|
|
}
|
2019-06-07 06:03:21 +08:00
|
|
|
controller_started = true;
|
2019-03-03 01:06:29 +08:00
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-06-24 01:02:34 +08:00
|
|
|
const char *window_title =
|
|
|
|
options->window_title ? options->window_title : device_name;
|
|
|
|
|
2021-02-26 05:00:34 +08:00
|
|
|
struct screen_params screen_params = {
|
|
|
|
.window_title = window_title,
|
|
|
|
.frame_size = frame_size,
|
|
|
|
.always_on_top = options->always_on_top,
|
|
|
|
.window_x = options->window_x,
|
|
|
|
.window_y = options->window_y,
|
|
|
|
.window_width = options->window_width,
|
|
|
|
.window_height = options->window_height,
|
|
|
|
.window_borderless = options->window_borderless,
|
|
|
|
.rotation = options->rotation,
|
|
|
|
.mipmaps = options->mipmaps,
|
2021-04-14 04:10:45 +08:00
|
|
|
.fullscreen = options->fullscreen,
|
2021-02-26 05:00:34 +08:00
|
|
|
};
|
|
|
|
|
2021-04-11 21:01:05 +08:00
|
|
|
if (!screen_init(&screen, &fps_counter, &screen_params)) {
|
2019-05-30 17:18:54 +08:00
|
|
|
goto end;
|
2019-03-03 01:06:29 +08:00
|
|
|
}
|
2021-04-11 19:07:44 +08:00
|
|
|
screen_initialized = true;
|
2019-03-03 01:06:29 +08:00
|
|
|
|
2021-04-11 21:01:05 +08:00
|
|
|
decoder_add_sink(&decoder, &screen.frame_sink);
|
|
|
|
|
2019-06-05 06:55:46 +08:00
|
|
|
if (options->turn_screen_off) {
|
|
|
|
struct control_msg msg;
|
|
|
|
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
|
|
|
|
msg.set_screen_power_mode.mode = SCREEN_POWER_MODE_OFF;
|
|
|
|
|
|
|
|
if (!controller_push_msg(&controller, &msg)) {
|
2019-06-24 02:49:38 +08:00
|
|
|
LOGW("Could not request 'set screen power mode'");
|
2019-06-05 06:55:46 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-05 19:12:08 +08:00
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2021-04-04 06:10:44 +08:00
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
if (options->v4l2_device) {
|
|
|
|
if (!sc_v4l2_sink_init(&v4l2_sink, options->v4l2_device, frame_size)) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder_add_sink(&decoder, &v4l2_sink.frame_sink);
|
|
|
|
|
|
|
|
v4l2_sink_initialized = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-02-20 05:40:12 +08:00
|
|
|
// now we consumed the header values, the socket receives the video stream
|
|
|
|
// start the stream
|
|
|
|
if (!stream_start(&stream)) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
stream_started = true;
|
|
|
|
|
2020-08-02 21:45:31 +08:00
|
|
|
input_manager_init(&input_manager, options);
|
2019-11-08 02:01:35 +08:00
|
|
|
|
2021-01-04 05:40:33 +08:00
|
|
|
ret = event_loop(options);
|
2018-02-13 17:10:18 +08:00
|
|
|
LOGD("quit...");
|
2018-03-25 21:23:00 +08:00
|
|
|
|
2021-04-14 04:22:54 +08:00
|
|
|
// Close the window immediately on closing, because screen_destroy() may
|
|
|
|
// only be called once the stream thread is joined (it may take time)
|
|
|
|
screen_hide_window(&screen);
|
|
|
|
|
2019-05-30 17:18:54 +08:00
|
|
|
end:
|
2021-04-11 21:01:05 +08:00
|
|
|
// The stream is not stopped explicitly, because it will stop by itself on
|
|
|
|
// end-of-stream
|
2019-05-30 17:18:54 +08:00
|
|
|
if (controller_started) {
|
2019-03-03 01:06:29 +08:00
|
|
|
controller_stop(&controller);
|
2019-05-30 17:18:54 +08:00
|
|
|
}
|
|
|
|
if (file_handler_initialized) {
|
|
|
|
file_handler_stop(&file_handler);
|
|
|
|
}
|
2019-06-07 22:55:19 +08:00
|
|
|
if (fps_counter_initialized) {
|
|
|
|
fps_counter_interrupt(&fps_counter);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
|
2021-01-01 23:34:47 +08:00
|
|
|
if (server_started) {
|
|
|
|
// shutdown the sockets and kill the server
|
|
|
|
server_stop(&server);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
|
|
|
|
// now that the sockets are shutdown, the stream and controller are
|
|
|
|
// interrupted, we can join them
|
|
|
|
if (stream_started) {
|
|
|
|
stream_join(&stream);
|
|
|
|
}
|
2021-04-11 20:32:42 +08:00
|
|
|
|
2021-04-04 06:10:44 +08:00
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
if (v4l2_sink_initialized) {
|
|
|
|
sc_v4l2_sink_destroy(&v4l2_sink);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-04-11 20:32:42 +08:00
|
|
|
// Destroy the screen only after the stream is guaranteed to be finished,
|
|
|
|
// because otherwise the screen could receive new frames after destruction
|
|
|
|
if (screen_initialized) {
|
|
|
|
screen_destroy(&screen);
|
|
|
|
}
|
|
|
|
|
2019-05-30 17:18:54 +08:00
|
|
|
if (controller_started) {
|
2019-03-03 01:06:29 +08:00
|
|
|
controller_join(&controller);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
if (controller_initialized) {
|
2019-03-03 01:06:29 +08:00
|
|
|
controller_destroy(&controller);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
|
|
|
|
if (recorder_initialized) {
|
2018-11-09 19:21:17 +08:00
|
|
|
recorder_destroy(&recorder);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
|
|
|
|
if (file_handler_initialized) {
|
2019-03-03 01:06:29 +08:00
|
|
|
file_handler_join(&file_handler);
|
|
|
|
file_handler_destroy(&file_handler);
|
|
|
|
}
|
2019-05-30 17:18:54 +08:00
|
|
|
|
2019-06-07 22:55:19 +08:00
|
|
|
if (fps_counter_initialized) {
|
|
|
|
fps_counter_join(&fps_counter);
|
|
|
|
fps_counter_destroy(&fps_counter);
|
|
|
|
}
|
|
|
|
|
2018-02-09 19:59:36 +08:00
|
|
|
server_destroy(&server);
|
2017-12-18 18:29:34 +08:00
|
|
|
|
2018-01-23 23:32:29 +08:00
|
|
|
return ret;
|
2017-12-12 22:12:07 +08:00
|
|
|
}
|