Compare commits
11 Commits
legacy_pas
...
adbkeyboar
Author | SHA1 | Date | |
---|---|---|---|
cd860bff81 | |||
160ae02b93 | |||
e970b4bda3 | |||
576814bcec | |||
42ab8fd611 | |||
363eeea19e | |||
76c2c6e69d | |||
d5f059c7cb | |||
adc547fa6e | |||
5dcfc0ebab | |||
ad5f567f07 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ build/
|
||||
.idea/
|
||||
.gradle/
|
||||
/x/
|
||||
local.properties
|
||||
|
31
README.md
31
README.md
@ -203,6 +203,22 @@ scrcpy --lock-video-orientation 3 # 90° clockwise
|
||||
This affects recording orientation.
|
||||
|
||||
|
||||
#### Encoder
|
||||
|
||||
Some devices have more than one encoder, and some of them may cause issues or
|
||||
crash. It is possible to select a different encoder:
|
||||
|
||||
```bash
|
||||
scrcpy --encoder OMX.qcom.video.encoder.avc
|
||||
```
|
||||
|
||||
To list the available encoders, you could pass an invalid encoder name, the
|
||||
error will give the available encoders:
|
||||
|
||||
```bash
|
||||
scrcpy --encoder _
|
||||
```
|
||||
|
||||
### Recording
|
||||
|
||||
It is possible to record the screen while mirroring:
|
||||
@ -548,6 +564,11 @@ into the device clipboard. As a consequence, any Android application could read
|
||||
its content. You should avoid to paste sensitive content (like passwords) that
|
||||
way.
|
||||
|
||||
Some devices do not behave as expected when setting the device clipboard
|
||||
programmatically. An option `--legacy-paste` is provided to change the behavior
|
||||
of <kbd>Ctrl</kbd>+<kbd>v</kbd> and <kbd>MOD</kbd>+<kbd>v</kbd> so that they
|
||||
also inject the computer clipboard text as a sequence of key events (the same
|
||||
way as <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>).
|
||||
|
||||
#### Pinch-to-zoom
|
||||
|
||||
@ -595,6 +616,16 @@ scrcpy --no-key-repeat
|
||||
```
|
||||
|
||||
|
||||
#### Right-click and middle-click
|
||||
|
||||
By default, right-click triggers BACK (or POWER on) and middle-click triggers
|
||||
HOME. To disable these shortcuts and forward the clicks to the device instead:
|
||||
|
||||
```bash
|
||||
scrcpy --forward-all-clicks
|
||||
```
|
||||
|
||||
|
||||
### File drop
|
||||
|
||||
#### Install APK
|
||||
|
14
app/scrcpy.1
14
app/scrcpy.1
@ -56,10 +56,18 @@ The list of possible display ids can be listed by "adb shell dumpsys display"
|
||||
|
||||
Default is 0.
|
||||
|
||||
.TP
|
||||
.BI "\-\-encoder " name
|
||||
Use a specific MediaCodec encoder (must be a H.264 encoder).
|
||||
|
||||
.TP
|
||||
.B \-\-force\-adb\-forward
|
||||
Do not attempt to use "adb reverse" to connect to the device.
|
||||
|
||||
.TP
|
||||
.B \-\-forward\-all\-clicks
|
||||
By default, right-click triggers BACK (or POWER on) and middle-click triggers HOME. This option disables these shortcuts and forward the clicks to the device instead.
|
||||
|
||||
.TP
|
||||
.B \-f, \-\-fullscreen
|
||||
Start in fullscreen.
|
||||
@ -68,6 +76,12 @@ Start in fullscreen.
|
||||
.B \-h, \-\-help
|
||||
Print this help.
|
||||
|
||||
.TP
|
||||
.B \-\-legacy\-paste
|
||||
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
|
||||
|
||||
This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically.
|
||||
|
||||
.TP
|
||||
.BI "\-\-lock\-video\-orientation " value
|
||||
Lock video orientation to \fIvalue\fR. Possible values are -1 (unlocked), 0, 1, 2 and 3. Natural device orientation is 0, and each increment adds a 90 degrees otation counterclockwise.
|
||||
|
@ -53,16 +53,30 @@ scrcpy_print_usage(const char *arg0) {
|
||||
"\n"
|
||||
" Default is 0.\n"
|
||||
"\n"
|
||||
" --encoder name\n"
|
||||
" Use a specific MediaCodec encoder (must be a H.264 encoder).\n"
|
||||
"\n"
|
||||
" --force-adb-forward\n"
|
||||
" Do not attempt to use \"adb reverse\" to connect to the\n"
|
||||
" the device.\n"
|
||||
"\n"
|
||||
" --forward-all-clicks\n"
|
||||
" By default, right-click triggers BACK (or POWER on) and\n"
|
||||
" middle-click triggers HOME. This option disables these\n"
|
||||
" shortcuts and forward the clicks to the device instead.\n"
|
||||
"\n"
|
||||
" -f, --fullscreen\n"
|
||||
" Start in fullscreen.\n"
|
||||
"\n"
|
||||
" -h, --help\n"
|
||||
" Print this help.\n"
|
||||
"\n"
|
||||
" --legacy-paste\n"
|
||||
" Inject computer clipboard text as a sequence of key events\n"
|
||||
" on Ctrl+v (like MOD+Shift+v).\n"
|
||||
" This is a workaround for some devices not behaving as\n"
|
||||
" expected when setting the device clipboard programmatically.\n"
|
||||
"\n"
|
||||
" --lock-video-orientation value\n"
|
||||
" Lock video orientation to value.\n"
|
||||
" Possible values are -1 (unlocked), 0, 1, 2 and 3.\n"
|
||||
@ -651,6 +665,10 @@ guess_record_format(const char *filename) {
|
||||
#define OPT_DISABLE_SCREENSAVER 1020
|
||||
#define OPT_SHORTCUT_MOD 1021
|
||||
#define OPT_NO_KEY_REPEAT 1022
|
||||
#define OPT_FORWARD_ALL_CLICKS 1023
|
||||
#define OPT_LEGACY_PASTE 1024
|
||||
#define OPT_ENCODER_NAME 1025
|
||||
#define OPT_USE_ADB_KEYBOARD 1026
|
||||
|
||||
bool
|
||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
@ -662,10 +680,14 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
{"disable-screensaver", no_argument, NULL,
|
||||
OPT_DISABLE_SCREENSAVER},
|
||||
{"display", required_argument, NULL, OPT_DISPLAY_ID},
|
||||
{"encoder", required_argument, NULL, OPT_ENCODER_NAME},
|
||||
{"force-adb-forward", no_argument, NULL,
|
||||
OPT_FORCE_ADB_FORWARD},
|
||||
{"forward-all-clicks", no_argument, NULL,
|
||||
OPT_FORWARD_ALL_CLICKS},
|
||||
{"fullscreen", no_argument, NULL, 'f'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"legacy-paste", no_argument, NULL, OPT_LEGACY_PASTE},
|
||||
{"lock-video-orientation", required_argument, NULL,
|
||||
OPT_LOCK_VIDEO_ORIENTATION},
|
||||
{"max-fps", required_argument, NULL, OPT_MAX_FPS},
|
||||
@ -688,6 +710,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
{"show-touches", no_argument, NULL, 't'},
|
||||
{"stay-awake", no_argument, NULL, 'w'},
|
||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
||||
{"use-adb-keyboard", no_argument, NULL, OPT_USE_ADB_KEYBOARD},
|
||||
{"verbosity", required_argument, NULL, 'V'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"window-title", required_argument, NULL, OPT_WINDOW_TITLE},
|
||||
@ -773,6 +796,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
case 'S':
|
||||
opts->turn_screen_off = true;
|
||||
break;
|
||||
case OPT_USE_ADB_KEYBOARD:
|
||||
opts->use_adb_keyboard = true;
|
||||
break;
|
||||
case 't':
|
||||
opts->show_touches = true;
|
||||
break;
|
||||
@ -845,6 +871,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
case OPT_CODEC_OPTIONS:
|
||||
opts->codec_options = optarg;
|
||||
break;
|
||||
case OPT_ENCODER_NAME:
|
||||
opts->encoder_name = optarg;
|
||||
break;
|
||||
case OPT_FORCE_ADB_FORWARD:
|
||||
opts->force_adb_forward = true;
|
||||
break;
|
||||
@ -856,6 +885,12 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPT_FORWARD_ALL_CLICKS:
|
||||
opts->forward_all_clicks = true;
|
||||
break;
|
||||
case OPT_LEGACY_PASTE:
|
||||
opts->legacy_paste = true;
|
||||
break;
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
return false;
|
||||
|
@ -60,6 +60,8 @@ input_manager_init(struct input_manager *im,
|
||||
im->control = options->control;
|
||||
im->forward_key_repeat = options->forward_key_repeat;
|
||||
im->prefer_text = options->prefer_text;
|
||||
im->forward_all_clicks = options->forward_all_clicks;
|
||||
im->legacy_paste = options->legacy_paste;
|
||||
|
||||
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
|
||||
assert(shortcut_mods->count);
|
||||
@ -440,7 +442,7 @@ input_manager_process_key(struct input_manager *im,
|
||||
return;
|
||||
case SDLK_v:
|
||||
if (control && !repeat && down) {
|
||||
if (shift) {
|
||||
if (shift || im->legacy_paste) {
|
||||
// inject the text as input events
|
||||
clipboard_paste(controller);
|
||||
} else {
|
||||
@ -504,6 +506,11 @@ input_manager_process_key(struct input_manager *im,
|
||||
}
|
||||
|
||||
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
|
||||
if (im->legacy_paste) {
|
||||
// inject the text as input events
|
||||
clipboard_paste(controller);
|
||||
return;
|
||||
}
|
||||
// Synchronize the computer clipboard to the device clipboard before
|
||||
// sending Ctrl+v, to allow seamless copy-paste.
|
||||
set_device_clipboard(controller, false);
|
||||
@ -629,7 +636,7 @@ input_manager_process_mouse_button(struct input_manager *im,
|
||||
}
|
||||
|
||||
bool down = event->type == SDL_MOUSEBUTTONDOWN;
|
||||
if (down) {
|
||||
if (!im->forward_all_clicks && down) {
|
||||
if (control && event->button == SDL_BUTTON_RIGHT) {
|
||||
press_back_or_turn_screen_on(im->controller);
|
||||
return;
|
||||
|
@ -25,6 +25,8 @@ struct input_manager {
|
||||
bool control;
|
||||
bool forward_key_repeat;
|
||||
bool prefer_text;
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
|
||||
struct {
|
||||
unsigned data[SC_MAX_SHORTCUT_MODS];
|
||||
|
@ -318,7 +318,9 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
.show_touches = options->show_touches,
|
||||
.stay_awake = options->stay_awake,
|
||||
.codec_options = options->codec_options,
|
||||
.encoder_name = options->encoder_name,
|
||||
.force_adb_forward = options->force_adb_forward,
|
||||
.use_adb_keyboard = options->use_adb_keyboard,
|
||||
};
|
||||
if (!server_start(&server, options->serial, ¶ms)) {
|
||||
return false;
|
||||
@ -422,7 +424,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
options->window_y, options->window_width,
|
||||
options->window_height,
|
||||
options->window_borderless,
|
||||
options->rotation, options-> mipmaps)) {
|
||||
options->rotation, options->mipmaps)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ struct scrcpy_options {
|
||||
const char *push_target;
|
||||
const char *render_driver;
|
||||
const char *codec_options;
|
||||
const char *encoder_name;
|
||||
enum sc_log_level log_level;
|
||||
enum sc_record_format record_format;
|
||||
struct sc_port_range port_range;
|
||||
@ -79,6 +80,9 @@ struct scrcpy_options {
|
||||
bool force_adb_forward;
|
||||
bool disable_screensaver;
|
||||
bool forward_key_repeat;
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
bool use_adb_keyboard;
|
||||
};
|
||||
|
||||
#define SCRCPY_OPTIONS_DEFAULT { \
|
||||
@ -89,6 +93,7 @@ struct scrcpy_options {
|
||||
.push_target = NULL, \
|
||||
.render_driver = NULL, \
|
||||
.codec_options = NULL, \
|
||||
.encoder_name = NULL, \
|
||||
.log_level = SC_LOG_LEVEL_INFO, \
|
||||
.record_format = SC_RECORD_FORMAT_AUTO, \
|
||||
.port_range = { \
|
||||
@ -123,6 +128,9 @@ struct scrcpy_options {
|
||||
.force_adb_forward = false, \
|
||||
.disable_screensaver = false, \
|
||||
.forward_key_repeat = true, \
|
||||
.forward_all_clicks = false, \
|
||||
.legacy_paste = false, \
|
||||
.use_adb_keyboard = false, \
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -294,6 +294,8 @@ execute_server(struct server *server, const struct server_params *params) {
|
||||
params->show_touches ? "true" : "false",
|
||||
params->stay_awake ? "true" : "false",
|
||||
params->codec_options ? params->codec_options : "-",
|
||||
params->encoder_name ? params->encoder_name : "-",
|
||||
params->use_adb_keyboard ? "true" : "false",
|
||||
};
|
||||
#ifdef SERVER_DEBUGGER
|
||||
LOGI("Server debugger waiting for a client on device port "
|
||||
|
@ -48,6 +48,7 @@ struct server_params {
|
||||
enum sc_log_level log_level;
|
||||
const char *crop;
|
||||
const char *codec_options;
|
||||
const char *encoder_name;
|
||||
struct sc_port_range port_range;
|
||||
uint16_t max_size;
|
||||
uint32_t bit_rate;
|
||||
@ -58,6 +59,7 @@ struct server_params {
|
||||
bool show_touches;
|
||||
bool stay_awake;
|
||||
bool force_adb_forward;
|
||||
bool use_adb_keyboard;
|
||||
};
|
||||
|
||||
// init default values
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.view.InputDevice;
|
||||
@ -20,6 +21,7 @@ public class Controller {
|
||||
|
||||
private final Device device;
|
||||
private final DesktopConnection connection;
|
||||
private final Options options;
|
||||
private final DeviceMessageSender sender;
|
||||
|
||||
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
||||
@ -31,9 +33,10 @@ public class Controller {
|
||||
|
||||
private boolean keepPowerModeOff;
|
||||
|
||||
public Controller(Device device, DesktopConnection connection) {
|
||||
public Controller(Device device, DesktopConnection connection, Options options) {
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
this.options = options;
|
||||
initPointers();
|
||||
sender = new DeviceMessageSender(connection);
|
||||
}
|
||||
@ -160,8 +163,20 @@ public class Controller {
|
||||
}
|
||||
|
||||
private int injectText(String text) {
|
||||
char[] chars = text.toCharArray();
|
||||
if (options.useADBKeyboard()) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction("ADB_INPUT_CHARS");
|
||||
int[] intChars = new int[chars.length];
|
||||
for (int i = 0; i < chars.length; ++i) {
|
||||
intChars[i] = chars[i];
|
||||
}
|
||||
intent.putExtra("chars", intChars);
|
||||
device.sendBroadcast(intent);
|
||||
return chars.length;
|
||||
}
|
||||
int successCount = 0;
|
||||
for (char c : text.toCharArray()) {
|
||||
for (char c : chars) {
|
||||
if (!injectChar(c)) {
|
||||
Ln.w("Could not inject char u+" + String.format("%04x", (int) c));
|
||||
continue;
|
||||
@ -205,9 +220,13 @@ public class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// Right-click and middle-click only work if the source is a mouse
|
||||
boolean nonPrimaryButtonPressed = (buttons & ~MotionEvent.BUTTON_PRIMARY) != 0;
|
||||
int source = nonPrimaryButtonPressed ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_TOUCHSCREEN;
|
||||
|
||||
MotionEvent event = MotionEvent
|
||||
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
|
||||
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, source,
|
||||
0);
|
||||
return device.injectEvent(event);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||
|
||||
import android.content.IOnPrimaryClipChangedListener;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
@ -273,4 +274,8 @@ public final class Device {
|
||||
public static ContentProvider createSettingsProvider() {
|
||||
return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {
|
||||
SERVICE_MANAGER.getActivityManager().sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.media.MediaCodecInfo;
|
||||
|
||||
public class InvalidEncoderException extends RuntimeException {
|
||||
|
||||
private final String name;
|
||||
private final MediaCodecInfo[] availableEncoders;
|
||||
|
||||
public InvalidEncoderException(String name, MediaCodecInfo[] availableEncoders) {
|
||||
super("There is no encoder having name '" + name + '"');
|
||||
this.name = name;
|
||||
this.availableEncoders = availableEncoders;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public MediaCodecInfo[] getAvailableEncoders() {
|
||||
return availableEncoders;
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ public class Options {
|
||||
private boolean showTouches;
|
||||
private boolean stayAwake;
|
||||
private String codecOptions;
|
||||
private String encoderName;
|
||||
private boolean useADBKeyboard;
|
||||
|
||||
public Ln.Level getLogLevel() {
|
||||
return logLevel;
|
||||
@ -120,4 +122,20 @@ public class Options {
|
||||
public void setCodecOptions(String codecOptions) {
|
||||
this.codecOptions = codecOptions;
|
||||
}
|
||||
|
||||
public String getEncoderName() {
|
||||
return encoderName;
|
||||
}
|
||||
|
||||
public void setEncoderName(String encoderName) {
|
||||
this.encoderName = encoderName;
|
||||
}
|
||||
|
||||
public boolean useADBKeyboard() {
|
||||
return useADBKeyboard;
|
||||
}
|
||||
|
||||
public void setUseADBKeyboard(boolean useADBKeyboard) {
|
||||
this.useADBKeyboard = useADBKeyboard;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.IBinder;
|
||||
import android.view.Surface;
|
||||
@ -12,6 +13,8 @@ import android.view.Surface;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@ -26,17 +29,19 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
private final AtomicBoolean rotationChanged = new AtomicBoolean();
|
||||
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
|
||||
|
||||
private String encoderName;
|
||||
private List<CodecOption> codecOptions;
|
||||
private int bitRate;
|
||||
private int maxFps;
|
||||
private boolean sendFrameMeta;
|
||||
private long ptsOrigin;
|
||||
|
||||
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions) {
|
||||
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName) {
|
||||
this.sendFrameMeta = sendFrameMeta;
|
||||
this.bitRate = bitRate;
|
||||
this.maxFps = maxFps;
|
||||
this.codecOptions = codecOptions;
|
||||
this.encoderName = encoderName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,7 +74,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
boolean alive;
|
||||
try {
|
||||
do {
|
||||
MediaCodec codec = createCodec();
|
||||
MediaCodec codec = createCodec(encoderName);
|
||||
IBinder display = createDisplay();
|
||||
ScreenInfo screenInfo = device.getScreenInfo();
|
||||
Rect contentRect = screenInfo.getContentRect();
|
||||
@ -150,8 +155,30 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
IO.writeFully(fd, headerBuffer);
|
||||
}
|
||||
|
||||
private static MediaCodec createCodec() throws IOException {
|
||||
return MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
|
||||
private static MediaCodecInfo[] listEncoders() {
|
||||
List<MediaCodecInfo> result = new ArrayList<>();
|
||||
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||
for (MediaCodecInfo codecInfo : list.getCodecInfos()) {
|
||||
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) {
|
||||
result.add(codecInfo);
|
||||
}
|
||||
}
|
||||
return result.toArray(new MediaCodecInfo[result.size()]);
|
||||
}
|
||||
|
||||
private static MediaCodec createCodec(String encoderName) throws IOException {
|
||||
if (encoderName != null) {
|
||||
Ln.d("Creating encoder by name: '" + encoderName + "'");
|
||||
try {
|
||||
return MediaCodec.createByCodecName(encoderName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
MediaCodecInfo[] encoders = listEncoders();
|
||||
throw new InvalidEncoderException(encoderName, encoders);
|
||||
}
|
||||
}
|
||||
MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
|
||||
Ln.d("Using encoder: '" + codec.getName() + "'");
|
||||
return codec;
|
||||
}
|
||||
|
||||
private static void setCodecOption(MediaFormat format, CodecOption codecOption) {
|
||||
|
@ -4,6 +4,7 @@ import com.genymobile.scrcpy.wrappers.ContentProvider;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
|
||||
@ -54,10 +55,11 @@ public final class Server {
|
||||
boolean tunnelForward = options.isTunnelForward();
|
||||
|
||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions);
|
||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
||||
options.getEncoderName());
|
||||
|
||||
if (options.getControl()) {
|
||||
final Controller controller = new Controller(device, connection);
|
||||
final Controller controller = new Controller(device, connection, options);
|
||||
|
||||
// asynchronous
|
||||
startController(controller);
|
||||
@ -120,7 +122,7 @@ public final class Server {
|
||||
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
||||
}
|
||||
|
||||
final int expectedParameters = 14;
|
||||
final int expectedParameters = 16;
|
||||
if (args.length != expectedParameters) {
|
||||
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
|
||||
}
|
||||
@ -167,6 +169,12 @@ public final class Server {
|
||||
String codecOptions = args[13];
|
||||
options.setCodecOptions(codecOptions);
|
||||
|
||||
String encoderName = "-".equals(args[14]) ? null : args[14];
|
||||
options.setEncoderName(encoderName);
|
||||
|
||||
boolean useADBKeyboard = Boolean.parseBoolean(args[15]);
|
||||
options.setUseADBKeyboard(useADBKeyboard);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@ -206,6 +214,15 @@ public final class Server {
|
||||
Ln.e(" scrcpy --display " + id);
|
||||
}
|
||||
}
|
||||
} else if (e instanceof InvalidEncoderException) {
|
||||
InvalidEncoderException iee = (InvalidEncoderException) e;
|
||||
MediaCodecInfo[] encoders = iee.getAvailableEncoders();
|
||||
if (encoders != null && encoders.length > 0) {
|
||||
Ln.e("Try to use one of the available encoders:");
|
||||
for (MediaCodecInfo encoder : encoders) {
|
||||
Ln.e(" scrcpy --encoder-name '" + encoder.getName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,9 @@ package com.genymobile.scrcpy.wrappers;
|
||||
|
||||
import com.genymobile.scrcpy.Ln;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
|
||||
@ -16,6 +18,7 @@ public class ActivityManager {
|
||||
private Method getContentProviderExternalMethod;
|
||||
private boolean getContentProviderExternalMethodLegacy;
|
||||
private Method removeContentProviderExternalMethod;
|
||||
private Method broadcastIntentMethod;
|
||||
|
||||
public ActivityManager(IInterface manager) {
|
||||
this.manager = manager;
|
||||
@ -42,6 +45,22 @@ public class ActivityManager {
|
||||
return removeContentProviderExternalMethod;
|
||||
}
|
||||
|
||||
private Method getBroadcastIntentMethod() throws NoSuchMethodException {
|
||||
if (broadcastIntentMethod == null) {
|
||||
try {
|
||||
Class<?> iApplicationThreadClass = Class.forName("android.app.IApplicationThread");
|
||||
Class<?> iIntentReceiverClass = Class.forName("android.content.IIntentReceiver");
|
||||
broadcastIntentMethod = manager.getClass()
|
||||
.getMethod("broadcastIntent", iApplicationThreadClass, Intent.class, String.class, iIntentReceiverClass, int.class,
|
||||
String.class, Bundle.class, String[].class, int.class, Bundle.class, boolean.class, boolean.class, int.class);
|
||||
return broadcastIntentMethod;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
return broadcastIntentMethod;
|
||||
}
|
||||
|
||||
private ContentProvider getContentProviderExternal(String name, IBinder token) {
|
||||
try {
|
||||
Method method = getGetContentProviderExternalMethod();
|
||||
@ -84,4 +103,13 @@ public class ActivityManager {
|
||||
public ContentProvider createSettingsProvider() {
|
||||
return getContentProviderExternal("settings", new Binder());
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {
|
||||
try {
|
||||
Method method = getBroadcastIntentMethod();
|
||||
method.invoke(manager, null, intent, null, null, 0, null, null, null, -1, null, true, false, ServiceManager.USER_ID);
|
||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||
Ln.e("Could not invoke method", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user