mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-03-17 09:34:36 +01:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
205d9808fa | ||
|
|
f13febe8b7 | ||
|
|
86d152ea86 | ||
|
|
c6ccb6a9a9 | ||
|
|
a2d1709bfc | ||
|
|
eca374c423 | ||
|
|
1e33d9b306 | ||
|
|
2f5546d33a | ||
|
|
d8ddf74865 | ||
|
|
2f4d312142 | ||
|
|
62c5e73a3d | ||
|
|
f82e92d65e | ||
|
|
4c0554d225 | ||
|
|
51b1b0f603 | ||
|
|
9f18c82863 | ||
|
|
2630512620 | ||
|
|
b0b0240ce0 |
@@ -116,16 +116,6 @@ conf.set('PORTABLE', get_option('portable'))
|
||||
conf.set('DEFAULT_LOCAL_PORT_RANGE_FIRST', '27183')
|
||||
conf.set('DEFAULT_LOCAL_PORT_RANGE_LAST', '27199')
|
||||
|
||||
# the default max video size for both dimensions, in pixels
|
||||
# overridden by option --max-size
|
||||
conf.set('DEFAULT_MAX_SIZE', '0') # 0: unlimited
|
||||
|
||||
# the default video orientation
|
||||
# natural device orientation is 0 and each increment adds 90 degrees
|
||||
# counterclockwise
|
||||
# overridden by option --lock-video-orientation
|
||||
conf.set('DEFAULT_LOCK_VIDEO_ORIENTATION', '-1') # -1: unlocked
|
||||
|
||||
# the default video bitrate, in bits/second
|
||||
# overridden by option --bit-rate
|
||||
conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "util/log.h"
|
||||
#include "util/str_util.h"
|
||||
|
||||
#define STR_IMPL_(x) #x
|
||||
#define STR(x) STR_IMPL_(x)
|
||||
|
||||
void
|
||||
scrcpy_print_usage(const char *arg0) {
|
||||
fprintf(stderr,
|
||||
@@ -23,7 +26,7 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" -b, --bit-rate value\n"
|
||||
" Encode the video at the given bit-rate, expressed in bits/s.\n"
|
||||
" Unit suffixes are supported: 'K' (x1000) and 'M' (x1000000).\n"
|
||||
" Default is %d.\n"
|
||||
" Default is " STR(DEFAULT_BIT_RATE) ".\n"
|
||||
"\n"
|
||||
" --codec-options key[:type]=value[,...]\n"
|
||||
" Set a list of comma-separated key:type=value options for the\n"
|
||||
@@ -81,7 +84,7 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" Possible values are -1 (unlocked), 0, 1, 2 and 3.\n"
|
||||
" Natural device orientation is 0, and each increment adds a\n"
|
||||
" 90 degrees rotation counterclockwise.\n"
|
||||
" Default is %d%s.\n"
|
||||
" Default is -1 (unlocked).\n"
|
||||
"\n"
|
||||
" --max-fps value\n"
|
||||
" Limit the frame rate of screen capture (officially supported\n"
|
||||
@@ -91,7 +94,7 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" Limit both the width and height of the video to value. The\n"
|
||||
" other dimension is computed so that the device aspect-ratio\n"
|
||||
" is preserved.\n"
|
||||
" Default is %d%s.\n"
|
||||
" Default is 0 (unlimited).\n"
|
||||
"\n"
|
||||
" -n, --no-control\n"
|
||||
" Disable device control (mirror the device in read-only).\n"
|
||||
@@ -105,7 +108,8 @@ scrcpy_print_usage(const char *arg0) {
|
||||
"\n"
|
||||
" -p, --port port[:port]\n"
|
||||
" Set the TCP port (range) used by the client to listen.\n"
|
||||
" Default is %d:%d.\n"
|
||||
" Default is " STR(DEFAULT_LOCAL_PORT_RANGE_FIRST) ":"
|
||||
STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".\n"
|
||||
"\n"
|
||||
" --prefer-text\n"
|
||||
" Inject alpha characters and space as text events instead of\n"
|
||||
@@ -297,12 +301,7 @@ scrcpy_print_usage(const char *arg0) {
|
||||
"\n"
|
||||
" Drag & drop APK file\n"
|
||||
" Install APK from computer\n"
|
||||
"\n",
|
||||
arg0,
|
||||
DEFAULT_BIT_RATE,
|
||||
DEFAULT_LOCK_VIDEO_ORIENTATION, DEFAULT_LOCK_VIDEO_ORIENTATION >= 0 ? "" : " (unlocked)",
|
||||
DEFAULT_MAX_SIZE, DEFAULT_MAX_SIZE ? "" : " (unlimited)",
|
||||
DEFAULT_LOCAL_PORT_RANGE_FIRST, DEFAULT_LOCAL_PORT_RANGE_LAST);
|
||||
"\n", arg0);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
#include "util/buffer_util.h"
|
||||
#include "util/log.h"
|
||||
|
||||
// set the decoded frame as ready for rendering, and notify
|
||||
static void
|
||||
push_frame(struct decoder *decoder) {
|
||||
video_buffer_producer_offer_frame(decoder->video_buffer, &decoder->frame);
|
||||
}
|
||||
|
||||
void
|
||||
decoder_init(struct decoder *decoder, struct video_buffer *vb) {
|
||||
decoder->video_buffer = vb;
|
||||
@@ -66,7 +60,8 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
|
||||
ret = avcodec_receive_frame(decoder->codec_ctx, decoder->frame);
|
||||
if (!ret) {
|
||||
// a frame was received
|
||||
push_frame(decoder);
|
||||
video_buffer_producer_offer_frame(decoder->video_buffer,
|
||||
&decoder->frame);
|
||||
} else if (ret != AVERROR(EAGAIN)) {
|
||||
LOGE("Could not receive video frame: %d", ret);
|
||||
return false;
|
||||
|
||||
@@ -10,6 +10,7 @@ struct video_buffer;
|
||||
|
||||
struct decoder {
|
||||
struct video_buffer *video_buffer;
|
||||
|
||||
AVCodecContext *codec_ctx;
|
||||
AVFrame *frame;
|
||||
};
|
||||
|
||||
@@ -168,7 +168,7 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_add_skipped_frames(struct fps_counter *counter, unsigned n) {
|
||||
fps_counter_add_skipped_frame(struct fps_counter *counter) {
|
||||
if (!is_started(counter)) {
|
||||
return;
|
||||
}
|
||||
@@ -176,6 +176,6 @@ fps_counter_add_skipped_frames(struct fps_counter *counter, unsigned n) {
|
||||
sc_mutex_lock(&counter->mutex);
|
||||
uint32_t now = SDL_GetTicks();
|
||||
check_interval_expired(counter, now);
|
||||
counter->nr_skipped += n;
|
||||
++counter->nr_skipped;
|
||||
sc_mutex_unlock(&counter->mutex);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,6 @@ void
|
||||
fps_counter_add_rendered_frame(struct fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_add_skipped_frames(struct fps_counter *counter, unsigned n);
|
||||
fps_counter_add_skipped_frame(struct fps_counter *counter);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
bool
|
||||
sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in,
|
||||
struct video_buffer *vb_out, enum sc_scale_filter scale_filter,
|
||||
struct size size) {
|
||||
enum sc_scale_filter scale_filter, struct size size) {
|
||||
bool ok = sc_mutex_init(&resizer->mutex);
|
||||
if (!ok) {
|
||||
return false;
|
||||
@@ -20,15 +19,22 @@ sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in,
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = video_buffer_init(&resizer->vb_out, false); // FIXME wait_consumer
|
||||
if (!ok) {
|
||||
sc_cond_destroy(&resizer->req_cond);
|
||||
sc_mutex_destroy(&resizer->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
resizer->resized_frame = av_frame_alloc();
|
||||
if (!resizer->resized_frame) {
|
||||
video_buffer_destroy(&resizer->vb_out);
|
||||
sc_cond_destroy(&resizer->req_cond);
|
||||
sc_mutex_destroy(&resizer->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
resizer->vb_in = vb_in;
|
||||
resizer->vb_out = vb_out;
|
||||
resizer->scale_filter = scale_filter;
|
||||
resizer->size = size;
|
||||
|
||||
@@ -111,11 +117,9 @@ run_resizer(void *data) {
|
||||
}
|
||||
|
||||
if (resizer->has_new_frame) {
|
||||
unsigned skipped;
|
||||
resizer->input_frame =
|
||||
video_buffer_consumer_take_frame(resizer->vb_in, &skipped);
|
||||
video_buffer_consumer_take_frame(resizer->vb_in);
|
||||
|
||||
(void) skipped; // FIXME forward skipped frames count
|
||||
resizer->has_new_frame = false;
|
||||
}
|
||||
|
||||
@@ -125,10 +129,16 @@ run_resizer(void *data) {
|
||||
// Do the actual work without mutex
|
||||
sc_resizer_swscale(resizer);
|
||||
|
||||
video_buffer_producer_offer_frame(resizer->vb_out,
|
||||
&resizer->resized_frame);
|
||||
|
||||
sc_mutex_lock(&resizer->mutex);
|
||||
|
||||
// Update the original size of the resized frame
|
||||
resizer->original_size.width = resizer->input_frame->width;
|
||||
resizer->original_size.height = resizer->input_frame->height;
|
||||
assert(resizer->original_size.width);
|
||||
assert(resizer->original_size.height);
|
||||
|
||||
video_buffer_producer_offer_frame(&resizer->vb_out,
|
||||
&resizer->resized_frame);
|
||||
}
|
||||
sc_mutex_unlock(&resizer->mutex);
|
||||
|
||||
@@ -156,7 +166,7 @@ sc_resizer_stop(struct sc_resizer *resizer) {
|
||||
sc_cond_signal(&resizer->req_cond);
|
||||
sc_mutex_unlock(&resizer->mutex);
|
||||
|
||||
video_buffer_interrupt(resizer->vb_out);
|
||||
video_buffer_interrupt(&resizer->vb_out);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -164,6 +174,20 @@ sc_resizer_join(struct sc_resizer *resizer) {
|
||||
sc_thread_join(&resizer->thread, NULL);
|
||||
}
|
||||
|
||||
const AVFrame *
|
||||
sc_resizer_consumer_take_frame(struct sc_resizer *resizer,
|
||||
struct size *out_original_size) {
|
||||
// Locking the mutex is necessary to ensure that the size corresponds to the correct frame
|
||||
sc_mutex_lock(&resizer->mutex);
|
||||
|
||||
const AVFrame *frame = video_buffer_consumer_take_frame(&resizer->vb_out);
|
||||
*out_original_size = resizer->original_size;
|
||||
|
||||
sc_mutex_unlock(&resizer->mutex);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void
|
||||
sc_resizer_process_new_frame(struct sc_resizer *resizer) {
|
||||
sc_mutex_lock(&resizer->mutex);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
struct sc_resizer {
|
||||
struct video_buffer *vb_in;
|
||||
struct video_buffer *vb_out;
|
||||
struct video_buffer vb_out;
|
||||
enum sc_scale_filter scale_filter;
|
||||
struct size size;
|
||||
|
||||
@@ -21,6 +21,9 @@ struct sc_resizer {
|
||||
const AVFrame *input_frame;
|
||||
AVFrame *resized_frame;
|
||||
|
||||
// original size of the available (resized) frame in vb_out
|
||||
struct size original_size;
|
||||
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond req_cond;
|
||||
@@ -32,8 +35,7 @@ struct sc_resizer {
|
||||
|
||||
bool
|
||||
sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in,
|
||||
struct video_buffer *vb_out, enum sc_scale_filter scale_filter,
|
||||
struct size size);
|
||||
enum sc_scale_filter scale_filter, struct size size);
|
||||
|
||||
void
|
||||
sc_resizer_destroy(struct sc_resizer *resizer);
|
||||
@@ -47,6 +49,10 @@ sc_resizer_stop(struct sc_resizer *resizer);
|
||||
void
|
||||
sc_resizer_join(struct sc_resizer *resizer);
|
||||
|
||||
const AVFrame *
|
||||
sc_resizer_consumer_take_frame(struct sc_resizer *resizer,
|
||||
struct size *out_original_size);
|
||||
|
||||
void
|
||||
sc_resizer_process_new_frame(struct sc_resizer *resizer);
|
||||
|
||||
|
||||
@@ -267,14 +267,6 @@ av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
|
||||
free(local_fmt);
|
||||
}
|
||||
|
||||
static void
|
||||
video_buffer_on_frame_available(struct video_buffer *vb, void *userdata) {
|
||||
(void) vb;
|
||||
(void) userdata;
|
||||
|
||||
screen_on_frame_available(&screen);
|
||||
}
|
||||
|
||||
bool
|
||||
scrcpy(const struct scrcpy_options *options) {
|
||||
if (!server_init(&server)) {
|
||||
@@ -341,12 +333,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
}
|
||||
fps_counter_initialized = true;
|
||||
|
||||
static const struct video_buffer_callbacks video_buffer_cbs = {
|
||||
.on_frame_available = video_buffer_on_frame_available,
|
||||
};
|
||||
|
||||
if (!video_buffer_init(&video_buffer, options->render_expired_frames,
|
||||
&video_buffer_cbs, NULL)) {
|
||||
if (!video_buffer_init(&video_buffer, options->render_expired_frames)) {
|
||||
goto end;
|
||||
}
|
||||
video_buffer_initialized = true;
|
||||
|
||||
@@ -120,10 +120,10 @@ struct scrcpy_options {
|
||||
.count = 2, \
|
||||
}, \
|
||||
.scale_filter = SC_SCALE_FILTER_TRILINEAR, \
|
||||
.max_size = DEFAULT_MAX_SIZE, \
|
||||
.max_size = 0, \
|
||||
.bit_rate = DEFAULT_BIT_RATE, \
|
||||
.max_fps = 0, \
|
||||
.lock_video_orientation = DEFAULT_LOCK_VIDEO_ORIENTATION, \
|
||||
.lock_video_orientation = -1, \
|
||||
.rotation = 0, \
|
||||
.window_x = SC_WINDOW_POSITION_UNDEFINED, \
|
||||
.window_y = SC_WINDOW_POSITION_UNDEFINED, \
|
||||
|
||||
128
app/src/screen.c
128
app/src/screen.c
@@ -190,6 +190,51 @@ screen_update_content_rect(struct screen *screen) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_frame_available(struct video_buffer *vb, void *userdata) {
|
||||
(void) vb;
|
||||
|
||||
struct screen *screen = userdata;
|
||||
|
||||
if (screen->use_swscale) {
|
||||
sc_resizer_process_new_frame(&screen->resizer);
|
||||
} else {
|
||||
static SDL_Event new_frame_event = {
|
||||
.type = EVENT_NEW_FRAME,
|
||||
};
|
||||
|
||||
// Post the event on the UI thread
|
||||
SDL_PushEvent(&new_frame_event);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_frame_skipped(struct video_buffer *vb, void *userdata) {
|
||||
(void) vb;
|
||||
|
||||
struct screen *screen = userdata;
|
||||
fps_counter_add_skipped_frame(screen->fps_counter);
|
||||
}
|
||||
|
||||
static void
|
||||
resizer_on_frame_available(struct video_buffer *vb, void *userdata) {
|
||||
(void) vb;
|
||||
(void) userdata;
|
||||
|
||||
static SDL_Event new_frame_event = {
|
||||
.type = EVENT_NEW_FRAME,
|
||||
};
|
||||
|
||||
// Post the event on the UI thread
|
||||
SDL_PushEvent(&new_frame_event);
|
||||
}
|
||||
|
||||
static void
|
||||
resizer_on_frame_skipped(struct video_buffer *vb, void *userdata) {
|
||||
// Count skipped frames from decoder or resizer the same way
|
||||
on_frame_skipped(vb, userdata);
|
||||
}
|
||||
|
||||
void
|
||||
screen_init(struct screen *screen, struct video_buffer *vb,
|
||||
struct fps_counter *fps_counter) {
|
||||
@@ -200,6 +245,13 @@ screen_init(struct screen *screen, struct video_buffer *vb,
|
||||
screen->has_frame = false;
|
||||
screen->fullscreen = false;
|
||||
screen->maximized = false;
|
||||
|
||||
static const struct video_buffer_callbacks cbs = {
|
||||
.on_frame_available = on_frame_available,
|
||||
.on_frame_skipped = on_frame_skipped,
|
||||
};
|
||||
|
||||
video_buffer_set_consumer_callbacks(vb, &cbs, screen);
|
||||
}
|
||||
|
||||
static inline SDL_Texture *
|
||||
@@ -238,19 +290,6 @@ is_swscale(enum sc_scale_filter scale_filter) {
|
||||
&& scale_filter != SC_SCALE_FILTER_TRILINEAR;
|
||||
}
|
||||
|
||||
static void
|
||||
on_resizer_frame_available(struct video_buffer *vb, void *userdata) {
|
||||
(void) vb;
|
||||
(void) userdata;
|
||||
|
||||
static SDL_Event new_frame_event = {
|
||||
.type = EVENT_NEW_FRAME,
|
||||
};
|
||||
|
||||
// Post the event on the UI thread
|
||||
SDL_PushEvent(&new_frame_event);
|
||||
}
|
||||
|
||||
bool
|
||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
struct size frame_size, bool always_on_top,
|
||||
@@ -337,11 +376,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
|
||||
screen->use_swscale = is_swscale(scale_filter);
|
||||
if (screen->use_swscale) {
|
||||
static const struct video_buffer_callbacks video_buffer_cbs = {
|
||||
.on_frame_available = on_resizer_frame_available,
|
||||
};
|
||||
bool ok = video_buffer_init(&screen->resizer_vb, false,
|
||||
&video_buffer_cbs, NULL);
|
||||
bool ok = video_buffer_init(&screen->resizer_vb, false);
|
||||
if (!ok) {
|
||||
LOGE("Could not create resizer video buffer");
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
@@ -349,8 +384,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = sc_resizer_init(&screen->resizer, screen->vb, &screen->resizer_vb,
|
||||
scale_filter, window_size);
|
||||
ok = sc_resizer_init(&screen->resizer, screen->vb, scale_filter,
|
||||
window_size);
|
||||
if (!ok) {
|
||||
LOGE("Could not create resizer");
|
||||
video_buffer_destroy(&screen->resizer_vb);
|
||||
@@ -359,6 +394,14 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct video_buffer_callbacks cbs = {
|
||||
.on_frame_available = resizer_on_frame_available,
|
||||
.on_frame_skipped = resizer_on_frame_skipped,
|
||||
};
|
||||
|
||||
video_buffer_set_consumer_callbacks(&screen->resizer.vb_out, &cbs,
|
||||
screen);
|
||||
|
||||
ok = sc_resizer_start(&screen->resizer);
|
||||
if (!ok) {
|
||||
LOGE("Could not start resizer");
|
||||
@@ -477,16 +520,17 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
||||
|
||||
// recreate the texture and resize the window if the frame size has changed
|
||||
static 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) {
|
||||
prepare_for_frame(struct screen *screen, struct size original_frame_size,
|
||||
struct size frame_size) {
|
||||
if (screen->frame_size.width != original_frame_size.width
|
||||
|| screen->frame_size.height != original_frame_size.height) {
|
||||
// frame dimension changed, destroy texture
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
|
||||
screen->frame_size = new_frame_size;
|
||||
screen->frame_size = original_frame_size;
|
||||
|
||||
struct size new_content_size =
|
||||
get_rotated_size(new_frame_size, screen->rotation);
|
||||
get_rotated_size(original_frame_size, screen->rotation);
|
||||
set_content_size(screen, new_content_size);
|
||||
|
||||
screen_update_content_rect(screen);
|
||||
@@ -525,16 +569,22 @@ update_texture(struct screen *screen, const AVFrame *frame) {
|
||||
|
||||
static bool
|
||||
screen_update_frame(struct screen *screen) {
|
||||
unsigned skipped;
|
||||
struct video_buffer *vb = screen->use_swscale ? &screen->resizer_vb
|
||||
: screen->vb;
|
||||
const AVFrame *frame = video_buffer_consumer_take_frame(vb, &skipped);
|
||||
const AVFrame *frame;
|
||||
struct size original_frame_size;
|
||||
|
||||
if (screen->use_swscale) {
|
||||
frame = sc_resizer_consumer_take_frame(&screen->resizer,
|
||||
&original_frame_size);
|
||||
} else {
|
||||
frame = video_buffer_consumer_take_frame(screen->vb);
|
||||
original_frame_size.width = frame->width;
|
||||
original_frame_size.height = frame->height;
|
||||
}
|
||||
struct size frame_size = {frame->width, frame->height};
|
||||
|
||||
fps_counter_add_skipped_frames(screen->fps_counter, skipped);
|
||||
fps_counter_add_rendered_frame(screen->fps_counter);
|
||||
|
||||
struct size new_frame_size = {frame->width, frame->height};
|
||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
||||
if (!prepare_for_frame(screen, original_frame_size, frame_size)) {
|
||||
return false;
|
||||
}
|
||||
update_texture(screen, frame);
|
||||
@@ -727,17 +777,3 @@ screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y) {
|
||||
*x = (int64_t) *x * dw / ww;
|
||||
*y = (int64_t) *y * dh / wh;
|
||||
}
|
||||
|
||||
void
|
||||
screen_on_frame_available(struct screen *screen) {
|
||||
if (screen->use_swscale) {
|
||||
sc_resizer_process_new_frame(&screen->resizer);
|
||||
} else {
|
||||
static SDL_Event new_frame_event = {
|
||||
.type = EVENT_NEW_FRAME,
|
||||
};
|
||||
|
||||
// Post the event on the UI thread
|
||||
SDL_PushEvent(&new_frame_event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +116,4 @@ screen_convert_drawable_to_frame_coords(struct screen *screen,
|
||||
void
|
||||
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y);
|
||||
|
||||
// Notify the screen that a new frame is available in the video_buffer.
|
||||
// Called from a separate thread.
|
||||
void
|
||||
screen_on_frame_available(struct screen *screen);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
#include "util/log.h"
|
||||
|
||||
bool
|
||||
video_buffer_init(struct video_buffer *vb, bool wait_consumer,
|
||||
const struct video_buffer_callbacks *cbs,
|
||||
void *cbs_userdata) {
|
||||
video_buffer_init(struct video_buffer *vb, bool wait_consumer) {
|
||||
vb->pending_frame = av_frame_alloc();
|
||||
if (!vb->pending_frame) {
|
||||
goto error_0;
|
||||
@@ -40,12 +38,9 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer,
|
||||
// there is initially no frame, so consider it has already been consumed
|
||||
vb->pending_frame_consumed = true;
|
||||
|
||||
vb->skipped = 0;
|
||||
|
||||
assert(cbs);
|
||||
assert(cbs->on_frame_available);
|
||||
vb->cbs = cbs;
|
||||
vb->cbs_userdata = cbs_userdata;
|
||||
// The callbacks must be set by the consumer via
|
||||
// video_buffer_set_consumer_callbacks()
|
||||
vb->cbs = NULL;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -74,8 +69,21 @@ swap_frames(AVFrame **lhs, AVFrame **rhs) {
|
||||
*rhs = tmp;
|
||||
}
|
||||
|
||||
void
|
||||
video_buffer_set_consumer_callbacks(struct video_buffer *vb,
|
||||
const struct video_buffer_callbacks *cbs,
|
||||
void *cbs_userdata) {
|
||||
assert(!vb->cbs); // must be set only once
|
||||
assert(cbs);
|
||||
assert(cbs->on_frame_available);
|
||||
vb->cbs = cbs;
|
||||
vb->cbs_userdata = cbs_userdata;
|
||||
}
|
||||
|
||||
void
|
||||
video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) {
|
||||
assert(vb->cbs);
|
||||
|
||||
sc_mutex_lock(&vb->mutex);
|
||||
if (vb->wait_consumer) {
|
||||
// wait for the current (expired) frame to be consumed
|
||||
@@ -88,23 +96,20 @@ video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) {
|
||||
swap_frames(pframe, &vb->pending_frame);
|
||||
|
||||
bool skipped = !vb->pending_frame_consumed;
|
||||
if (skipped) {
|
||||
++vb->skipped;
|
||||
}
|
||||
|
||||
vb->pending_frame_consumed = false;
|
||||
|
||||
sc_mutex_unlock(&vb->mutex);
|
||||
|
||||
if (!skipped) {
|
||||
// If skipped, then the previous call will consume this frame, the
|
||||
// callback must not be called
|
||||
if (skipped) {
|
||||
if (vb->cbs->on_frame_skipped)
|
||||
vb->cbs->on_frame_skipped(vb, vb->cbs_userdata);
|
||||
} else {
|
||||
vb->cbs->on_frame_available(vb, vb->cbs_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
const AVFrame *
|
||||
video_buffer_consumer_take_frame(struct video_buffer *vb, unsigned *skipped) {
|
||||
video_buffer_consumer_take_frame(struct video_buffer *vb) {
|
||||
sc_mutex_lock(&vb->mutex);
|
||||
assert(!vb->pending_frame_consumed);
|
||||
vb->pending_frame_consumed = true;
|
||||
@@ -116,12 +121,6 @@ video_buffer_consumer_take_frame(struct video_buffer *vb, unsigned *skipped) {
|
||||
// unblock video_buffer_offer_decoded_frame()
|
||||
sc_cond_signal(&vb->pending_frame_consumed_cond);
|
||||
}
|
||||
|
||||
if (skipped) {
|
||||
*skipped = vb->skipped;
|
||||
}
|
||||
vb->skipped = 0; // reset
|
||||
|
||||
sc_mutex_unlock(&vb->mutex);
|
||||
|
||||
// consumer_frame is only written from this thread, no need to lock
|
||||
|
||||
@@ -39,8 +39,6 @@ struct video_buffer {
|
||||
sc_cond pending_frame_consumed_cond;
|
||||
bool pending_frame_consumed;
|
||||
|
||||
unsigned skipped;
|
||||
|
||||
const struct video_buffer_callbacks *cbs;
|
||||
void *cbs_userdata;
|
||||
};
|
||||
@@ -48,16 +46,25 @@ struct video_buffer {
|
||||
struct video_buffer_callbacks {
|
||||
// Called when a new frame can be consumed by
|
||||
// video_buffer_consumer_take_frame(vb)
|
||||
// This callback is mandatory (it must not be NULL).
|
||||
void (*on_frame_available)(struct video_buffer *vb, void *userdata);
|
||||
|
||||
// Called when a pending frame has been overwritten by the producer
|
||||
// This callback is optional (it may be NULL).
|
||||
void (*on_frame_skipped)(struct video_buffer *vb, void *userdata);
|
||||
};
|
||||
|
||||
bool
|
||||
video_buffer_init(struct video_buffer *vb, bool wait_consumer,
|
||||
const struct video_buffer_callbacks *cbs, void *cbs_userdata);
|
||||
video_buffer_init(struct video_buffer *vb, bool wait_consumer);
|
||||
|
||||
void
|
||||
video_buffer_destroy(struct video_buffer *vb);
|
||||
|
||||
void
|
||||
video_buffer_set_consumer_callbacks(struct video_buffer *vb,
|
||||
const struct video_buffer_callbacks *cbs,
|
||||
void *cbs_userdata);
|
||||
|
||||
// set the producer frame as ready for consuming
|
||||
// the produced frame is exchanged with an unused allocated frame
|
||||
void
|
||||
@@ -65,10 +72,8 @@ video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe);
|
||||
|
||||
// mark the consumer frame as consumed and return it
|
||||
// the frame is valid until the next call to this function
|
||||
// the output parameter "skipped" indicates how many produced frames have been
|
||||
// skipped
|
||||
const AVFrame *
|
||||
video_buffer_consumer_take_frame(struct video_buffer *vb, unsigned *skipped);
|
||||
video_buffer_consumer_take_frame(struct video_buffer *vb);
|
||||
|
||||
// wake up and avoid any blocking call
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user