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>
|
|
|
|
|
2018-01-23 23:32:29 +08:00
|
|
|
#include "command.h"
|
|
|
|
#include "common.h"
|
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-02-13 17:10:18 +08:00
|
|
|
#include "log.h"
|
2018-08-15 23:01:54 +08:00
|
|
|
#include "lock_util.h"
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
#include "net.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-03-02 22:16:55 +08:00
|
|
|
#include "video_buffer.h"
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2018-02-08 22:16:27 +08:00
|
|
|
static struct server server = SERVER_INITIALIZER;
|
2018-02-08 18:14:13 +08:00
|
|
|
static struct screen screen = SCREEN_INITIALIZER;
|
2019-03-02 22:16:55 +08:00
|
|
|
static struct video_buffer video_buffer;
|
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;
|
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,
|
2019-03-02 22:16:55 +08:00
|
|
|
.video_buffer = &video_buffer,
|
2018-02-15 19:07:47 +08:00
|
|
|
.screen = &screen,
|
2019-03-03 05:40:51 +08:00
|
|
|
.control = SDL_TRUE,
|
2018-02-15 19:07:47 +08:00
|
|
|
};
|
|
|
|
|
2018-03-14 04:26:05 +08:00
|
|
|
#if defined(__APPLE__) || defined(__WINDOWS__)
|
|
|
|
# define CONTINUOUS_RESIZING_WORKAROUND
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
|
|
|
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
|
|
|
|
// not triggered. As a workaround, handle them in an event handler.
|
|
|
|
//
|
|
|
|
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
|
|
|
|
// <https://stackoverflow.com/a/40693139/1987178>
|
2019-03-03 03:09:56 +08:00
|
|
|
static int
|
|
|
|
event_watcher(void *data, SDL_Event *event) {
|
|
|
|
if (event->type == SDL_WINDOWEVENT
|
|
|
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
2018-03-14 04:26:05 +08:00
|
|
|
// called from another thread, not very safe, but it's a workaround!
|
|
|
|
screen_render(&screen);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static SDL_bool
|
|
|
|
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
|
2019-03-03 05:40:51 +08:00
|
|
|
handle_event(SDL_Event *event, SDL_bool control) {
|
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 EVENT_NEW_FRAME:
|
|
|
|
if (!screen.has_frame) {
|
|
|
|
screen.has_frame = SDL_TRUE;
|
|
|
|
// this is the very first frame, show the window
|
|
|
|
screen_show_window(&screen);
|
|
|
|
}
|
|
|
|
if (!screen_update_frame(&screen, &video_buffer)) {
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
switch (event->window.event) {
|
|
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
|
|
screen_render(&screen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_TEXTINPUT:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 04:49:39 +08:00
|
|
|
input_manager_process_text_input(&input_manager, &event->text);
|
|
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
case SDL_KEYUP:
|
2019-03-03 05:40:51 +08:00
|
|
|
// some key events do not interact with the device, so process the
|
|
|
|
// event even if control is disabled
|
2019-03-03 04:49:39 +08:00
|
|
|
input_manager_process_key(&input_manager, &event->key);
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 04:49:39 +08:00
|
|
|
input_manager_process_mouse_motion(&input_manager, &event->motion);
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 04:49:39 +08:00
|
|
|
input_manager_process_mouse_wheel(&input_manager, &event->wheel);
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
2019-03-03 05:40:51 +08:00
|
|
|
// some mouse events do not interact with the device, so process
|
|
|
|
// the event even if control is disabled
|
2019-03-03 04:49:39 +08:00
|
|
|
input_manager_process_mouse_button(&input_manager, &event->button);
|
|
|
|
break;
|
|
|
|
case SDL_DROPFILE: {
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!control) {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 04:49:39 +08:00
|
|
|
file_handler_action_t action;
|
|
|
|
if (is_apk(event->drop.file)) {
|
|
|
|
action = ACTION_INSTALL_APK;
|
|
|
|
} else {
|
|
|
|
action = ACTION_PUSH_FILE;
|
|
|
|
}
|
|
|
|
file_handler_request(&file_handler, action, event->drop.file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EVENT_RESULT_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static SDL_bool
|
2019-03-03 05:40:51 +08:00
|
|
|
event_loop(SDL_bool display, SDL_bool control) {
|
2018-03-14 04:26:05 +08:00
|
|
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
2019-03-03 04:32:31 +08:00
|
|
|
if (display) {
|
|
|
|
SDL_AddEventWatch(event_watcher, NULL);
|
|
|
|
}
|
2018-03-14 04:26:05 +08:00
|
|
|
#endif
|
2018-01-23 23:32:29 +08:00
|
|
|
SDL_Event event;
|
|
|
|
while (SDL_WaitEvent(&event)) {
|
2019-03-03 05:40:51 +08:00
|
|
|
enum event_result result = handle_event(&event, control);
|
2019-03-03 04:49:39 +08:00
|
|
|
switch (result) {
|
|
|
|
case EVENT_RESULT_STOPPED_BY_USER:
|
2018-07-20 22:33:55 +08:00
|
|
|
return SDL_TRUE;
|
2019-03-03 04:49:39 +08:00
|
|
|
case EVENT_RESULT_STOPPED_BY_EOS:
|
|
|
|
return SDL_FALSE;
|
|
|
|
case EVENT_RESULT_CONTINUE:
|
2018-04-29 06:17:34 +08:00
|
|
|
break;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-20 22:33:55 +08:00
|
|
|
return SDL_FALSE;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static process_t
|
|
|
|
set_show_touches_enabled(const char *serial, SDL_bool enabled) {
|
2018-03-25 21:23:00 +08:00
|
|
|
const char *value = enabled ? "1" : "0";
|
|
|
|
const char *const adb_cmd[] = {
|
|
|
|
"shell", "settings", "put", "system", "show_touches", value
|
|
|
|
};
|
2018-03-25 22:20:34 +08:00
|
|
|
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
static void
|
|
|
|
wait_show_touches(process_t process) {
|
2018-03-25 22:20:34 +08:00
|
|
|
// reap the process, ignore the result
|
|
|
|
process_check_success(process, "show_touches");
|
2018-03-25 21:23:00 +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) {
|
|
|
|
SDL_LogPriority priority = sdl_priority_from_av_level(level);
|
|
|
|
if (priority == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
char *local_fmt = SDL_malloc(strlen(fmt) + 10);
|
|
|
|
if (!local_fmt) {
|
|
|
|
LOGC("Cannot allocate string");
|
|
|
|
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);
|
|
|
|
SDL_free(local_fmt);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
SDL_bool
|
|
|
|
scrcpy(const struct scrcpy_options *options) {
|
2019-03-03 01:18:12 +08:00
|
|
|
SDL_bool record = !!options->record_filename;
|
2018-03-21 21:04:13 +08:00
|
|
|
if (!server_start(&server, options->serial, options->port,
|
2018-11-11 21:41:54 +08:00
|
|
|
options->max_size, options->bit_rate, options->crop,
|
2019-03-03 01:18:12 +08:00
|
|
|
record)) {
|
2018-01-23 23:32:29 +08:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-05-27 21:46:07 +08:00
|
|
|
process_t proc_show_touches = PROCESS_NONE;
|
2018-03-25 22:20:34 +08:00
|
|
|
SDL_bool show_touches_waited;
|
|
|
|
if (options->show_touches) {
|
|
|
|
LOGI("Enable show_touches");
|
|
|
|
proc_show_touches = set_show_touches_enabled(options->serial, SDL_TRUE);
|
|
|
|
show_touches_waited = SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-02-09 19:59:36 +08:00
|
|
|
SDL_bool ret = SDL_TRUE;
|
|
|
|
|
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
|
|
|
if (!sdl_init_and_configure()) {
|
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_destroy_server;
|
|
|
|
}
|
|
|
|
|
2018-03-22 04:43:12 +08:00
|
|
|
socket_t device_socket = server_connect_to(&server);
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
if (device_socket == INVALID_SOCKET) {
|
2018-03-12 17:19:12 +08:00
|
|
|
server_stop(&server);
|
2018-02-09 19:59:36 +08:00
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_destroy_server;
|
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
|
2018-02-08 22:42:49 +08:00
|
|
|
if (!device_read_info(device_socket, device_name, &frame_size)) {
|
2018-03-12 17:19:12 +08:00
|
|
|
server_stop(&server);
|
2018-02-09 19:59:36 +08:00
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_destroy_server;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 05:46:46 +08:00
|
|
|
SDL_bool display = !options->no_display;
|
2019-03-03 05:40:51 +08:00
|
|
|
SDL_bool control = !options->no_control;
|
|
|
|
|
|
|
|
input_manager.control = control;
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 01:06:29 +08:00
|
|
|
struct decoder *dec = NULL;
|
|
|
|
if (display) {
|
|
|
|
if (!video_buffer_init(&video_buffer)) {
|
|
|
|
server_stop(&server);
|
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_destroy_server;
|
|
|
|
}
|
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
if (control && !file_handler_init(&file_handler, server.serial)) {
|
2019-03-03 01:06:29 +08:00
|
|
|
ret = SDL_FALSE;
|
|
|
|
server_stop(&server);
|
|
|
|
goto finally_destroy_video_buffer;
|
|
|
|
}
|
2018-04-29 06:17:34 +08:00
|
|
|
|
2019-03-03 01:06:29 +08:00
|
|
|
decoder_init(&decoder, &video_buffer);
|
|
|
|
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)) {
|
2018-11-09 19:21:17 +08:00
|
|
|
ret = SDL_FALSE;
|
|
|
|
server_stop(&server);
|
|
|
|
goto finally_destroy_file_handler;
|
|
|
|
}
|
|
|
|
rec = &recorder;
|
|
|
|
}
|
|
|
|
|
2019-02-09 19:06:21 +08:00
|
|
|
av_log_set_callback(av_log_callback);
|
|
|
|
|
2019-03-03 01:06:29 +08:00
|
|
|
stream_init(&stream, device_socket, dec, rec);
|
2018-01-23 23:32:29 +08:00
|
|
|
|
|
|
|
// now we consumed the header values, the socket receives the video stream
|
2019-03-02 23:43:43 +08:00
|
|
|
// start the stream
|
|
|
|
if (!stream_start(&stream)) {
|
2018-01-23 23:32:29 +08:00
|
|
|
ret = SDL_FALSE;
|
2018-03-12 17:19:12 +08:00
|
|
|
server_stop(&server);
|
2018-11-09 19:21:17 +08:00
|
|
|
goto finally_destroy_recorder;
|
2018-01-23 23:32:29 +08:00
|
|
|
}
|
|
|
|
|
2019-03-03 01:06:29 +08:00
|
|
|
if (display) {
|
2019-03-03 05:40:51 +08:00
|
|
|
if (control) {
|
|
|
|
if (!controller_init(&controller, device_socket)) {
|
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_stop_stream;
|
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
if (!controller_start(&controller)) {
|
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_destroy_controller;
|
|
|
|
}
|
2019-03-03 01:06:29 +08:00
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 03:09:56 +08:00
|
|
|
if (!screen_init_rendering(&screen, device_name, frame_size,
|
|
|
|
options->always_on_top)) {
|
2019-03-03 01:06:29 +08:00
|
|
|
ret = SDL_FALSE;
|
|
|
|
goto finally_stop_and_join_controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options->fullscreen) {
|
|
|
|
screen_switch_fullscreen(&screen);
|
|
|
|
}
|
2018-02-05 19:12:08 +08:00
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2018-03-25 21:23:00 +08:00
|
|
|
if (options->show_touches) {
|
2018-03-25 22:20:34 +08:00
|
|
|
wait_show_touches(proc_show_touches);
|
|
|
|
show_touches_waited = SDL_TRUE;
|
2018-03-25 21:23:00 +08:00
|
|
|
}
|
2018-01-23 23:32:29 +08:00
|
|
|
|
2019-03-03 05:40:51 +08:00
|
|
|
ret = event_loop(display, control);
|
2018-02-13 17:10:18 +08:00
|
|
|
LOGD("quit...");
|
2018-03-25 21:23:00 +08:00
|
|
|
|
2018-03-25 21:59:43 +08:00
|
|
|
screen_destroy(&screen);
|
|
|
|
|
2018-02-08 18:14:13 +08:00
|
|
|
finally_stop_and_join_controller:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (display && control) {
|
2019-03-03 01:06:29 +08:00
|
|
|
controller_stop(&controller);
|
|
|
|
controller_join(&controller);
|
|
|
|
}
|
2018-02-08 18:14:13 +08:00
|
|
|
finally_destroy_controller:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (display && control) {
|
2019-03-03 01:06:29 +08:00
|
|
|
controller_destroy(&controller);
|
|
|
|
}
|
2019-03-02 23:43:43 +08:00
|
|
|
finally_stop_stream:
|
|
|
|
stream_stop(&stream);
|
|
|
|
// stop the server before stream_join() to wake up the stream
|
2018-03-12 17:19:12 +08:00
|
|
|
server_stop(&server);
|
2019-03-02 23:43:43 +08:00
|
|
|
stream_join(&stream);
|
2018-11-09 19:21:17 +08:00
|
|
|
finally_destroy_recorder:
|
2019-03-03 01:18:12 +08:00
|
|
|
if (record) {
|
2018-11-09 19:21:17 +08:00
|
|
|
recorder_destroy(&recorder);
|
|
|
|
}
|
2019-03-03 01:09:55 +08:00
|
|
|
finally_destroy_file_handler:
|
2019-03-03 05:40:51 +08:00
|
|
|
if (display && control) {
|
2019-03-03 01:06:29 +08:00
|
|
|
file_handler_stop(&file_handler);
|
|
|
|
file_handler_join(&file_handler);
|
|
|
|
file_handler_destroy(&file_handler);
|
|
|
|
}
|
2019-03-02 22:16:55 +08:00
|
|
|
finally_destroy_video_buffer:
|
2019-03-03 01:06:29 +08:00
|
|
|
if (display) {
|
|
|
|
video_buffer_destroy(&video_buffer);
|
|
|
|
}
|
2018-02-09 19:59:36 +08:00
|
|
|
finally_destroy_server:
|
2018-03-25 22:20:34 +08:00
|
|
|
if (options->show_touches) {
|
|
|
|
if (!show_touches_waited) {
|
|
|
|
// wait the process which enabled "show touches"
|
|
|
|
wait_show_touches(proc_show_touches);
|
|
|
|
}
|
|
|
|
LOGI("Disable show_touches");
|
2019-03-03 03:09:56 +08:00
|
|
|
proc_show_touches = set_show_touches_enabled(options->serial,
|
|
|
|
SDL_FALSE);
|
2018-03-25 22:20:34 +08:00
|
|
|
wait_show_touches(proc_show_touches);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|