Compare commits

..

12 Commits

Author SHA1 Message Date
Romain Vimont
e3a07c3309 update-on-event 2018-09-13 22:24:26 +02:00
Romain Vimont
2e40285244 hidpi-scale-wip 2018-09-13 20:37:45 +02:00
Romain Vimont
eca99d5af7 Fix header guard name 2018-09-13 16:27:19 +02:00
Romain Vimont
6a1fb070f7 Merge branch 'npes87184:dev' into dev (#254)
Return specific error for missing adb binary on Windows
2018-09-09 15:28:56 +02:00
yuchenlin
27bed948d4 Use specific error for missing binary on Windows
Signed-off-by: yuchenlin <npes87184@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2018-09-09 15:28:25 +02:00
Romain Vimont
66def38b73 Avoid additional buffer copy in userspace
Directly send the data from MediaCodec buffers to the LocalSocket,
without an intermediate copy in userspace.
2018-09-09 15:05:06 +02:00
Romain Vimont
a60aef5aaf Merge branch 'philippsandhaus:master' into dev (#252)
Added new command line parameter to start in fullscreen
2018-09-04 18:51:56 +02:00
Romain Vimont
28015c3ee4 Present fullscreen option in README 2018-09-04 18:51:35 +02:00
Philipp Sandhaus
af9808cf02 Add option to start in fullscreen
Signed-off-by: Romain Vimont <rom@rom1v.com>
2018-09-04 18:51:32 +02:00
Romain Vimont
34550311be Merge branch 'npes87184:dev' into dev (#236)
check adb runnable before starting scrcpy
2018-09-04 18:43:17 +02:00
Romain Vimont
55d33ddd5f Do not handle system-specific values in command.c
The common command.c handled process errors from system-specific int
values (errno).

Rather, expose a new enum process_result to handle error cause in a
generic way.
2018-09-04 08:57:07 +02:00
yuchenlin
6d2d803003 Notify adb missing
There are many user who encounters missing adb.
To stop things happens again, we check it and show
sexy response to user.

Signed-off-by: yuchenlin <npes87184@gmail.com>
2018-09-04 08:46:17 +02:00
19 changed files with 189 additions and 145 deletions

View File

@@ -312,6 +312,12 @@ To show physical touches while scrcpy is running:
scrcpy -t
```
The app may be started directly in fullscreen:
```
scrcpy -f
```
To run without installing:
```bash
@@ -338,15 +344,12 @@ To run without installing:
| turn screen on | _Right-click²_ |
| paste computer clipboard to device | `Ctrl`+`v` |
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
| toggle "raw key events" mode ([#87]) | `Ctrl`+`k` |
| install APK from computer | drag & drop APK file |
| push file to `/sdcard/` | drag & drop non-APK file |
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
[#87]: https://github.com/Genymobile/scrcpy/issues/87
## Why _scrcpy_?

View File

@@ -18,9 +18,25 @@ static inline const char *get_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) {
const char *cmd[len + 4];
int i;
process_t process;
cmd[0] = get_adb_command();
if (serial) {
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 *));
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) {

View File

@@ -32,7 +32,13 @@
#endif
# 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_simple_wait(process_t pid, exit_code_t *exit_code);

View File

@@ -70,83 +70,6 @@ static enum android_metastate convert_meta_state(SDL_Keymod mod) {
return autocomplete_metastate(metastate);
}
// only used if raw_key_events is enabled
static SDL_bool convert_text_keycode(SDL_Keycode from, enum android_keycode *to) {
switch (from) {
MAP(SDLK_SPACE, AKEYCODE_SPACE);
MAP(SDLK_HASH, AKEYCODE_POUND);
MAP(SDLK_QUOTE, AKEYCODE_APOSTROPHE);
MAP(SDLK_ASTERISK, AKEYCODE_STAR);
MAP(SDLK_COMMA, AKEYCODE_COMMA);
MAP(SDLK_MINUS, AKEYCODE_MINUS);
MAP(SDLK_PERIOD, AKEYCODE_PERIOD);
MAP(SDLK_SLASH, AKEYCODE_SLASH);
MAP(SDLK_0, AKEYCODE_0);
MAP(SDLK_1, AKEYCODE_1);
MAP(SDLK_2, AKEYCODE_2);
MAP(SDLK_3, AKEYCODE_3);
MAP(SDLK_4, AKEYCODE_4);
MAP(SDLK_5, AKEYCODE_5);
MAP(SDLK_6, AKEYCODE_6);
MAP(SDLK_7, AKEYCODE_7);
MAP(SDLK_8, AKEYCODE_8);
MAP(SDLK_9, AKEYCODE_9);
MAP(SDLK_SEMICOLON, AKEYCODE_SEMICOLON);
MAP(SDLK_EQUALS, AKEYCODE_EQUALS);
MAP(SDLK_AT, AKEYCODE_AT);
MAP(SDLK_LEFTBRACKET, AKEYCODE_LEFT_BRACKET);
MAP(SDLK_BACKSLASH, AKEYCODE_BACKSLASH);
MAP(SDLK_RIGHTBRACKET, AKEYCODE_RIGHT_BRACKET);
MAP(SDLK_BACKQUOTE, AKEYCODE_GRAVE);
MAP(SDLK_a, AKEYCODE_A);
MAP(SDLK_b, AKEYCODE_B);
MAP(SDLK_c, AKEYCODE_C);
MAP(SDLK_d, AKEYCODE_D);
MAP(SDLK_e, AKEYCODE_E);
MAP(SDLK_f, AKEYCODE_F);
MAP(SDLK_g, AKEYCODE_G);
MAP(SDLK_h, AKEYCODE_H);
MAP(SDLK_i, AKEYCODE_I);
MAP(SDLK_j, AKEYCODE_J);
MAP(SDLK_k, AKEYCODE_K);
MAP(SDLK_l, AKEYCODE_L);
MAP(SDLK_m, AKEYCODE_M);
MAP(SDLK_n, AKEYCODE_N);
MAP(SDLK_o, AKEYCODE_O);
MAP(SDLK_p, AKEYCODE_P);
MAP(SDLK_q, AKEYCODE_Q);
MAP(SDLK_r, AKEYCODE_R);
MAP(SDLK_s, AKEYCODE_S);
MAP(SDLK_t, AKEYCODE_T);
MAP(SDLK_u, AKEYCODE_U);
MAP(SDLK_v, AKEYCODE_V);
MAP(SDLK_w, AKEYCODE_W);
MAP(SDLK_x, AKEYCODE_X);
MAP(SDLK_y, AKEYCODE_Y);
MAP(SDLK_z, AKEYCODE_Z);
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
MAP(SDLK_KP_1, AKEYCODE_NUMPAD_1);
MAP(SDLK_KP_2, AKEYCODE_NUMPAD_2);
MAP(SDLK_KP_3, AKEYCODE_NUMPAD_3);
MAP(SDLK_KP_4, AKEYCODE_NUMPAD_4);
MAP(SDLK_KP_5, AKEYCODE_NUMPAD_5);
MAP(SDLK_KP_6, AKEYCODE_NUMPAD_6);
MAP(SDLK_KP_7, AKEYCODE_NUMPAD_7);
MAP(SDLK_KP_8, AKEYCODE_NUMPAD_8);
MAP(SDLK_KP_9, AKEYCODE_NUMPAD_9);
MAP(SDLK_KP_0, AKEYCODE_NUMPAD_0);
MAP(SDLK_KP_DIVIDE, AKEYCODE_NUMPAD_DIVIDE);
MAP(SDLK_KP_MULTIPLY, AKEYCODE_NUMPAD_MULTIPLY);
MAP(SDLK_KP_MINUS, AKEYCODE_NUMPAD_SUBTRACT);
MAP(SDLK_KP_PLUS, AKEYCODE_NUMPAD_ADD);
MAP(SDLK_KP_PERIOD, AKEYCODE_NUMPAD_DOT);
MAP(SDLK_KP_EQUALS, AKEYCODE_NUMPAD_EQUALS);
MAP(SDLK_KP_LEFTPAREN, AKEYCODE_NUMPAD_LEFT_PAREN);
MAP(SDLK_KP_RIGHTPAREN, AKEYCODE_NUMPAD_RIGHT_PAREN);
FAIL;
}
}
static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to) {
switch (from) {
MAP(SDLK_RETURN, AKEYCODE_ENTER);
@@ -196,8 +119,7 @@ static enum android_motionevent_buttons convert_mouse_buttons(Uint32 state) {
}
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to,
SDL_bool raw_key_events) {
struct control_event *to) {
to->type = CONTROL_EVENT_TYPE_KEYCODE;
if (!convert_keycode_action(from->type, &to->keycode_event.action)) {
@@ -205,10 +127,7 @@ SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
}
if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode)) {
if (!raw_key_events ||
!convert_text_keycode(from->keysym.sym, &to->keycode_event.keycode)) {
return SDL_FALSE;
}
return SDL_FALSE;
}
to->keycode_event.metastate = convert_meta_state(from->keysym.mod);

View File

@@ -16,9 +16,7 @@ struct complete_mouse_wheel_event {
};
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to,
SDL_bool raw_key_events);
struct control_event *to);
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
struct size screen_size,
struct control_event *to);

View File

@@ -1,5 +1,5 @@
#ifndef FILE_HANDLER_H
#define FILE_HADNELR_H
#define FILE_HANDLER_H
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_stdinc.h>

View File

@@ -129,10 +129,6 @@ static void clipboard_paste(struct controller *controller) {
void input_manager_process_text_input(struct input_manager *input_manager,
const SDL_TextInputEvent *event) {
if (input_manager->raw_key_events) {
// we will forward the raw key events instead
return;
}
struct control_event control_event;
control_event.type = CONTROL_EVENT_TYPE_TEXT;
control_event.text_event.text = SDL_strdup(event->text);
@@ -220,21 +216,13 @@ void input_manager_process_key(struct input_manager *input_manager,
switch_fps_counter_state(input_manager->frames);
}
return;
case SDLK_k:
if (!repeat && event->type == SDL_KEYDOWN) {
input_manager->raw_key_events ^= SDL_TRUE; // toggle
LOGI("Raw key events mode %s",
input_manager->raw_key_events ? "enabled" : "disabled");
}
return;
}
return;
}
struct control_event control_event;
SDL_bool raw_key_events = input_manager->raw_key_events;
if (input_key_from_sdl_to_android(event, &control_event, raw_key_events)) {
if (input_key_from_sdl_to_android(event, &control_event)) {
if (!controller_push_event(input_manager->controller, &control_event)) {
LOGW("Cannot send control event");
}

View File

@@ -11,7 +11,6 @@ struct input_manager {
struct controller *controller;
struct frames *frames;
struct screen *screen;
SDL_bool raw_key_events;
};
void input_manager_process_text_input(struct input_manager *input_manager,

View File

@@ -11,6 +11,7 @@
struct args {
const char *serial;
const char *crop;
SDL_bool fullscreen;
SDL_bool help;
SDL_bool version;
SDL_bool show_touches;
@@ -36,6 +37,9 @@ static void usage(const char *arg0) {
" (typically, portrait for a phone, landscape for a tablet).\n"
" Any --max-size value is computed on the cropped size.\n"
"\n"
" -f, --fullscreen\n"
" Start in fullscreen.\n"
"\n"
" -h, --help\n"
" Print this help.\n"
"\n"
@@ -200,6 +204,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
static const struct option long_options[] = {
{"bit-rate", required_argument, NULL, 'b'},
{"crop", required_argument, NULL, 'c'},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"max-size", required_argument, NULL, 'm'},
{"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 },
};
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) {
case 'b':
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':
args->crop = optarg;
break;
case 'f':
args->fullscreen = SDL_TRUE;
break;
case 'h':
args->help = SDL_TRUE;
break;
@@ -305,6 +313,7 @@ int main(int argc, char *argv[]) {
.max_size = args.max_size,
.bit_rate = args.bit_rate,
.show_touches = args.show_touches,
.fullscreen = args.fullscreen,
};
int res = scrcpy(&options) ? 0 : 1;

View File

@@ -35,7 +35,6 @@ static struct input_manager input_manager = {
.controller = &controller,
.frames = &frames,
.screen = &screen,
.raw_key_events = SDL_FALSE,
};
#if defined(__APPLE__) || defined(__WINDOWS__)
@@ -89,6 +88,7 @@ static SDL_bool event_loop(void) {
switch (event.window.event) {
case SDL_WINDOWEVENT_EXPOSED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
screen_update_scale(&screen);
screen_render(&screen);
break;
}
@@ -224,6 +224,10 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
show_touches_waited = SDL_TRUE;
}
if (options->fullscreen) {
screen_switch_fullscreen(&screen);
}
ret = event_loop();
LOGD("quit...");

View File

@@ -10,6 +10,7 @@ struct scrcpy_options {
Uint16 max_size;
Uint32 bit_rate;
SDL_bool show_touches;
SDL_bool fullscreen;
};
SDL_bool scrcpy(const struct scrcpy_options *options);

View File

@@ -132,6 +132,17 @@ static inline struct size get_initial_optimal_size(struct 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) {
*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;
}
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());
screen_destroy(screen);
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
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 (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());
return SDL_FALSE;
}
@@ -302,3 +313,7 @@ void screen_resize_to_pixel_perfect(struct screen *screen) {
LOGD("Resized to pixel-perfect");
}
}
void screen_update_scale(struct screen *screen) {
render_set_scaled_logical_size(screen, screen->frame_size);
}

View File

@@ -66,4 +66,8 @@ void screen_resize_to_fit(struct screen *screen);
// resize window to 1:1 (pixel-perfect)
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

View File

@@ -1,5 +1,7 @@
#include "command.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -7,18 +9,65 @@
#include <unistd.h>
#include "log.h"
pid_t cmd_execute(const char *path, const char *const argv[]) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
enum process_result cmd_execute(const char *path, const char *const argv[], pid_t *pid) {
int fd[2];
if (pipe(fd) == -1) {
perror("pipe");
return PROCESS_ERROR_GENERIC;
}
if (pid == 0) {
execvp(path, (char *const *)argv);
perror("exec");
enum process_result ret = PROCESS_SUCCESS;
*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);
}
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) {

View File

@@ -4,7 +4,7 @@
#include "log.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;
PROCESS_INFORMATION pi;
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));
if (ret >= sizeof(cmd)) {
LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1);
return NULL;
*handle = NULL;
return PROCESS_ERROR_GENERIC;
}
#ifdef WINDOWS_NOCONSOLE
@@ -27,10 +28,15 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) {
int flags = 0;
#endif
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) {

View File

@@ -5,9 +5,9 @@ import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public final class DesktopConnection implements Closeable {
@@ -18,14 +18,14 @@ public final class DesktopConnection implements Closeable {
private final LocalSocket socket;
private final InputStream inputStream;
private final OutputStream outputStream;
private final FileDescriptor fd;
private final ControlEventReader reader = new ControlEventReader();
private DesktopConnection(LocalSocket socket) throws IOException {
this.socket = socket;
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
fd = socket.getFileDescriptor();
}
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 + 2] = (byte) (height >> 8);
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
outputStream.write(buffer, 0, buffer.length);
IO.writeFully(fd, buffer, 0, buffer.length);
}
public OutputStream getOutputStream() {
return outputStream;
public FileDescriptor getFd() {
return fd;
}
public ControlEvent receiveControlEvent() throws IOException {

View 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));
}
}

View File

@@ -9,8 +9,8 @@ import android.media.MediaFormat;
import android.os.IBinder;
import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -48,7 +48,7 @@ public class ScreenEncoder implements Device.RotationListener {
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);
device.setRotationListener(this);
boolean alive;
@@ -64,7 +64,7 @@ public class ScreenEncoder implements Device.RotationListener {
setDisplaySurface(display, surface, contentRect, videoRect);
codec.start();
try {
alive = encode(codec, outputStream);
alive = encode(codec, fd);
} finally {
codec.stop();
destroyDisplay(display);
@@ -77,9 +77,7 @@ public class ScreenEncoder implements Device.RotationListener {
}
}
private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException {
@SuppressWarnings("checkstyle:MagicNumber")
byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
boolean eof = false;
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
while (!consumeRotationChange() && !eof) {
@@ -91,15 +89,8 @@ public class ScreenEncoder implements Device.RotationListener {
break;
}
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
while (outputBuffer.hasRemaining()) {
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);
}
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
IO.writeFully(fd, codecBuffer);
}
} finally {
if (outputBufferId >= 0) {

View File

@@ -21,7 +21,7 @@ public final class Server {
try {
// synchronous
screenEncoder.streamScreen(device, connection.getOutputStream());
screenEncoder.streamScreen(device, connection.getFd());
} catch (IOException e) {
// this is expected on close
Ln.d("Screen streaming stopped");