Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
e250628fae | |||
a26c4e76da |
@ -66,6 +66,8 @@ static void SDLCALL
|
||||
sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
|
||||
struct sc_audio_player *ap = userdata;
|
||||
|
||||
// This callback is called with the lock used by SDL_LockAudioDevice()
|
||||
|
||||
assert(len_int > 0);
|
||||
size_t len = len_int;
|
||||
uint32_t count = TO_SAMPLES(len);
|
||||
@ -179,19 +181,29 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
||||
if (written < samples) {
|
||||
uint32_t remaining = samples - written;
|
||||
|
||||
assert(remaining <= cap);
|
||||
skipped_samples = sc_audiobuf_truncate(&ap->buf, cap - remaining);
|
||||
// All samples that could be written without locking have been written,
|
||||
// now we need to lock to drop/consume old samples
|
||||
SDL_LockAudioDevice(ap->device);
|
||||
|
||||
LOGW("Audio buffer full, %" PRIu32 " samples dropped", skipped_samples);
|
||||
// Retry with the lock
|
||||
written += sc_audiobuf_write(&ap->buf,
|
||||
swr_buf + TO_BYTES(written),
|
||||
remaining);
|
||||
if (written < samples) {
|
||||
remaining = samples - written;
|
||||
// Still insufficient, drop old samples to make space
|
||||
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
|
||||
assert(skipped_samples == remaining);
|
||||
|
||||
// Now there is enough space
|
||||
uint32_t w = sc_audiobuf_write(&ap->buf,
|
||||
swr_buf + TO_BYTES(written),
|
||||
remaining);
|
||||
assert(w == remaining);
|
||||
(void) w;
|
||||
// Now there is enough space
|
||||
uint32_t w = sc_audiobuf_write(&ap->buf,
|
||||
swr_buf + TO_BYTES(written),
|
||||
remaining);
|
||||
assert(w == remaining);
|
||||
(void) w;
|
||||
}
|
||||
|
||||
written = samples;
|
||||
SDL_UnlockAudioDevice(ap->device);
|
||||
}
|
||||
|
||||
uint32_t underflow = 0;
|
||||
@ -213,22 +225,30 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
||||
|
||||
uint32_t can_read = sc_audiobuf_can_read(&ap->buf);
|
||||
if (can_read > max_buffered_samples) {
|
||||
uint32_t skipped = sc_audiobuf_truncate(&ap->buf, max_buffered_samples);
|
||||
assert(skipped);
|
||||
|
||||
if (played) {
|
||||
LOGD("[Audio] Buffering threshold exceeded, skipping %" PRIu32
|
||||
" samples", skipped);
|
||||
#ifndef SC_AUDIO_PLAYER_NDEBUG
|
||||
} else {
|
||||
LOGD("[Audio] Playback not started, skipping %" PRIu32 " samples",
|
||||
skipped);
|
||||
#endif
|
||||
}
|
||||
|
||||
skipped_samples += skipped;
|
||||
uint32_t skip_samples = 0;
|
||||
|
||||
SDL_LockAudioDevice(ap->device);
|
||||
can_read = sc_audiobuf_can_read(&ap->buf);
|
||||
if (can_read > max_buffered_samples) {
|
||||
skip_samples = can_read - max_buffered_samples;
|
||||
uint32_t r = sc_audiobuf_read(&ap->buf, NULL, skip_samples);
|
||||
assert(r == skip_samples);
|
||||
(void) r;
|
||||
skipped_samples += skip_samples;
|
||||
}
|
||||
SDL_UnlockAudioDevice(ap->device);
|
||||
|
||||
if (skip_samples) {
|
||||
if (played) {
|
||||
LOGD("[Audio] Buffering threshold exceeded, skipping %" PRIu32
|
||||
" samples", skip_samples);
|
||||
#ifndef SC_AUDIO_PLAYER_NDEBUG
|
||||
} else {
|
||||
LOGD("[Audio] Playback not started, skipping %" PRIu32
|
||||
" samples", skip_samples);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
atomic_store_explicit(&ap->received, true, memory_order_relaxed);
|
||||
@ -388,7 +408,7 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
|
||||
// Use a ring-buffer of the target buffering size plus 1 second between the
|
||||
// producer and the consumer. It's too big on purpose, to guarantee that
|
||||
// the producer and the consumer will be able to access it in parallel
|
||||
// without dropping samples.
|
||||
// without locking.
|
||||
uint32_t audiobuf_samples = ap->target_buffering + ap->sample_rate;
|
||||
|
||||
size_t sample_size = ap->nb_channels * ap->out_bytes_per_sample;
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
|
@ -37,35 +37,34 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
|
||||
assert(samples_count);
|
||||
|
||||
uint8_t *to = to_;
|
||||
assert(to);
|
||||
|
||||
// The tail cursor may be updated by the writer thread to drop samples
|
||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
||||
// Only the reader thread can write tail without synchronization, so
|
||||
// memory_order_relaxed is sufficient
|
||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_relaxed);
|
||||
|
||||
// The head cursor is updated after the data is written to the array
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t right_count = buf->alloc_size - tail;
|
||||
if (right_count > samples_count) {
|
||||
right_count = samples_count;
|
||||
}
|
||||
memcpy(to,
|
||||
buf->data + (tail * buf->sample_size),
|
||||
right_count * buf->sample_size);
|
||||
if (to) {
|
||||
uint32_t right_count = buf->alloc_size - tail;
|
||||
if (right_count > samples_count) {
|
||||
right_count = samples_count;
|
||||
}
|
||||
memcpy(to,
|
||||
buf->data + (tail * buf->sample_size),
|
||||
right_count * buf->sample_size);
|
||||
|
||||
if (samples_count > right_count) {
|
||||
uint32_t left_count = samples_count - right_count;
|
||||
memcpy(to + (right_count * buf->sample_size),
|
||||
buf->data,
|
||||
left_count * buf->sample_size);
|
||||
if (samples_count > right_count) {
|
||||
uint32_t left_count = samples_count - right_count;
|
||||
memcpy(to + (right_count * buf->sample_size),
|
||||
buf->data,
|
||||
left_count * buf->sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t new_tail = (tail + samples_count) % buf->alloc_size;
|
||||
@ -87,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;
|
||||
}
|
||||
@ -114,29 +110,3 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_,
|
||||
|
||||
return samples_count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sc_audiobuf_truncate(struct sc_audiobuf *buf, uint32_t samples_limit) {
|
||||
for (;;) {
|
||||
// Only the writer thread can write head, so memory_order_relaxed is
|
||||
// sufficient
|
||||
uint32_t head = atomic_load_explicit(&buf->head, memory_order_relaxed);
|
||||
|
||||
// The tail cursor is updated after the data is consumed by the reader
|
||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
||||
|
||||
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
|
||||
if (can_read <= samples_limit) {
|
||||
// Nothing to truncate
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t skip = can_read - samples_limit;
|
||||
uint32_t new_tail = (tail + skip) % buf->alloc_size;
|
||||
if (atomic_compare_exchange_weak_explicit(&buf->tail, &tail, new_tail,
|
||||
memory_order_acq_rel,
|
||||
memory_order_acquire)) {
|
||||
return skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,16 +49,6 @@ uint32_t
|
||||
sc_audiobuf_write(struct sc_audiobuf *buf, const void *from,
|
||||
uint32_t samples_count);
|
||||
|
||||
/**
|
||||
* Drop old samples to keep at most sample_limit samples
|
||||
*
|
||||
* Must be called by the writer thread.
|
||||
*
|
||||
* \return the number of samples dropped
|
||||
*/
|
||||
uint32_t
|
||||
sc_audiobuf_truncate(struct sc_audiobuf *buf, uint32_t samples_limit);
|
||||
|
||||
static inline uint32_t
|
||||
sc_audiobuf_capacity(struct sc_audiobuf *buf) {
|
||||
assert(buf->alloc_size);
|
||||
|
@ -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