Compare commits
12 Commits
rawkeyeven
...
logicalsca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3a07c3309 | ||
|
|
2e40285244 | ||
|
|
eca99d5af7 | ||
|
|
6a1fb070f7 | ||
|
|
27bed948d4 | ||
|
|
66def38b73 | ||
|
|
a60aef5aaf | ||
|
|
28015c3ee4 | ||
|
|
af9808cf02 | ||
|
|
34550311be | ||
|
|
55d33ddd5f | ||
|
|
6d2d803003 |
@@ -312,6 +312,12 @@ To show physical touches while scrcpy is running:
|
|||||||
scrcpy -t
|
scrcpy -t
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The app may be started directly in fullscreen:
|
||||||
|
|
||||||
|
```
|
||||||
|
scrcpy -f
|
||||||
|
```
|
||||||
|
|
||||||
To run without installing:
|
To run without installing:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -18,9 +18,25 @@ static inline const char *get_adb_command() {
|
|||||||
return adb_command;
|
return adb_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void show_adb_err_msg(enum process_result err) {
|
||||||
|
switch (err) {
|
||||||
|
case PROCESS_ERROR_GENERIC:
|
||||||
|
LOGE("Failed to execute adb");
|
||||||
|
break;
|
||||||
|
case PROCESS_ERROR_MISSING_BINARY:
|
||||||
|
LOGE("'adb' command not found (make it accessible from your PATH "
|
||||||
|
"or define its full path in the ADB environment variable)");
|
||||||
|
break;
|
||||||
|
case PROCESS_SUCCESS:
|
||||||
|
/* do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) {
|
process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) {
|
||||||
const char *cmd[len + 4];
|
const char *cmd[len + 4];
|
||||||
int i;
|
int i;
|
||||||
|
process_t process;
|
||||||
cmd[0] = get_adb_command();
|
cmd[0] = get_adb_command();
|
||||||
if (serial) {
|
if (serial) {
|
||||||
cmd[1] = "-s";
|
cmd[1] = "-s";
|
||||||
@@ -32,7 +48,12 @@ process_t adb_execute(const char *serial, const char *const adb_cmd[], int len)
|
|||||||
|
|
||||||
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
|
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
|
||||||
cmd[len + i] = NULL;
|
cmd[len + i] = NULL;
|
||||||
return cmd_execute(cmd[0], cmd);
|
enum process_result r = cmd_execute(cmd[0], cmd, &process);
|
||||||
|
if (r != PROCESS_SUCCESS) {
|
||||||
|
show_adb_err_msg(r);
|
||||||
|
return PROCESS_NONE;
|
||||||
|
}
|
||||||
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name) {
|
process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name) {
|
||||||
|
|||||||
@@ -32,7 +32,13 @@
|
|||||||
#endif
|
#endif
|
||||||
# define NO_EXIT_CODE -1
|
# define NO_EXIT_CODE -1
|
||||||
|
|
||||||
process_t cmd_execute(const char *path, const char *const argv[]);
|
enum process_result {
|
||||||
|
PROCESS_SUCCESS,
|
||||||
|
PROCESS_ERROR_GENERIC,
|
||||||
|
PROCESS_ERROR_MISSING_BINARY,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum process_result cmd_execute(const char *path, const char *const argv[], process_t *process);
|
||||||
SDL_bool cmd_terminate(process_t pid);
|
SDL_bool cmd_terminate(process_t pid);
|
||||||
SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code);
|
SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef FILE_HANDLER_H
|
#ifndef FILE_HANDLER_H
|
||||||
#define FILE_HADNELR_H
|
#define FILE_HANDLER_H
|
||||||
|
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include <SDL2/SDL_stdinc.h>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
struct args {
|
struct args {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
|
SDL_bool fullscreen;
|
||||||
SDL_bool help;
|
SDL_bool help;
|
||||||
SDL_bool version;
|
SDL_bool version;
|
||||||
SDL_bool show_touches;
|
SDL_bool show_touches;
|
||||||
@@ -36,6 +37,9 @@ static void usage(const char *arg0) {
|
|||||||
" (typically, portrait for a phone, landscape for a tablet).\n"
|
" (typically, portrait for a phone, landscape for a tablet).\n"
|
||||||
" Any --max-size value is computed on the cropped size.\n"
|
" Any --max-size value is computed on the cropped size.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" -f, --fullscreen\n"
|
||||||
|
" Start in fullscreen.\n"
|
||||||
|
"\n"
|
||||||
" -h, --help\n"
|
" -h, --help\n"
|
||||||
" Print this help.\n"
|
" Print this help.\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -200,6 +204,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
{"bit-rate", required_argument, NULL, 'b'},
|
{"bit-rate", required_argument, NULL, 'b'},
|
||||||
{"crop", required_argument, NULL, 'c'},
|
{"crop", required_argument, NULL, 'c'},
|
||||||
|
{"fullscreen", no_argument, NULL, 'f'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"max-size", required_argument, NULL, 'm'},
|
{"max-size", required_argument, NULL, 'm'},
|
||||||
{"port", required_argument, NULL, 'p'},
|
{"port", required_argument, NULL, 'p'},
|
||||||
@@ -209,7 +214,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
{NULL, 0, NULL, 0 },
|
{NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "b:c:hm:p:s:tv", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "b:c:fhm:p:s:tv", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
||||||
@@ -219,6 +224,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
case 'c':
|
case 'c':
|
||||||
args->crop = optarg;
|
args->crop = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
args->fullscreen = SDL_TRUE;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
args->help = SDL_TRUE;
|
args->help = SDL_TRUE;
|
||||||
break;
|
break;
|
||||||
@@ -305,6 +313,7 @@ int main(int argc, char *argv[]) {
|
|||||||
.max_size = args.max_size,
|
.max_size = args.max_size,
|
||||||
.bit_rate = args.bit_rate,
|
.bit_rate = args.bit_rate,
|
||||||
.show_touches = args.show_touches,
|
.show_touches = args.show_touches,
|
||||||
|
.fullscreen = args.fullscreen,
|
||||||
};
|
};
|
||||||
int res = scrcpy(&options) ? 0 : 1;
|
int res = scrcpy(&options) ? 0 : 1;
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ static SDL_bool event_loop(void) {
|
|||||||
switch (event.window.event) {
|
switch (event.window.event) {
|
||||||
case SDL_WINDOWEVENT_EXPOSED:
|
case SDL_WINDOWEVENT_EXPOSED:
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
screen_update_scale(&screen);
|
||||||
screen_render(&screen);
|
screen_render(&screen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -223,6 +224,10 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
|
|||||||
show_touches_waited = SDL_TRUE;
|
show_touches_waited = SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options->fullscreen) {
|
||||||
|
screen_switch_fullscreen(&screen);
|
||||||
|
}
|
||||||
|
|
||||||
ret = event_loop();
|
ret = event_loop();
|
||||||
LOGD("quit...");
|
LOGD("quit...");
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ struct scrcpy_options {
|
|||||||
Uint16 max_size;
|
Uint16 max_size;
|
||||||
Uint32 bit_rate;
|
Uint32 bit_rate;
|
||||||
SDL_bool show_touches;
|
SDL_bool show_touches;
|
||||||
|
SDL_bool fullscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_bool scrcpy(const struct scrcpy_options *options);
|
SDL_bool scrcpy(const struct scrcpy_options *options);
|
||||||
|
|||||||
@@ -132,6 +132,17 @@ static inline struct size get_initial_optimal_size(struct size frame_size) {
|
|||||||
return get_optimal_size(frame_size, frame_size);
|
return get_optimal_size(frame_size, frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply hidpi scaling and call SDL_RenderSetLogicalSize
|
||||||
|
static inline SDL_bool render_set_scaled_logical_size(struct screen *screen, struct size size) {
|
||||||
|
int w, h, dw, dh;
|
||||||
|
SDL_GL_GetDrawableSize(screen->window, &w, &h);
|
||||||
|
SDL_GetWindowSize(screen->window, &dw, &dh);
|
||||||
|
// use 32 bits unsigned not to lose precision (width and height fit in 16 bits)
|
||||||
|
int scaled_x = (Uint32) size.width * (Uint32) w / (Uint32) dw;
|
||||||
|
int scaled_y = (Uint32) size.height * (Uint32) h / (Uint32) dh;
|
||||||
|
return SDL_RenderSetLogicalSize(screen->renderer, scaled_x, scaled_y);
|
||||||
|
}
|
||||||
|
|
||||||
void screen_init(struct screen *screen) {
|
void screen_init(struct screen *screen) {
|
||||||
*screen = (struct screen) SCREEN_INITIALIZER;
|
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||||
}
|
}
|
||||||
@@ -163,7 +174,7 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s
|
|||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width, frame_size.height)) {
|
if (render_set_scaled_logical_size(screen, frame_size)) {
|
||||||
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
screen_destroy(screen);
|
screen_destroy(screen);
|
||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
@@ -208,7 +219,7 @@ void screen_destroy(struct screen *screen) {
|
|||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static SDL_bool prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
static SDL_bool prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width || screen->frame_size.height != new_frame_size.height) {
|
if (screen->frame_size.width != new_frame_size.width || screen->frame_size.height != new_frame_size.height) {
|
||||||
if (SDL_RenderSetLogicalSize(screen->renderer, new_frame_size.width, new_frame_size.height)) {
|
if (render_set_scaled_logical_size(screen, new_frame_size)) {
|
||||||
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
@@ -302,3 +313,7 @@ void screen_resize_to_pixel_perfect(struct screen *screen) {
|
|||||||
LOGD("Resized to pixel-perfect");
|
LOGD("Resized to pixel-perfect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void screen_update_scale(struct screen *screen) {
|
||||||
|
render_set_scaled_logical_size(screen, screen->frame_size);
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,4 +66,8 @@ void screen_resize_to_fit(struct screen *screen);
|
|||||||
// resize window to 1:1 (pixel-perfect)
|
// resize window to 1:1 (pixel-perfect)
|
||||||
void screen_resize_to_pixel_perfect(struct screen *screen);
|
void screen_resize_to_pixel_perfect(struct screen *screen);
|
||||||
|
|
||||||
|
// recompute the scale in case the window moved from/to another screen with a
|
||||||
|
// different HiDPI
|
||||||
|
void screen_update_scale(struct screen *screen);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -7,18 +9,65 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
pid_t cmd_execute(const char *path, const char *const argv[]) {
|
enum process_result cmd_execute(const char *path, const char *const argv[], pid_t *pid) {
|
||||||
pid_t pid = fork();
|
int fd[2];
|
||||||
if (pid == -1) {
|
|
||||||
perror("fork");
|
if (pipe(fd) == -1) {
|
||||||
return -1;
|
perror("pipe");
|
||||||
|
return PROCESS_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
if (pid == 0) {
|
|
||||||
execvp(path, (char *const *)argv);
|
enum process_result ret = PROCESS_SUCCESS;
|
||||||
perror("exec");
|
|
||||||
|
*pid = fork();
|
||||||
|
if (*pid == -1) {
|
||||||
|
perror("fork");
|
||||||
|
ret = PROCESS_ERROR_GENERIC;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pid > 0) {
|
||||||
|
// parent close write side
|
||||||
|
close(fd[1]);
|
||||||
|
fd[1] = -1;
|
||||||
|
// wait for EOF or receive errno from child
|
||||||
|
if (read(fd[0], &ret, sizeof(ret)) == -1) {
|
||||||
|
perror("read");
|
||||||
|
ret = PROCESS_ERROR_GENERIC;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
} else if (*pid == 0) {
|
||||||
|
// child close read side
|
||||||
|
close(fd[0]);
|
||||||
|
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) {
|
||||||
|
execvp(path, (char *const *)argv);
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
ret = PROCESS_ERROR_MISSING_BINARY;
|
||||||
|
} else {
|
||||||
|
ret = PROCESS_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
perror("exec");
|
||||||
|
} else {
|
||||||
|
perror("fcntl");
|
||||||
|
ret = PROCESS_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
// send ret to the parent
|
||||||
|
if (write(fd[1], &ret, sizeof(ret)) == -1) {
|
||||||
|
perror("write");
|
||||||
|
}
|
||||||
|
// close write side before exiting
|
||||||
|
close(fd[1]);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
return pid;
|
|
||||||
|
end:
|
||||||
|
if (fd[0] != -1) {
|
||||||
|
close(fd[0]);
|
||||||
|
}
|
||||||
|
if (fd[1] != -1) {
|
||||||
|
close(fd[1]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_bool cmd_terminate(pid_t pid) {
|
SDL_bool cmd_terminate(pid_t pid) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
HANDLE cmd_execute(const char *path, const char *const argv[]) {
|
enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
|
||||||
STARTUPINFO si;
|
STARTUPINFO si;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
memset(&si, 0, sizeof(si));
|
memset(&si, 0, sizeof(si));
|
||||||
@@ -18,7 +18,8 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) {
|
|||||||
size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd));
|
size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd));
|
||||||
if (ret >= sizeof(cmd)) {
|
if (ret >= sizeof(cmd)) {
|
||||||
LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1);
|
LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1);
|
||||||
return NULL;
|
*handle = NULL;
|
||||||
|
return PROCESS_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WINDOWS_NOCONSOLE
|
#ifdef WINDOWS_NOCONSOLE
|
||||||
@@ -27,10 +28,15 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) {
|
|||||||
int flags = 0;
|
int flags = 0;
|
||||||
#endif
|
#endif
|
||||||
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
|
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
|
||||||
return NULL;
|
*handle = NULL;
|
||||||
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||||
|
return PROCESS_ERROR_MISSING_BINARY;
|
||||||
|
}
|
||||||
|
return PROCESS_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pi.hProcess;
|
*handle = pi.hProcess;
|
||||||
|
return PROCESS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_bool cmd_terminate(HANDLE handle) {
|
SDL_bool cmd_terminate(HANDLE handle) {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import android.net.LocalSocket;
|
|||||||
import android.net.LocalSocketAddress;
|
import android.net.LocalSocketAddress;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public final class DesktopConnection implements Closeable {
|
public final class DesktopConnection implements Closeable {
|
||||||
@@ -18,14 +18,14 @@ public final class DesktopConnection implements Closeable {
|
|||||||
|
|
||||||
private final LocalSocket socket;
|
private final LocalSocket socket;
|
||||||
private final InputStream inputStream;
|
private final InputStream inputStream;
|
||||||
private final OutputStream outputStream;
|
private final FileDescriptor fd;
|
||||||
|
|
||||||
private final ControlEventReader reader = new ControlEventReader();
|
private final ControlEventReader reader = new ControlEventReader();
|
||||||
|
|
||||||
private DesktopConnection(LocalSocket socket) throws IOException {
|
private DesktopConnection(LocalSocket socket) throws IOException {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
inputStream = socket.getInputStream();
|
inputStream = socket.getInputStream();
|
||||||
outputStream = socket.getOutputStream();
|
fd = socket.getFileDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LocalSocket connect(String abstractName) throws IOException {
|
private static LocalSocket connect(String abstractName) throws IOException {
|
||||||
@@ -78,11 +78,11 @@ public final class DesktopConnection implements Closeable {
|
|||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
||||||
outputStream.write(buffer, 0, buffer.length);
|
IO.writeFully(fd, buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public FileDescriptor getFd() {
|
||||||
return outputStream;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlEvent receiveControlEvent() throws IOException {
|
public ControlEvent receiveControlEvent() throws IOException {
|
||||||
|
|||||||
31
server/src/main/java/com/genymobile/scrcpy/IO.java
Normal file
31
server/src/main/java/com/genymobile/scrcpy/IO.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.system.OsConstants;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class IO {
|
||||||
|
private IO() {
|
||||||
|
// not instantiable
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
|
||||||
|
while (from.hasRemaining()) {
|
||||||
|
try {
|
||||||
|
Os.write(fd, from);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
if (e.errno != OsConstants.EINTR) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
|
||||||
|
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@ import android.media.MediaFormat;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
return rotationChanged.getAndSet(false);
|
return rotationChanged.getAndSet(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void streamScreen(Device device, OutputStream outputStream) throws IOException {
|
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
||||||
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
|
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
|
||||||
device.setRotationListener(this);
|
device.setRotationListener(this);
|
||||||
boolean alive;
|
boolean alive;
|
||||||
@@ -64,7 +64,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
setDisplaySurface(display, surface, contentRect, videoRect);
|
setDisplaySurface(display, surface, contentRect, videoRect);
|
||||||
codec.start();
|
codec.start();
|
||||||
try {
|
try {
|
||||||
alive = encode(codec, outputStream);
|
alive = encode(codec, fd);
|
||||||
} finally {
|
} finally {
|
||||||
codec.stop();
|
codec.stop();
|
||||||
destroyDisplay(display);
|
destroyDisplay(display);
|
||||||
@@ -77,9 +77,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException {
|
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
|
||||||
byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video
|
|
||||||
boolean eof = false;
|
boolean eof = false;
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
while (!consumeRotationChange() && !eof) {
|
while (!consumeRotationChange() && !eof) {
|
||||||
@@ -91,15 +89,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (outputBufferId >= 0) {
|
if (outputBufferId >= 0) {
|
||||||
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
|
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
|
||||||
while (outputBuffer.hasRemaining()) {
|
IO.writeFully(fd, codecBuffer);
|
||||||
int remaining = outputBuffer.remaining();
|
|
||||||
int len = Math.min(buf.length, remaining);
|
|
||||||
// the outputBuffer is probably direct (it has no underlying array), and LocalSocket does not expose channels,
|
|
||||||
// so we must copy the data locally to write them manually to the output stream
|
|
||||||
outputBuffer.get(buf, 0, len);
|
|
||||||
outputStream.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (outputBufferId >= 0) {
|
if (outputBufferId >= 0) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public final class Server {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// synchronous
|
// synchronous
|
||||||
screenEncoder.streamScreen(device, connection.getOutputStream());
|
screenEncoder.streamScreen(device, connection.getFd());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// this is expected on close
|
// this is expected on close
|
||||||
Ln.d("Screen streaming stopped");
|
Ln.d("Screen streaming stopped");
|
||||||
|
|||||||
Reference in New Issue
Block a user