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.
This commit is contained in:
Romain Vimont 2021-02-23 19:59:43 +01:00
parent fb9f9848bd
commit cb9c42bdcb
6 changed files with 24 additions and 29 deletions

View file

@ -11,22 +11,9 @@
#include "util/buffer_util.h" #include "util/buffer_util.h"
#include "util/log.h" #include "util/log.h"
// set the decoded frame as ready for rendering, and notify
static void
push_frame(struct decoder *decoder) {
bool previous_frame_skipped;
video_buffer_producer_offer_frame(decoder->video_buffer,
&previous_frame_skipped);
if (previous_frame_skipped) {
fps_counter_add_skipped_frame(decoder->fps_counter);
}
}
void void
decoder_init(struct decoder *decoder, struct video_buffer *vb, decoder_init(struct decoder *decoder, struct video_buffer *vb) {
struct fps_counter *fps_counter) {
decoder->video_buffer = vb; decoder->video_buffer = vb;
decoder->fps_counter = fps_counter;
} }
bool bool
@ -66,7 +53,7 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
decoder->video_buffer->producer_frame); decoder->video_buffer->producer_frame);
if (!ret) { if (!ret) {
// a frame was received // a frame was received
push_frame(decoder); video_buffer_producer_offer_frame(decoder->video_buffer);
} else if (ret != AVERROR(EAGAIN)) { } else if (ret != AVERROR(EAGAIN)) {
LOGE("Could not receive video frame: %d", ret); LOGE("Could not receive video frame: %d", ret);
return false; return false;

View file

@ -10,14 +10,12 @@ struct video_buffer;
struct decoder { struct decoder {
struct video_buffer *video_buffer; struct video_buffer *video_buffer;
struct fps_counter *fps_counter;
AVCodecContext *codec_ctx; AVCodecContext *codec_ctx;
}; };
void void
decoder_init(struct decoder *decoder, struct video_buffer *vb, decoder_init(struct decoder *decoder, struct video_buffer *vb);
struct fps_counter *fps_counter);
bool bool
decoder_open(struct decoder *decoder, const AVCodec *codec); decoder_open(struct decoder *decoder, const AVCodec *codec);

View file

@ -346,7 +346,7 @@ scrcpy(const struct scrcpy_options *options) {
file_handler_initialized = true; file_handler_initialized = true;
} }
decoder_init(&decoder, &video_buffer, &fps_counter); decoder_init(&decoder, &video_buffer);
dec = &decoder; dec = &decoder;
} }

View file

@ -204,6 +204,14 @@ on_frame_available(struct video_buffer *vb, void *userdata) {
SDL_PushEvent(&new_frame_event); 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);
}
void void
screen_init(struct screen *screen, struct video_buffer *vb, screen_init(struct screen *screen, struct video_buffer *vb,
struct fps_counter *fps_counter) { struct fps_counter *fps_counter) {
@ -213,9 +221,10 @@ screen_init(struct screen *screen, struct video_buffer *vb,
static const struct video_buffer_callbacks cbs = { static const struct video_buffer_callbacks cbs = {
.on_frame_available = on_frame_available, .on_frame_available = on_frame_available,
.on_frame_skipped = on_frame_skipped,
}; };
video_buffer_set_consumer_callbacks(vb, &cbs, NULL); video_buffer_set_consumer_callbacks(vb, &cbs, screen);
} }
static inline SDL_Texture * static inline SDL_Texture *

View file

@ -98,8 +98,7 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb,
} }
void void
video_buffer_producer_offer_frame(struct video_buffer *vb, video_buffer_producer_offer_frame(struct video_buffer *vb) {
bool *previous_frame_skipped) {
assert(vb->cbs); assert(vb->cbs);
sc_mutex_lock(&vb->mutex); sc_mutex_lock(&vb->mutex);
@ -113,14 +112,14 @@ video_buffer_producer_offer_frame(struct video_buffer *vb,
video_buffer_swap_producer_frame(vb); video_buffer_swap_producer_frame(vb);
bool skipped = !vb->pending_frame_consumed; bool skipped = !vb->pending_frame_consumed;
*previous_frame_skipped = skipped;
vb->pending_frame_consumed = false; vb->pending_frame_consumed = false;
sc_mutex_unlock(&vb->mutex); sc_mutex_unlock(&vb->mutex);
if (!skipped) { if (skipped) {
// If skipped, then the previous call will consume this frame, the if (vb->cbs->on_frame_skipped)
// callback must not be called vb->cbs->on_frame_skipped(vb, vb->cbs_userdata);
} else {
vb->cbs->on_frame_available(vb, vb->cbs_userdata); vb->cbs->on_frame_available(vb, vb->cbs_userdata);
} }
} }

View file

@ -49,6 +49,10 @@ struct video_buffer_callbacks {
// video_buffer_consumer_take_frame(vb) // video_buffer_consumer_take_frame(vb)
// This callback is mandatory (it must not be NULL). // This callback is mandatory (it must not be NULL).
void (*on_frame_available)(struct video_buffer *vb, void *userdata); 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 bool
@ -63,10 +67,8 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb,
void *cbs_userdata); void *cbs_userdata);
// set the producer frame as ready for consuming // set the producer frame as ready for consuming
// the output flag is set to report whether the previous frame has been skipped
void void
video_buffer_producer_offer_frame(struct video_buffer *vb, video_buffer_producer_offer_frame(struct video_buffer *vb);
bool *previous_frame_skipped);
// mark the consumer frame as consumed and return it // mark the consumer frame as consumed and return it
// the frame is valid until the next call to this function // the frame is valid until the next call to this function