mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-02-25 15:54:28 +01:00
Compare commits
2 Commits
icon_decod
...
window.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be9bf7d756 | ||
|
|
f7c0826360 |
@@ -194,11 +194,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
||||
// Still insufficient, drop old samples to make space
|
||||
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
|
||||
assert(skipped_samples == remaining);
|
||||
}
|
||||
|
||||
SDL_UnlockAudioDevice(ap->device);
|
||||
|
||||
if (written < samples) {
|
||||
// Now there is enough space
|
||||
uint32_t w = sc_audiobuf_write(&ap->buf,
|
||||
swr_buf + TO_BYTES(written),
|
||||
@@ -206,6 +202,8 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
||||
assert(w == remaining);
|
||||
(void) w;
|
||||
}
|
||||
|
||||
SDL_UnlockAudioDevice(ap->device);
|
||||
}
|
||||
|
||||
uint32_t underflow = 0;
|
||||
|
||||
@@ -2585,11 +2585,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
|
||||
#ifdef HAVE_V4L2
|
||||
if (v4l2) {
|
||||
if (!opts->video) {
|
||||
LOGE("V4L2 sink requires video capture, but --no-video was set.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->lock_video_orientation ==
|
||||
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||
LOGI("Video orientation is locked for v4l2 sink. "
|
||||
@@ -2618,8 +2613,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
if (otg) {
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
|
||||
} else if (!opts->video_playback) {
|
||||
LOGI("No video mirroring, mouse mode switched to UHID");
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
|
||||
LOGI("No video mirroring, SDK mouse disabled (you might want "
|
||||
"--mouse=uhid)");
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED;
|
||||
} else {
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
|
||||
}
|
||||
@@ -2738,11 +2734,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
|
||||
if (opts->record_filename) {
|
||||
if (!opts->video && !opts->audio) {
|
||||
LOGE("Video and audio disabled, nothing to record");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opts->record_format) {
|
||||
opts->record_format = guess_record_format(opts->record_filename);
|
||||
if (!opts->record_format) {
|
||||
|
||||
@@ -6,19 +6,8 @@
|
||||
|
||||
#define SC_CONTROL_MSG_QUEUE_MAX 64
|
||||
|
||||
static void
|
||||
sc_controller_receiver_on_error(struct sc_receiver *receiver, void *userdata) {
|
||||
(void) receiver;
|
||||
|
||||
struct sc_controller *controller = userdata;
|
||||
// Forward the event to the controller listener
|
||||
controller->cbs->on_error(controller, controller->cbs_userdata);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
const struct sc_controller_callbacks *cbs,
|
||||
void *cbs_userdata) {
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket) {
|
||||
sc_vecdeque_init(&controller->queue);
|
||||
|
||||
bool ok = sc_vecdeque_reserve(&controller->queue, SC_CONTROL_MSG_QUEUE_MAX);
|
||||
@@ -26,12 +15,7 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct sc_receiver_callbacks receiver_cbs = {
|
||||
.on_error = sc_controller_receiver_on_error,
|
||||
};
|
||||
|
||||
ok = sc_receiver_init(&controller->receiver, control_socket, &receiver_cbs,
|
||||
controller);
|
||||
ok = sc_receiver_init(&controller->receiver, control_socket);
|
||||
if (!ok) {
|
||||
sc_vecdeque_destroy(&controller->queue);
|
||||
return false;
|
||||
@@ -55,10 +39,6 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
controller->control_socket = control_socket;
|
||||
controller->stopped = false;
|
||||
|
||||
assert(cbs && cbs->on_error);
|
||||
controller->cbs = cbs;
|
||||
controller->cbs_userdata = cbs_userdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,16 +125,10 @@ run_controller(void *data) {
|
||||
sc_control_msg_destroy(&msg);
|
||||
if (!ok) {
|
||||
LOGD("Could not write msg to socket");
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
controller->cbs->on_error(controller, controller->cbs_userdata);
|
||||
|
||||
return 1; // ignored
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -22,19 +22,10 @@ struct sc_controller {
|
||||
bool stopped;
|
||||
struct sc_control_msg_queue queue;
|
||||
struct sc_receiver receiver;
|
||||
|
||||
const struct sc_controller_callbacks *cbs;
|
||||
void *cbs_userdata;
|
||||
};
|
||||
|
||||
struct sc_controller_callbacks {
|
||||
void (*on_error)(struct sc_controller *controller, void *userdata);
|
||||
};
|
||||
|
||||
bool
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||
const struct sc_controller_callbacks *cbs,
|
||||
void *cbs_userdata);
|
||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket);
|
||||
|
||||
void
|
||||
sc_controller_configure(struct sc_controller *controller,
|
||||
|
||||
@@ -23,6 +23,12 @@ sc_display_init_novideo_icon(struct sc_display *display,
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_RenderClear(display->renderer);
|
||||
if (display->texture) {
|
||||
SDL_RenderCopy(display->renderer, display->texture, NULL, NULL);
|
||||
}
|
||||
SDL_RenderPresent(display->renderer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@
|
||||
#define SC_EVENT_RECORDER_ERROR (SDL_USEREVENT + 6)
|
||||
#define SC_EVENT_SCREEN_INIT_SIZE (SDL_USEREVENT + 7)
|
||||
#define SC_EVENT_TIME_LIMIT_REACHED (SDL_USEREVENT + 8)
|
||||
#define SC_EVENT_CONTROLLER_ERROR (SDL_USEREVENT + 9)
|
||||
|
||||
@@ -78,10 +78,7 @@ decode_image(const char *path) {
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
const AVCodec *codec;
|
||||
|
||||
int stream =
|
||||
av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
|
||||
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
||||
if (stream < 0 ) {
|
||||
LOGE("Could not find best image stream");
|
||||
goto close_input;
|
||||
@@ -89,6 +86,12 @@ decode_image(const char *path) {
|
||||
|
||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||
|
||||
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
||||
if (!codec) {
|
||||
LOGE("Could not find image decoder");
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
||||
if (!codec_ctx) {
|
||||
LOG_OOM();
|
||||
|
||||
@@ -748,8 +748,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
||||
}
|
||||
|
||||
// double-click on black borders resize to fit the device screen
|
||||
bool video = im->screen->video;
|
||||
if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||
int32_t x = event->x;
|
||||
int32_t y = event->y;
|
||||
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "util/str.h"
|
||||
|
||||
bool
|
||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
|
||||
const struct sc_receiver_callbacks *cbs, void *cbs_userdata) {
|
||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket) {
|
||||
bool ok = sc_mutex_init(&receiver->mutex);
|
||||
if (!ok) {
|
||||
return false;
|
||||
@@ -21,10 +20,6 @@ sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
|
||||
receiver->acksync = NULL;
|
||||
receiver->uhid_devices = NULL;
|
||||
|
||||
assert(cbs && cbs->on_error);
|
||||
receiver->cbs = cbs;
|
||||
receiver->cbs_userdata = cbs_userdata;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -157,8 +152,6 @@ run_receiver(void *data) {
|
||||
}
|
||||
}
|
||||
|
||||
receiver->cbs->on_error(receiver, receiver->cbs_userdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,18 +19,10 @@ struct sc_receiver {
|
||||
|
||||
struct sc_acksync *acksync;
|
||||
struct sc_uhid_devices *uhid_devices;
|
||||
|
||||
const struct sc_receiver_callbacks *cbs;
|
||||
void *cbs_userdata;
|
||||
};
|
||||
|
||||
struct sc_receiver_callbacks {
|
||||
void (*on_error)(struct sc_receiver *receiver, void *userdata);
|
||||
};
|
||||
|
||||
bool
|
||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
|
||||
const struct sc_receiver_callbacks *cbs, void *cbs_userdata);
|
||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket);
|
||||
|
||||
void
|
||||
sc_receiver_destroy(struct sc_receiver *receiver);
|
||||
|
||||
@@ -174,9 +174,6 @@ event_loop(struct scrcpy *s) {
|
||||
case SC_EVENT_DEMUXER_ERROR:
|
||||
LOGE("Demuxer error");
|
||||
return SCRCPY_EXIT_FAILURE;
|
||||
case SC_EVENT_CONTROLLER_ERROR:
|
||||
LOGE("Controller error");
|
||||
return SCRCPY_EXIT_FAILURE;
|
||||
case SC_EVENT_RECORDER_ERROR:
|
||||
LOGE("Recorder error");
|
||||
return SCRCPY_EXIT_FAILURE;
|
||||
@@ -268,16 +265,6 @@ sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_controller_on_error(struct sc_controller *controller, void *userdata) {
|
||||
// Note: this function may be called twice, once from the controller thread
|
||||
// and once from the receiver thread
|
||||
(void) controller;
|
||||
(void) userdata;
|
||||
|
||||
PUSH_EVENT(SC_EVENT_CONTROLLER_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_server_on_connection_failed(struct sc_server *server, void *userdata) {
|
||||
(void) server;
|
||||
@@ -421,7 +408,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
return SCRCPY_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (options->window) {
|
||||
if (options->video_playback) {
|
||||
// Set hints before starting the server thread to avoid race conditions
|
||||
// in SDL
|
||||
sdl_set_hints(options->render_driver);
|
||||
@@ -566,12 +553,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
struct sc_mouse_processor *mp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
static const struct sc_controller_callbacks controller_cbs = {
|
||||
.on_error = sc_controller_on_error,
|
||||
};
|
||||
|
||||
if (!sc_controller_init(&s->controller, s->server.control_socket,
|
||||
&controller_cbs, NULL)) {
|
||||
if (!sc_controller_init(&s->controller, s->server.control_socket)) {
|
||||
goto end;
|
||||
}
|
||||
controller_initialized = true;
|
||||
|
||||
@@ -259,13 +259,6 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||
(void) res; // any error already logged
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_render_novideo(struct sc_screen *screen) {
|
||||
enum sc_display_result res =
|
||||
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
|
||||
(void) res; // any error already logged
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__WINDOWS__)
|
||||
# define CONTINUOUS_RESIZING_WORKAROUND
|
||||
#endif
|
||||
@@ -378,7 +371,6 @@ sc_screen_init(struct sc_screen *screen,
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
screen->paused = false;
|
||||
screen->resume_frame = NULL;
|
||||
screen->orientation = SC_ORIENTATION_0;
|
||||
|
||||
screen->video = params->video;
|
||||
|
||||
@@ -398,12 +390,10 @@ sc_screen_init(struct sc_screen *screen,
|
||||
goto error_destroy_frame_buffer;
|
||||
}
|
||||
|
||||
if (screen->video) {
|
||||
screen->orientation = params->orientation;
|
||||
if (screen->orientation != SC_ORIENTATION_0) {
|
||||
LOGI("Initial display orientation set to %s",
|
||||
sc_orientation_get_name(screen->orientation));
|
||||
}
|
||||
screen->orientation = params->orientation;
|
||||
if (screen->orientation != SC_ORIENTATION_0) {
|
||||
LOGI("Initial display orientation set to %s",
|
||||
sc_orientation_get_name(screen->orientation));
|
||||
}
|
||||
|
||||
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
@@ -413,11 +403,6 @@ sc_screen_init(struct sc_screen *screen,
|
||||
if (params->window_borderless) {
|
||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
if (params->video) {
|
||||
// The window will be shown on first frame
|
||||
window_flags |= SDL_WINDOW_HIDDEN
|
||||
| SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
|
||||
const char *title = params->window_title;
|
||||
assert(title);
|
||||
@@ -426,17 +411,22 @@ sc_screen_init(struct sc_screen *screen,
|
||||
int y = SDL_WINDOWPOS_UNDEFINED;
|
||||
int width = 256;
|
||||
int height = 256;
|
||||
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
|
||||
x = params->window_x;
|
||||
}
|
||||
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
|
||||
y = params->window_y;
|
||||
}
|
||||
if (params->window_width) {
|
||||
width = params->window_width;
|
||||
}
|
||||
if (params->window_height) {
|
||||
height = params->window_height;
|
||||
if (params->video) {
|
||||
// The window will be shown on first frame
|
||||
window_flags |= SDL_WINDOW_HIDDEN
|
||||
| SDL_WINDOW_RESIZABLE;
|
||||
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
|
||||
x = params->window_x;
|
||||
}
|
||||
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
|
||||
y = params->window_y;
|
||||
}
|
||||
if (params->window_width) {
|
||||
width = params->window_width;
|
||||
}
|
||||
if (params->window_height) {
|
||||
height = params->window_height;
|
||||
}
|
||||
}
|
||||
|
||||
// The window will be positioned and sized on first video frame
|
||||
@@ -459,9 +449,8 @@ sc_screen_init(struct sc_screen *screen,
|
||||
}
|
||||
|
||||
SDL_Surface *icon_novideo = params->video ? NULL : icon;
|
||||
bool mipmaps = params->video && params->mipmaps;
|
||||
ok = sc_display_init(&screen->display, screen->window, icon_novideo,
|
||||
mipmaps);
|
||||
params->mipmaps);
|
||||
if (icon) {
|
||||
scrcpy_icon_destroy(icon);
|
||||
}
|
||||
@@ -868,11 +857,6 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
||||
return true;
|
||||
}
|
||||
case SDL_WINDOWEVENT:
|
||||
if (!screen->video
|
||||
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||
sc_screen_render_novideo(screen);
|
||||
}
|
||||
|
||||
// !video implies !has_frame
|
||||
assert(screen->video || !screen->has_frame);
|
||||
if (!screen->has_frame) {
|
||||
|
||||
@@ -176,8 +176,6 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
|
||||
free(lpAttributeList);
|
||||
}
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
// These handles are used by the child process, close them for this process
|
||||
if (pin) {
|
||||
CloseHandle(stdin_read_handle);
|
||||
|
||||
@@ -46,9 +46,6 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
|
||||
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
|
||||
|
||||
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
|
||||
if (!can_read) {
|
||||
return 0;
|
||||
}
|
||||
if (samples_count > can_read) {
|
||||
samples_count = can_read;
|
||||
}
|
||||
@@ -89,9 +86,6 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_,
|
||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
||||
|
||||
uint32_t can_write = (buf->alloc_size + tail - head - 1) % buf->alloc_size;
|
||||
if (!can_write) {
|
||||
return 0;
|
||||
}
|
||||
if (samples_count > can_write) {
|
||||
samples_count = can_write;
|
||||
}
|
||||
|
||||
@@ -17,26 +17,11 @@ Read [keyboard](keyboard.md) and [mouse](mouse.md).
|
||||
|
||||
## Control only
|
||||
|
||||
To control the device without mirroring:
|
||||
To control only with UHID mouse and keyboard:
|
||||
|
||||
```bash
|
||||
scrcpy --no-video --no-audio
|
||||
```
|
||||
|
||||
By default, mouse mode is switched to UHID if video mirroring is disabled (a
|
||||
relative mouse mode is required).
|
||||
|
||||
To also use a UHID keyboard, set it explicitly:
|
||||
|
||||
```bash
|
||||
scrcpy --no-video --no-audio --keyboard=uhid
|
||||
scrcpy --no-video --no-audio -K # short version
|
||||
```
|
||||
|
||||
To use AOA instead (over USB only):
|
||||
|
||||
```bash
|
||||
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa
|
||||
scrcpy --no-video --no-audio --keyboard=uhid --mouse=uhid
|
||||
scrcpy --no-video --no-audio -KM # short version
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -39,13 +39,13 @@ It only works if the device is connected over USB.
|
||||
See [FAQ](/FAQ.md#otg-issues-on-windows).
|
||||
|
||||
|
||||
## Control only
|
||||
## Control-only
|
||||
|
||||
Note that the purpose of OTG is to control the device without USB debugging
|
||||
(adb).
|
||||
|
||||
If you want to solely control the device without mirroring while USB debugging
|
||||
is enabled, then OTG mode is not necessary.
|
||||
If you want to only control the device without mirroring while USB debugging is
|
||||
enabled, then OTG mode is not necessary.
|
||||
|
||||
Instead, disable video and audio, and select UHID (or AOA):
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
this.downsizeOnError = downsizeOnError;
|
||||
}
|
||||
|
||||
private void streamCapture() throws IOException, ConfigurationException {
|
||||
private void streamScreen() throws IOException, ConfigurationException {
|
||||
Codec codec = streamer.getCodec();
|
||||
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
|
||||
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
|
||||
@@ -254,7 +254,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
Looper.prepare();
|
||||
|
||||
try {
|
||||
streamCapture();
|
||||
streamScreen();
|
||||
} catch (ConfigurationException e) {
|
||||
// Do not print stack trace, a user-friendly error-message has already been logged
|
||||
} catch (IOException e) {
|
||||
|
||||
Reference in New Issue
Block a user