scrcpy.c 10.0 KB
Newer Older
R
Romain Vimont 已提交
1
#include "scrcpy.h"
R
Romain Vimont 已提交
2

R
Romain Vimont 已提交
3 4
#include <stdio.h>
#include <string.h>
R
Romain Vimont 已提交
5 6
#include <unistd.h>
#include <libavformat/avformat.h>
R
Romain Vimont 已提交
7
#include <sys/time.h>
R
Romain Vimont 已提交
8 9
#include <SDL2/SDL.h>

R
Romain Vimont 已提交
10 11
#include "command.h"
#include "common.h"
R
Romain Vimont 已提交
12
#include "controller.h"
R
Romain Vimont 已提交
13
#include "decoder.h"
14
#include "device.h"
R
Romain Vimont 已提交
15
#include "events.h"
N
npes87184 已提交
16
#include "file_handler.h"
17 18
#include "fps_counter.h"
#include "input_manager.h"
R
Romain Vimont 已提交
19
#include "log.h"
20
#include "lock_util.h"
21
#include "net.h"
R
Romain Vimont 已提交
22
#include "recorder.h"
23
#include "screen.h"
R
Romain Vimont 已提交
24
#include "server.h"
R
Romain Vimont 已提交
25
#include "stream.h"
26
#include "tiny_xpm.h"
27
#include "video_buffer.h"
R
Romain Vimont 已提交
28

29
static struct server server = SERVER_INITIALIZER;
30
static struct screen screen = SCREEN_INITIALIZER;
31
static struct video_buffer video_buffer;
R
Romain Vimont 已提交
32
static struct stream stream;
R
Romain Vimont 已提交
33
static struct decoder decoder;
R
Romain Vimont 已提交
34
static struct recorder recorder;
R
Romain Vimont 已提交
35
static struct controller controller;
N
npes87184 已提交
36
static struct file_handler file_handler;
R
Romain Vimont 已提交
37

38 39
static struct input_manager input_manager = {
    .controller = &controller,
40
    .video_buffer = &video_buffer,
41 42 43
    .screen = &screen,
};

44 45 46 47 48 49 50 51 52 53
#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>
R
Romain Vimont 已提交
54
static int event_watcher(void *data, SDL_Event *event) {
55 56 57 58 59 60 61 62
    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
        // called from another thread, not very safe, but it's a workaround!
        screen_render(&screen);
    }
    return 0;
}
#endif

63 64 65 66 67
static SDL_bool is_apk(const char *file) {
    const char *ext = strrchr(file, '.');
    return ext && !strcmp(ext, ".apk");
}

G
Grief 已提交
68
static SDL_bool event_loop(void) {
69
#ifdef CONTINUOUS_RESIZING_WORKAROUND
R
Romain Vimont 已提交
70
    SDL_AddEventWatch(event_watcher, NULL);
71
#endif
R
Romain Vimont 已提交
72 73 74
    SDL_Event event;
    while (SDL_WaitEvent(&event)) {
        switch (event.type) {
R
Romain Vimont 已提交
75 76
            case EVENT_STREAM_STOPPED:
                LOGD("Video stream stopped");
G
Grief 已提交
77
                return SDL_FALSE;
R
Romain Vimont 已提交
78
            case SDL_QUIT:
R
Romain Vimont 已提交
79
                LOGD("User requested to quit");
G
Grief 已提交
80
                return SDL_TRUE;
R
Romain Vimont 已提交
81
            case EVENT_NEW_FRAME:
R
Romain Vimont 已提交
82 83 84 85 86
                if (!screen.has_frame) {
                    screen.has_frame = SDL_TRUE;
                    // this is the very first frame, show the window
                    screen_show_window(&screen);
                }
87
                if (!screen_update_frame(&screen, &video_buffer)) {
G
Grief 已提交
88
                    return SDL_FALSE;
R
Romain Vimont 已提交
89 90 91 92
                }
                break;
            case SDL_WINDOWEVENT:
                switch (event.window.event) {
R
Romain Vimont 已提交
93 94 95 96
                    case SDL_WINDOWEVENT_EXPOSED:
                    case SDL_WINDOWEVENT_SIZE_CHANGED:
                        screen_render(&screen);
                        break;
R
Romain Vimont 已提交
97 98
                }
                break;
R
Romain Vimont 已提交
99
            case SDL_TEXTINPUT:
100
                input_manager_process_text_input(&input_manager, &event.text);
R
Romain Vimont 已提交
101 102 103
                break;
            case SDL_KEYDOWN:
            case SDL_KEYUP:
104
                input_manager_process_key(&input_manager, &event.key);
R
Romain Vimont 已提交
105 106
                break;
            case SDL_MOUSEMOTION:
107
                input_manager_process_mouse_motion(&input_manager, &event.motion);
R
Romain Vimont 已提交
108
                break;
R
Romain Vimont 已提交
109
            case SDL_MOUSEWHEEL:
110
                input_manager_process_mouse_wheel(&input_manager, &event.wheel);
R
Romain Vimont 已提交
111 112
                break;
            case SDL_MOUSEBUTTONDOWN:
R
Romain Vimont 已提交
113
            case SDL_MOUSEBUTTONUP:
114
                input_manager_process_mouse_button(&input_manager, &event.button);
R
Romain Vimont 已提交
115
                break;
116 117 118 119 120 121 122 123
            case SDL_DROPFILE: {
                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);
124
                break;
125
            }
R
Romain Vimont 已提交
126 127
        }
    }
G
Grief 已提交
128
    return SDL_FALSE;
R
Romain Vimont 已提交
129 130
}

131
static process_t set_show_touches_enabled(const char *serial, SDL_bool enabled) {
132 133 134 135
    const char *value = enabled ? "1" : "0";
    const char *const adb_cmd[] = {
        "shell", "settings", "put", "system", "show_touches", value
    };
136 137 138 139 140 141
    return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
}

static void wait_show_touches(process_t process) {
    // reap the process, ignore the result
    process_check_success(process, "show_touches");
142 143
}

R
Romain Vimont 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static SDL_LogPriority sdl_priority_from_av_level(int level) {
    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);
}

178
SDL_bool scrcpy(const struct scrcpy_options *options) {
179
    SDL_bool record = !!options->record_filename;
180
    if (!server_start(&server, options->serial, options->port,
181
                      options->max_size, options->bit_rate, options->crop,
182
                      record)) {
R
Romain Vimont 已提交
183 184 185
        return SDL_FALSE;
    }

R
Romain Vimont 已提交
186
    process_t proc_show_touches = PROCESS_NONE;
187 188 189 190 191 192 193
    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;
    }

194 195
    SDL_bool ret = SDL_TRUE;

R
Romain Vimont 已提交
196 197 198 199 200
    if (!sdl_init_and_configure()) {
        ret = SDL_FALSE;
        goto finally_destroy_server;
    }

201
    socket_t device_socket = server_connect_to(&server);
202
    if (device_socket == INVALID_SOCKET) {
R
Romain Vimont 已提交
203
        server_stop(&server);
204 205
        ret = SDL_FALSE;
        goto finally_destroy_server;
R
Romain Vimont 已提交
206 207 208
    }

    char device_name[DEVICE_NAME_FIELD_LENGTH];
209
    struct size frame_size;
R
Romain Vimont 已提交
210 211 212 213

    // 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
214
    if (!device_read_info(device_socket, device_name, &frame_size)) {
R
Romain Vimont 已提交
215
        server_stop(&server);
216 217
        ret = SDL_FALSE;
        goto finally_destroy_server;
R
Romain Vimont 已提交
218 219
    }

R
Romain Vimont 已提交
220
    SDL_bool display = !options->no_window;
R
Romain Vimont 已提交
221

R
Romain Vimont 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234
    struct decoder *dec = NULL;
    if (display) {
        if (!video_buffer_init(&video_buffer)) {
            server_stop(&server);
            ret = SDL_FALSE;
            goto finally_destroy_server;
        }

        if (!file_handler_init(&file_handler, server.serial)) {
            ret = SDL_FALSE;
            server_stop(&server);
            goto finally_destroy_video_buffer;
        }
235

R
Romain Vimont 已提交
236 237 238
        decoder_init(&decoder, &video_buffer);
        dec = &decoder;
    }
R
Romain Vimont 已提交
239

R
Romain Vimont 已提交
240
    struct recorder *rec = NULL;
241
    if (record) {
R
Romain Vimont 已提交
242 243 244 245
        if (!recorder_init(&recorder,
                           options->record_filename,
                           options->record_format,
                           frame_size)) {
R
Romain Vimont 已提交
246 247 248 249 250 251 252
            ret = SDL_FALSE;
            server_stop(&server);
            goto finally_destroy_file_handler;
        }
        rec = &recorder;
    }

R
Romain Vimont 已提交
253 254
    av_log_set_callback(av_log_callback);

R
Romain Vimont 已提交
255
    stream_init(&stream, device_socket, dec, rec);
R
Romain Vimont 已提交
256 257

    // now we consumed the header values, the socket receives the video stream
R
Romain Vimont 已提交
258 259
    // start the stream
    if (!stream_start(&stream)) {
R
Romain Vimont 已提交
260
        ret = SDL_FALSE;
R
Romain Vimont 已提交
261
        server_stop(&server);
R
Romain Vimont 已提交
262
        goto finally_destroy_recorder;
R
Romain Vimont 已提交
263 264
    }

R
Romain Vimont 已提交
265 266 267 268 269
    if (display) {
        if (!controller_init(&controller, device_socket)) {
            ret = SDL_FALSE;
            goto finally_stop_stream;
        }
R
Romain Vimont 已提交
270

R
Romain Vimont 已提交
271 272 273 274
        if (!controller_start(&controller)) {
            ret = SDL_FALSE;
            goto finally_destroy_controller;
        }
R
Romain Vimont 已提交
275

R
Romain Vimont 已提交
276 277 278 279 280 281 282 283
        if (!screen_init_rendering(&screen, device_name, frame_size, options->always_on_top)) {
            ret = SDL_FALSE;
            goto finally_stop_and_join_controller;
        }

        if (options->fullscreen) {
            screen_switch_fullscreen(&screen);
        }
R
Romain Vimont 已提交
284
    }
R
Romain Vimont 已提交
285

286
    if (options->show_touches) {
287 288
        wait_show_touches(proc_show_touches);
        show_touches_waited = SDL_TRUE;
289
    }
R
Romain Vimont 已提交
290

G
Grief 已提交
291
    ret = event_loop();
R
Romain Vimont 已提交
292
    LOGD("quit...");
293

294 295
    screen_destroy(&screen);

296
finally_stop_and_join_controller:
R
Romain Vimont 已提交
297 298 299 300
    if (display) {
        controller_stop(&controller);
        controller_join(&controller);
    }
301
finally_destroy_controller:
R
Romain Vimont 已提交
302 303 304
    if (display) {
        controller_destroy(&controller);
    }
R
Romain Vimont 已提交
305 306 307
finally_stop_stream:
    stream_stop(&stream);
    // stop the server before stream_join() to wake up the stream
R
Romain Vimont 已提交
308
    server_stop(&server);
R
Romain Vimont 已提交
309
    stream_join(&stream);
R
Romain Vimont 已提交
310
finally_destroy_recorder:
311
    if (record) {
R
Romain Vimont 已提交
312 313
        recorder_destroy(&recorder);
    }
R
Romain Vimont 已提交
314
finally_destroy_file_handler:
R
Romain Vimont 已提交
315 316 317 318 319
    if (display) {
        file_handler_stop(&file_handler);
        file_handler_join(&file_handler);
        file_handler_destroy(&file_handler);
    }
320
finally_destroy_video_buffer:
R
Romain Vimont 已提交
321 322 323
    if (display) {
        video_buffer_destroy(&video_buffer);
    }
324
finally_destroy_server:
325 326 327 328 329 330 331 332 333 334
    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");
        proc_show_touches = set_show_touches_enabled(options->serial, SDL_FALSE);
        wait_show_touches(proc_show_touches);
    }

335
    server_destroy(&server);
R
Romain Vimont 已提交
336

R
Romain Vimont 已提交
337
    return ret;
R
Romain Vimont 已提交
338
}