scrcpy.c 12.2 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
#include <SDL2/SDL.h>
R
Romain Vimont 已提交
9
#include <SDL2/SDL_net.h>
R
Romain Vimont 已提交
10

R
Romain Vimont 已提交
11 12
#include "command.h"
#include "common.h"
R
Romain Vimont 已提交
13
#include "controller.h"
R
Romain Vimont 已提交
14 15 16 17 18 19
#include "convert.h"
#include "decoder.h"
#include "events.h"
#include "frames.h"
#include "lockutil.h"
#include "netutil.h"
20
#include "screen.h"
R
Romain Vimont 已提交
21
#include "server.h"
R
Romain Vimont 已提交
22
#include "tinyxpm.h"
R
Romain Vimont 已提交
23 24 25

#define DEVICE_NAME_FIELD_LENGTH 64

26
static struct server server = SERVER_INITIALIZER;
27
static struct screen screen = SCREEN_INITIALIZER;
R
Romain Vimont 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
static struct frames frames;
static struct decoder decoder;
static struct controller controller;

static long timestamp_ms(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static void count_frame(void) {
    static long ts = 0;
    static int nbframes = 0;
    long now = timestamp_ms();
    ++nbframes;
    if (now - ts > 1000) {
        SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%d fps", nbframes);
        ts = now;
        nbframes = 0;
    }
}

// name must be at least DEVICE_NAME_FIELD_LENGTH bytes
R
Romain Vimont 已提交
51
static SDL_bool read_initial_device_info(TCPsocket socket, char *device_name, struct size *size) {
R
Romain Vimont 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64
    unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
    if (SDLNet_TCP_Recv(socket, buf, sizeof(buf)) <= 0) {
        return SDL_FALSE;
    }
    buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0'; // in case the client sends garbage
    // scrcpy is safe here, since name contains at least DEVICE_NAME_FIELD_LENGTH bytes
    // and strlen(buf) < DEVICE_NAME_FIELD_LENGTH
    strcpy(device_name, (char *) buf);
    size->width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8) | buf[DEVICE_NAME_FIELD_LENGTH + 1];
    size->height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8) | buf[DEVICE_NAME_FIELD_LENGTH + 3];
    return SDL_TRUE;
}

65
static struct point get_mouse_point(void) {
R
Romain Vimont 已提交
66 67 68 69
    int x;
    int y;
    SDL_GetMouseState(&x, &y);
    SDL_assert_release(x >= 0 && x < 0x10000 && y >= 0 && y < 0x10000);
R
Romain Vimont 已提交
70
    return (struct point) {
R
Romain Vimont 已提交
71 72 73 74 75
        .x = (Uint16) x,
        .y = (Uint16) y,
    };
}

R
Romain Vimont 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
static void send_keycode(enum android_keycode keycode, const char *name) {
    // send DOWN event
    struct control_event control_event = {
        .type = CONTROL_EVENT_TYPE_KEYCODE,
        .keycode_event = {
            .action = AKEY_EVENT_ACTION_DOWN,
            .keycode = keycode,
            .metastate = 0,
        },
    };

    if (!controller_push_event(&controller, &control_event)) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send %s (DOWN)", name);
        return;
    }

    // send UP event
    control_event.keycode_event.action = AKEY_EVENT_ACTION_UP;
    if (!controller_push_event(&controller, &control_event)) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send %s (UP)", name);
    }
}

R
Romain Vimont 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
static inline void action_home(void) {
    send_keycode(AKEYCODE_HOME, "HOME");
}

static inline void action_back(void) {
    send_keycode(AKEYCODE_BACK, "BACK");
}

static inline void action_app_switch(void) {
    send_keycode(AKEYCODE_APP_SWITCH, "APP_SWITCH");
}

static inline void action_power(void) {
    send_keycode(AKEYCODE_POWER, "POWER");
}

static inline void action_volume_up(void) {
    send_keycode(AKEYCODE_VOLUME_UP, "VOLUME_UP");
}

static inline void action_volume_down(void) {
    send_keycode(AKEYCODE_VOLUME_DOWN, "VOLUME_DOWN");
}

R
Romain Vimont 已提交
123 124 125 126 127 128 129 130 131 132 133 134
static void turn_screen_on(void) {
    struct control_event control_event = {
        .type = CONTROL_EVENT_TYPE_COMMAND,
        .command_event = {
            .action = CONTROL_EVENT_COMMAND_SCREEN_ON,
        },
    };
    if (!controller_push_event(&controller, &control_event)) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot turn screen on");
    }
}

R
Romain Vimont 已提交
135 136 137 138
static SDL_bool handle_new_frame(void) {
    mutex_lock(frames.mutex);
    AVFrame *frame = frames.rendering_frame;
    frames.rendering_frame_consumed = SDL_TRUE;
139 140 141 142 143
#ifndef SKIP_FRAMES
    // if SKIP_FRAMES is disabled, then notify the decoder the current frame is
    // consumed, so that it may push a new one
    cond_signal(frames.rendering_frame_consumed_cond);
#endif
R
Romain Vimont 已提交
144

145
    if (!screen_update(&screen, frame)){
R
Romain Vimont 已提交
146
        return SDL_FALSE;
R
Romain Vimont 已提交
147
    }
R
Romain Vimont 已提交
148 149
    mutex_unlock(frames.mutex);

150
    screen_render(&screen);
R
Romain Vimont 已提交
151 152 153
    return SDL_TRUE;
}

R
Romain Vimont 已提交
154 155 156 157 158
static SDL_bool is_ctrl_down(void) {
    const Uint8 *state = SDL_GetKeyboardState(NULL);
    return state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL];
}

R
Romain Vimont 已提交
159
static void handle_text_input(const SDL_TextInputEvent *event) {
R
Romain Vimont 已提交
160
    if (is_ctrl_down()) {
R
Romain Vimont 已提交
161 162 163 164 165 166 167 168
        switch (event->text[0]) {
            case '+':
                action_volume_up();
                break;
            case '-':
                action_volume_down();
                break;
        }
R
Romain Vimont 已提交
169 170 171
        return;
    }

R
Romain Vimont 已提交
172 173 174 175 176 177
    struct control_event control_event;
    control_event.type = CONTROL_EVENT_TYPE_TEXT;
    strncpy(control_event.text_event.text, event->text, TEXT_MAX_LENGTH);
    control_event.text_event.text[TEXT_MAX_LENGTH] = '\0';
    if (!controller_push_event(&controller, &control_event)) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send text event");
R
Romain Vimont 已提交
178
    }
R
Romain Vimont 已提交
179
}
R
Romain Vimont 已提交
180

R
Romain Vimont 已提交
181 182 183 184 185
static void handle_key(const SDL_KeyboardEvent *event) {
    SDL_Keycode keycode = event->keysym.sym;
    SDL_bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
    SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
    SDL_bool repeat = event->repeat;
R
Romain Vimont 已提交
186

R
Romain Vimont 已提交
187 188 189 190 191 192 193
    // capture all Ctrl events
    if (ctrl) {
        // only consider keydown events, and ignore repeated events
        if (repeat || event->type != SDL_KEYDOWN) {
            return;
        }

194 195
        if (shift) {
            // currently, there is no shortcut implying SHIFT
R
Romain Vimont 已提交
196 197 198
            return;
        }

199
        switch (keycode) {
200
            case SDLK_h:
R
Romain Vimont 已提交
201
                action_home();
202
                return;
203 204
            case SDLK_b: // fall-through
            case SDLK_BACKSPACE:
R
Romain Vimont 已提交
205
                action_back();
206 207
                return;
            case SDLK_m:
R
Romain Vimont 已提交
208
                action_app_switch();
209 210
                return;
            case SDLK_p:
R
Romain Vimont 已提交
211 212 213
                action_power();
                return;
            case SDLK_f:
214
                screen_switch_fullscreen(&screen);
R
Romain Vimont 已提交
215 216
                return;
            case SDLK_x:
217
                screen_resize_to_fit(&screen);
R
Romain Vimont 已提交
218 219
                return;
            case SDLK_g:
220
                screen_resize_to_pixel_perfect(&screen);
221
                return;
R
Romain Vimont 已提交
222 223
        }

R
Romain Vimont 已提交
224 225 226 227 228 229 230 231 232 233
        return;
    }

    struct control_event control_event;
    if (input_key_from_sdl_to_android(event, &control_event)) {
        if (!controller_push_event(&controller, &control_event)) {
            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send control event");
        }
    }
}
R
Romain Vimont 已提交
234

R
Romain Vimont 已提交
235
static void handle_mouse_motion(const SDL_MouseMotionEvent *event, struct size screen_size) {
236 237 238 239
    if (!event->state) {
        // do not send motion events when no button is pressed
        return;
    }
R
Romain Vimont 已提交
240 241 242 243 244 245 246 247 248
    struct control_event control_event;
    if (mouse_motion_from_sdl_to_android(event, screen_size, &control_event)) {
        if (!controller_push_event(&controller, &control_event)) {
            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send mouse motion event");
        }
    }
}

static void handle_mouse_button(const SDL_MouseButtonEvent *event, struct size screen_size) {
R
Romain Vimont 已提交
249 250 251 252
    if (event->button == SDL_BUTTON_RIGHT) {
        turn_screen_on();
        return;
    };
R
Romain Vimont 已提交
253 254 255 256 257 258 259 260
    struct control_event control_event;
    if (mouse_button_from_sdl_to_android(event, screen_size, &control_event)) {
        if (!controller_push_event(&controller, &control_event)) {
            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send mouse button event");
        }
    }
}

R
Romain Vimont 已提交
261
static void handle_mouse_wheel(const SDL_MouseWheelEvent *event, struct position position) {
R
Romain Vimont 已提交
262
    struct control_event control_event;
R
Romain Vimont 已提交
263
    if (mouse_wheel_from_sdl_to_android(event, position, &control_event)) {
R
Romain Vimont 已提交
264 265 266 267 268 269
        if (!controller_push_event(&controller, &control_event)) {
            SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send wheel button event");
        }
    }
}

R
Romain Vimont 已提交
270
static void event_loop(void) {
R
Romain Vimont 已提交
271 272 273
    SDL_Event event;
    while (SDL_WaitEvent(&event)) {
        switch (event.type) {
R
Romain Vimont 已提交
274 275
            case EVENT_DECODER_STOPPED:
                SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Video decoder stopped");
R
Romain Vimont 已提交
276
                return;
R
Romain Vimont 已提交
277
            case SDL_QUIT:
R
Romain Vimont 已提交
278
                SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "User requested to quit");
R
Romain Vimont 已提交
279
                return;
R
Romain Vimont 已提交
280 281 282 283 284 285 286 287 288 289
            case EVENT_NEW_FRAME:
                if (!handle_new_frame()) {
                    return;
                }
                count_frame(); // display fps for debug
                break;
            case SDL_WINDOWEVENT:
                switch (event.window.event) {
                case SDL_WINDOWEVENT_EXPOSED:
                case SDL_WINDOWEVENT_SIZE_CHANGED:
290
                    screen_render(&screen);
R
Romain Vimont 已提交
291 292 293 294 295 296
                    break;
                }
                break;
            case SDL_TEXTINPUT: {
                handle_text_input(&event.text);
                break;
R
Romain Vimont 已提交
297
            }
R
Romain Vimont 已提交
298 299 300 301 302
            case SDL_KEYDOWN:
            case SDL_KEYUP:
                handle_key(&event.key);
                break;
            case SDL_MOUSEMOTION:
303
                handle_mouse_motion(&event.motion, screen.frame_size);
R
Romain Vimont 已提交
304 305 306
                break;
            case SDL_MOUSEWHEEL: {
                struct position position = {
307
                    .screen_size = screen.frame_size,
R
Romain Vimont 已提交
308 309 310 311 312 313 314
                    .point = get_mouse_point(),
                };
                handle_mouse_wheel(&event.wheel, position);
                break;
            }
            case SDL_MOUSEBUTTONDOWN:
            case SDL_MOUSEBUTTONUP: {
315
                handle_mouse_button(&event.button, screen.frame_size);
R
Romain Vimont 已提交
316 317 318 319 320 321
                break;
            }
        }
    }
}

R
Romain Vimont 已提交
322
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 bit_rate) {
323
    if (!server_start(&server, serial, local_port, max_size, bit_rate)) {
R
Romain Vimont 已提交
324 325 326 327 328 329
        return SDL_FALSE;
    }

    // to reduce startup time, we could be tempted to init other stuff before blocking here
    // but we should not block after SDL_Init since it handles the signals (Ctrl+C) in its
    // event loop: blocking could lead to deadlock
330
    TCPsocket device_socket = server_connect_to(&server);
R
Romain Vimont 已提交
331
    if (!device_socket) {
332 333
        server_stop(&server, serial);
        return SDL_FALSE;
R
Romain Vimont 已提交
334 335 336
    }

    char device_name[DEVICE_NAME_FIELD_LENGTH];
337
    struct size frame_size;
R
Romain Vimont 已提交
338 339 340 341 342 343

    // 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
    if (!read_initial_device_info(device_socket, device_name, &frame_size)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not retrieve initial screen size");
344 345
        server_stop(&server, serial);
        return SDL_FALSE;
R
Romain Vimont 已提交
346 347 348
    }

    if (!frames_init(&frames)) {
349 350
        server_stop(&server, serial);
        return SDL_FALSE;
R
Romain Vimont 已提交
351 352
    }

353 354
    SDL_bool ret = SDL_TRUE;

R
Romain Vimont 已提交
355 356 357 358 359 360 361
    decoder.frames = &frames;
    decoder.video_socket = device_socket;

    // now we consumed the header values, the socket receives the video stream
    // start the decoder
    if (!decoder_start(&decoder)) {
        ret = SDL_FALSE;
362
        server_stop(&server, serial);
363
        goto finally_destroy_frames;
R
Romain Vimont 已提交
364 365 366 367
    }

    if (!controller_init(&controller, device_socket)) {
        ret = SDL_FALSE;
368
        goto finally_stop_decoder;
R
Romain Vimont 已提交
369 370 371 372
    }

    if (!controller_start(&controller)) {
        ret = SDL_FALSE;
373
        goto finally_destroy_controller;
R
Romain Vimont 已提交
374 375
    }

376
    if (!sdl_init_and_configure()) {
R
Romain Vimont 已提交
377
        ret = SDL_FALSE;
378
        goto finally_stop_and_join_controller;
R
Romain Vimont 已提交
379 380
    }

381
    if (!screen_init_rendering(&screen, device_name, frame_size)) {
R
Romain Vimont 已提交
382
        ret = SDL_FALSE;
383
        goto finally_stop_and_join_controller;
R
Romain Vimont 已提交
384
    }
R
Romain Vimont 已提交
385 386 387 388

    event_loop();

    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "quit...");
389 390
    screen_destroy(&screen);
finally_stop_and_join_controller:
R
Romain Vimont 已提交
391 392
    controller_stop(&controller);
    controller_join(&controller);
393
finally_destroy_controller:
R
Romain Vimont 已提交
394
    controller_destroy(&controller);
395
finally_stop_decoder:
R
Romain Vimont 已提交
396
    // kill the server before decoder_join() to wake up the decoder
397
    server_stop(&server, serial);
R
Romain Vimont 已提交
398
    decoder_join(&decoder);
399
finally_destroy_frames:
R
Romain Vimont 已提交
400
    frames_destroy(&frames);
R
Romain Vimont 已提交
401

R
Romain Vimont 已提交
402
    return ret;
R
Romain Vimont 已提交
403
}