Skip to content

Commit

Permalink
Update backend to scrcpy v1.15.1-ws
Browse files Browse the repository at this point in the history
  • Loading branch information
drauggres committed Aug 7, 2020
1 parent 3ca4636 commit 2ede9e6
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 32 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ npm start

## Known issues

* The server on the Android Emulator listens on internal interface and not available from the outside (as workaround you can do `adb forward tcp:8886 tcp:8886` and use `127.0.0.1` instead of emulator IP address)
* The server on the Android Emulator listens on the internal interface and not available from the outside (as workaround you can do `adb forward tcp:8886 tcp:8886` and use `127.0.0.1` instead of emulator IP address)
* H264bsdDecoder may fail to start
* Version `0.2.0` is incompatible with previous. Reboot device or manually kill `app_process`.
* Version `0.3.0` is incompatible with previous. Reboot device or manually kill `app_process`.

## Related projects
* [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy)
Expand All @@ -46,6 +46,6 @@ npm start

## scrcpy websocket fork

Currently support of WebSocket protocol added to v1.13 of scrcpy
* [Prebuilt package](https://github.com/NetrisTV/scrcpy/releases/download/v1.13-ws/scrcpy-server.jar)
* [Source code](https://github.com/NetrisTV/scrcpy/tree/feature/websocket-v1.13)
Currently, support of WebSocket protocol added to v1.15.1 of scrcpy
* [Prebuilt package](/src/public/scrcpy-server.jar)
* [Source code](https://github.com/NetrisTV/scrcpy/tree/feature/websocket-v1.15.x)
4 changes: 2 additions & 2 deletions src/DeviceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ export class DeviceController implements DeviceMessageListener {
btn.title = title;
btn.appendChild(SvgImage.create(icon));
btn.onmousedown = () => {
const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0);
const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0, 0);
connection.sendEvent(event);
};
btn.onmouseup = () => {
const event = new KeyCodeControlEvent(KeyEvent.ACTION_UP, code, 0);
const event = new KeyCodeControlEvent(KeyEvent.ACTION_UP, code, 0, 0);
connection.sendEvent(event);
};
this.controlButtons.appendChild(btn);
Expand Down
4 changes: 2 additions & 2 deletions src/DeviceMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default class DeviceMessage {
throw Error('Empty buffer');
}
let offset = 1;
const length = this.buffer.readUInt16BE(offset);
offset += 2;
const length = this.buffer.readInt32BE(offset);
offset += 4;
const textBytes = this.buffer.slice(offset, offset + length);
return Util.utf8ByteArrayToString(textBytes);
}
Expand Down
25 changes: 22 additions & 3 deletions src/KeyInputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,34 @@ export interface KeyEventListener {
}

export class KeyInputHandler {
private static readonly repeatCounter: Map<number, number> = new Map();
private static readonly listeners: Set<KeyEventListener> = new Set();
private static handler = (e: Event): void => {
const event = e as KeyboardEvent;
const keyCode = KeyToCodeMap.get(event.code);
if (!keyCode) {
return;
}
const action =
event.type === 'keydown' ? KeyEvent.ACTION_DOWN : event.type === 'keyup' ? KeyEvent.ACTION_UP : -1;
let action: typeof KeyEvent.ACTION_DOWN | typeof KeyEvent.ACTION_DOWN;
let repeatCount: number = 0;
if (event.type === 'keydown') {
action = KeyEvent.ACTION_DOWN;
if (event.repeat) {
let count = KeyInputHandler.repeatCounter.get(keyCode);
if (typeof count !== 'number') {
count = 1;
} else {
count ++;
}
repeatCount = count;
KeyInputHandler.repeatCounter.set(keyCode, count);
}
} else if (event.type === 'keyup') {
action = KeyEvent.ACTION_UP;
KeyInputHandler.repeatCounter.delete(keyCode);
} else {
return;
}
const metaState =
(event.getModifierState('Alt') ? KeyEvent.META_ALT_ON : 0) |
(event.getModifierState('Shift') ? KeyEvent.META_SHIFT_ON : 0) |
Expand All @@ -25,7 +44,7 @@ export class KeyInputHandler {
(event.getModifierState('ScrollLock') ? KeyEvent.META_SCROLL_LOCK_ON : 0) |
(event.getModifierState('NumLock') ? KeyEvent.META_NUM_LOCK_ON : 0);

const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, metaState);
const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, repeatCount, metaState);
KeyInputHandler.listeners.forEach((listener) => {
listener.onKeyEvent(controlEvent);
});
Expand Down
9 changes: 9 additions & 0 deletions src/VideoSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Settings {
iFrameInterval: number;
sendFrameMeta: boolean;
lockedVideoOrientation: number;
codecOptions?: string;
}

export default class VideoSettings {
Expand All @@ -19,6 +20,7 @@ export default class VideoSettings {
public readonly iFrameInterval: number = 0;
public readonly sendFrameMeta: boolean = false;
public readonly lockedVideoOrientation: number = -1;
public readonly codecOptions: string = '-';

constructor(data?: Settings) {
if (data) {
Expand Down Expand Up @@ -47,6 +49,7 @@ export default class VideoSettings {
if (left || top || right || bottom) {
crop = new Rect(left, top, right, bottom);
}
const codecOptions = '-';
return new VideoSettings({
crop,
bitrate,
Expand All @@ -55,6 +58,7 @@ export default class VideoSettings {
iFrameInterval,
lockedVideoOrientation,
sendFrameMeta,
codecOptions
});
}

Expand All @@ -70,6 +74,7 @@ export default class VideoSettings {
iFrameInterval: a.iFrameInterval,
lockedVideoOrientation: a.lockedVideoOrientation,
sendFrameMeta: a.sendFrameMeta,
codecOptions: a.codecOptions,
});
}

Expand All @@ -78,6 +83,7 @@ export default class VideoSettings {
return false;
}
return (
this.codecOptions === o.codecOptions &&
Rect.equals(this.crop, o.crop) &&
this.lockedVideoOrientation === o.lockedVideoOrientation &&
this.maxSize === o.maxSize &&
Expand All @@ -101,6 +107,8 @@ export default class VideoSettings {
offset = buffer.writeUInt16BE(bottom, offset);
offset = buffer.writeUInt8(this.sendFrameMeta ? 1 : 0, offset);
buffer.writeInt8(this.lockedVideoOrientation, offset);
// FIXME: codec options are ignored
// should be something like: "codecOptions=`i-frame-interval=${iFrameInterval}`";
return buffer;
}

Expand All @@ -125,6 +133,7 @@ export default class VideoSettings {
crop: this.crop,
sendFrameMeta: this.sendFrameMeta,
lockedVideoOrientation: this.lockedVideoOrientation,
codecOptions: this.codecOptions,
};
}
}
23 changes: 13 additions & 10 deletions src/controlEvent/CommandControlEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,20 @@ export default class CommandControlEvent extends ControlEvent {
return event;
}

public static createSetClipboardCommand(text: string): CommandControlEvent {
public static createSetClipboardCommand(text: string, paste: boolean = false): CommandControlEvent {
const event = new CommandControlEvent(ControlEvent.TYPE_SET_CLIPBOARD);
const temp = Util.stringToUtf8ByteArray(text);
let offset = CommandControlEvent.PAYLOAD_LENGTH + 1;
const buffer = new Buffer(offset + 2 + temp.length);
buffer.writeUInt8(event.type, 0);
buffer.writeUInt16BE(temp.length, offset);
offset += 2;
temp.forEach((byte, index) => {
buffer.writeUInt8(byte, index + offset);
});
const textBytes: Uint8Array | null = text ? Util.stringToUtf8ByteArray(text) : null;
const textLength = textBytes ? textBytes.length : 0;
let offset = 0;
const buffer = new Buffer(1 + 1 + 4 + textLength);
offset = buffer.writeInt8(event.type, offset);
offset = buffer.writeInt8(paste ? 1 : 0, offset);
offset = buffer.writeInt32BE(textLength, offset);
if (textBytes) {
textBytes.forEach((byte: number, index: number) => {
buffer.writeUInt8(byte, index + offset);
});
}
event.buffer = buffer;
return event;
}
Expand Down
14 changes: 8 additions & 6 deletions src/controlEvent/KeyCodeControlEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Buffer } from 'buffer';
import ControlEvent from './ControlEvent';

export default class KeyCodeControlEvent extends ControlEvent {
public static PAYLOAD_LENGTH = 9;
public static PAYLOAD_LENGTH = 13;

constructor(readonly action: number, readonly keycode: number, readonly metaState: number) {
constructor(readonly action: number, readonly keycode: number, readonly repeat: number, readonly metaState: number) {
super(ControlEvent.TYPE_KEYCODE);
}

Expand All @@ -13,10 +13,12 @@ export default class KeyCodeControlEvent extends ControlEvent {
*/
public toBuffer(): Buffer {
const buffer = new Buffer(KeyCodeControlEvent.PAYLOAD_LENGTH + 1);
buffer.writeUInt8(this.type, 0);
buffer.writeUInt8(this.action, 1);
buffer.writeUInt32BE(this.keycode, 2);
buffer.writeUInt32BE(this.metaState, 6);
let offset = 0;
offset = buffer.writeInt8(this.type, offset);
offset = buffer.writeInt8(this.action, offset);
offset = buffer.writeInt32BE(this.keycode, offset);
offset = buffer.writeInt32BE(this.repeat, offset);
buffer.writeInt32BE(this.metaState, offset);
return buffer;
}

Expand Down
Binary file modified src/public/scrcpy-server.jar
Binary file not shown.
38 changes: 37 additions & 1 deletion src/server/Constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
export const SERVER_PACKAGE = 'com.genymobile.scrcpy.Server';
export const SERVER_PORT = 8886;
export const SERVER_VERSION = '1.13.1';
export const SERVER_VERSION = '1.15.1-ws';

const LOG_LEVEL = 'ERROR';
const MAX_SIZE = 0;
const BITRATE = 8000000;
const MAX_FPS = 60;
const LOCKED_SCREEN_ORIENTATION = -1;
const TUNNEL_FORWARD = false;
const CROP = '-';
const SEND_META_FRAME = false;
const CONTROL = true; // If control is enabled, synchronize Android clipboard to the computer automatically
const DISPLAY_ID = 0;
const SHOW_TOUCHES = false;
const STAY_AWAKE = false;
const CODEC_OPTIONS = '-';
const SERVER_TYPE = 'web';

const ARGUMENTS = [
SERVER_VERSION,
LOG_LEVEL,
MAX_SIZE,
BITRATE,
MAX_FPS,
LOCKED_SCREEN_ORIENTATION,
TUNNEL_FORWARD,
CROP,
SEND_META_FRAME,
CONTROL,
DISPLAY_ID,
SHOW_TOUCHES,
STAY_AWAKE,
CODEC_OPTIONS,
SERVER_TYPE,
SERVER_PORT
];

export const ARGS_STRING = `/ ${SERVER_PACKAGE} ${ARGUMENTS.join(' ')} 2>&1 > /dev/null`;
5 changes: 2 additions & 3 deletions src/server/ServerDeviceConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { spawn } from 'child_process';
import * as path from 'path';
import { Device } from '../common/Device';
import { AdbKitChangesSet, AdbKitClient, AdbKitDevice, AdbKitTracker, PushTransfer } from 'adbkit';
import { SERVER_PACKAGE, SERVER_PORT, SERVER_VERSION } from './Constants';
import { SERVER_PACKAGE, SERVER_VERSION, ARGS_STRING } from './Constants';
import Timeout = NodeJS.Timeout;

const TEMP_PATH = '/data/local/tmp/';
const FILE_DIR = path.join(__dirname, '../public');
const FILE_NAME = 'scrcpy-server.jar';
const ARGS = `/ ${SERVER_PACKAGE} ${SERVER_VERSION} 0 8000000 60 -1 false - false false 0 web ${SERVER_PORT} 2>&1 > /dev/null`;

const GET_SHELL_PROCESSES = 'for DIR in /proc/*; do [ -d "$DIR" ] && echo $DIR; done';
const CHECK_CMDLINE = `[ -f "$a/cmdline" ] && grep -av find "$a/cmdline" |grep -sae '^app_process.*${SERVER_PACKAGE}' |grep ${SERVER_VERSION} 2>&1 > /dev/null && echo $a;`;
Expand Down Expand Up @@ -218,7 +217,7 @@ export class ServerDeviceConnection extends EventEmitter {

private spawnServer(device: AdbKitDevice): void {
const { id: udid } = device;
const command = `CLASSPATH=${TEMP_PATH}${FILE_NAME} nohup app_process ${ARGS}`;
const command = `CLASSPATH=${TEMP_PATH}${FILE_NAME} nohup app_process ${ARGS_STRING}`;
const adb = spawn('adb', ['-s', `${udid}`, 'shell', command], { stdio: ['ignore', 'pipe', 'pipe'] });

adb.stdout.on('data', (data) => {
Expand Down

0 comments on commit 2ede9e6

Please sign in to comment.