Compare commits

..

17 Commits

Author SHA1 Message Date
Romain Vimont
205d9808fa wip_everything_broken 2021-02-23 22:20:52 +01:00
Romain Vimont
f13febe8b7 resizer_callbacks 2021-02-23 22:04:20 +01:00
Romain Vimont
86d152ea86 enable async resizing 2021-02-23 20:19:59 +01:00
Romain Vimont
c6ccb6a9a9 swscale-wip 2021-02-23 20:19:59 +01:00
Romain Vimont
a2d1709bfc wip resizer 2021-02-23 20:19:53 +01:00
Romain Vimont
eca374c423 scale_filter 2021-02-23 20:12:19 +01:00
Romain Vimont
1e33d9b306 Move decoder frame to decoder
The video buffer held 3 frames:
 - the producer frame (for decoding)
 - the pending frame (to exchange between the producer and consumer)
 - the consumer frame (for rendering)

It worked well, but it prevented video buffers to be chained, because
the consumer frame of the first video buffer must be the same as the
producer frame of the second video buffer.

To solve this problem, make the decoder handle its decoding frame, and
keep only the pending and consumer frames in the video_buffer.

    decoder -> pending -> consumer -> pending -> consumer
             |---------------------||---------------------|
                  video_buffer 1         video_buffer 2

This paves the way to support asynchronous swscale.
2021-02-23 20:06:31 +01:00
Romain Vimont
2f5546d33a Release frame data as soon as possible
During a frame swap, one of the two frames involved can be released.
2021-02-23 20:03:12 +01:00
Romain Vimont
d8ddf74865 Factorize frame swap 2021-02-23 20:03:12 +01:00
Romain Vimont
2f4d312142 Remove screen static initializer
Most of the fields are initialized dynamically.
2021-02-23 20:03:12 +01:00
Romain Vimont
62c5e73a3d Use a callback to notify frame skip
A skipped frame is detected when the producer offers a frame while the
current pending frame has not been consumed.

However, the producer (in practice the decoder) is not interested in the
fact that a frame has been skipped, only the consumer (the renderer) is.

Therefore, notify frame skip via a consumer callback. This allows to
manage the skipped and rendered frames count at the same place, and
remove fps_counter from decoder.
2021-02-23 20:03:11 +01:00
Romain Vimont
f82e92d65e Use a callback to notify a new frame
Make the decoder independant of the SDL even mechanism, by making the
consumer register a callback on the video_buffer.
2021-02-23 15:22:25 +01:00
Romain Vimont
4c0554d225 Initialize screen before starting the stream
As soon as the stream is started, the video buffer could notify a new
frame available.

In order to pass this event to the screen without race condition, the
screen must be initialized before the screen is started.
2021-02-23 15:19:50 +01:00
Romain Vimont
51b1b0f603 Make video buffer more generic
Video buffer is a tool between a frame producer and a frame consumer.

For now, it is used between a decoder and a renderer, but in the future
another instance might be used to swscale decoded frames.
2021-02-23 14:56:56 +01:00
Romain Vimont
9f18c82863 Move fps counter out of video buffer
In order to make video buffer more generic, move out its specific
responsibility to count the fps between the decoder and the renderer.
2021-02-23 14:56:53 +01:00
Romain Vimont
2630512620 Simplify default values
It makes sense to extract default values for bitrate and port range
(which are arbitrary and might be changed in the future).

However, the default values for "max size" and "lock video orientation"
are naturally unlimited/unlocked, and will never be changed. Extracting
these options just added complexity for no benefit, so hardcode them.
2021-02-23 09:37:16 +01:00
Romain Vimont
b0b0240ce0 Insert numerical values statically in usage string 2021-02-22 22:03:50 +01:00
14 changed files with 177 additions and 140 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -10,6 +10,7 @@ struct video_buffer;
struct decoder {
struct video_buffer *video_buffer;
AVCodecContext *codec_ctx;
AVFrame *frame;
};

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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, \

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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