Compare commits

...

4 Commits

Author SHA1 Message Date
86c4cbcf9e Extract current video_buffer to frame_buffer
The current video buffer only stores one pending frame.

In order to add a new buffering feature, move this part to a separate
"frame buffer". Keep the video_buffer, which currently delegates all its
calls to the frame_buffer.
2021-06-27 20:29:07 +02:00
7917dc313e Rename video_buffer to sc_video_buffer
Add a scrcpy-specific prefix.
2021-06-26 16:05:56 +02:00
d743e03f44 Move include fps_counter
The fps_counter is not used from video_buffer.
2021-06-26 16:04:52 +02:00
9217cfa079 Remove obsolete comment
Commit 2a94a2b119 removed video_buffer
callbacks, the comment is now meaningless.
2021-06-26 16:04:52 +02:00
9 changed files with 164 additions and 110 deletions

View File

@ -10,6 +10,7 @@ src = [
'src/event_converter.c', 'src/event_converter.c',
'src/file_handler.c', 'src/file_handler.c',
'src/fps_counter.c', 'src/fps_counter.c',
'src/frame_buffer.c',
'src/input_manager.c', 'src/input_manager.c',
'src/opengl.c', 'src/opengl.c',
'src/receiver.c', 'src/receiver.c',

88
app/src/frame_buffer.c Normal file
View File

@ -0,0 +1,88 @@
#include "frame_buffer.h"
#include <assert.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include "util/log.h"
bool
sc_frame_buffer_init(struct sc_frame_buffer *fb) {
fb->pending_frame = av_frame_alloc();
if (!fb->pending_frame) {
return false;
}
fb->tmp_frame = av_frame_alloc();
if (!fb->tmp_frame) {
av_frame_free(&fb->pending_frame);
return false;
}
bool ok = sc_mutex_init(&fb->mutex);
if (!ok) {
av_frame_free(&fb->pending_frame);
av_frame_free(&fb->tmp_frame);
return false;
}
// there is initially no frame, so consider it has already been consumed
fb->pending_frame_consumed = true;
return true;
}
void
sc_frame_buffer_destroy(struct sc_frame_buffer *fb) {
sc_mutex_destroy(&fb->mutex);
av_frame_free(&fb->pending_frame);
av_frame_free(&fb->tmp_frame);
}
static inline void
swap_frames(AVFrame **lhs, AVFrame **rhs) {
AVFrame *tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
bool
sc_frame_buffer_push(struct sc_frame_buffer *fb, const AVFrame *frame,
bool *previous_frame_skipped) {
sc_mutex_lock(&fb->mutex);
// Use a temporary frame to preserve pending_frame in case of error.
// tmp_frame is an empty frame, no need to call av_frame_unref() beforehand.
int r = av_frame_ref(fb->tmp_frame, frame);
if (r) {
LOGE("Could not ref frame: %d", r);
return false;
}
// Now that av_frame_ref() succeeded, we can replace the previous
// pending_frame
swap_frames(&fb->pending_frame, &fb->tmp_frame);
av_frame_unref(fb->tmp_frame);
if (previous_frame_skipped) {
*previous_frame_skipped = !fb->pending_frame_consumed;
}
fb->pending_frame_consumed = false;
sc_mutex_unlock(&fb->mutex);
return true;
}
void
sc_frame_buffer_consume(struct sc_frame_buffer *fb, AVFrame *dst) {
sc_mutex_lock(&fb->mutex);
assert(!fb->pending_frame_consumed);
fb->pending_frame_consumed = true;
av_frame_move_ref(dst, fb->pending_frame);
// av_frame_move_ref() resets its source frame, so no need to call
// av_frame_unref()
sc_mutex_unlock(&fb->mutex);
}

44
app/src/frame_buffer.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef SC_FRAME_BUFFER_H
#define SC_FRAME_BUFFER_H
#include "common.h"
#include <stdbool.h>
#include "util/thread.h"
// forward declarations
typedef struct AVFrame AVFrame;
/**
* A frame buffer holds 1 pending frame, which is the last frame received from
* the producer (typically, the decoder).
*
* If a pending frame has not been consumed when the producer pushes a new
* frame, then it is lost. The intent is to always provide access to the very
* last frame to minimize latency.
*/
struct sc_frame_buffer {
AVFrame *pending_frame;
AVFrame *tmp_frame; // To preserve the pending frame on error
sc_mutex mutex;
bool pending_frame_consumed;
};
bool
sc_frame_buffer_init(struct sc_frame_buffer *fb);
void
sc_frame_buffer_destroy(struct sc_frame_buffer *fb);
bool
sc_frame_buffer_push(struct sc_frame_buffer *fb, const AVFrame *frame,
bool *skipped);
void
sc_frame_buffer_consume(struct sc_frame_buffer *fb, AVFrame *dst);
#endif

View File

@ -276,7 +276,7 @@ screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
struct screen *screen = DOWNCAST(sink); struct screen *screen = DOWNCAST(sink);
bool previous_frame_skipped; bool previous_frame_skipped;
bool ok = video_buffer_push(&screen->vb, frame, &previous_frame_skipped); bool ok = sc_video_buffer_push(&screen->vb, frame, &previous_frame_skipped);
if (!ok) { if (!ok) {
return false; return false;
} }
@ -304,7 +304,7 @@ screen_init(struct screen *screen, const struct screen_params *params) {
screen->fullscreen = false; screen->fullscreen = false;
screen->maximized = false; screen->maximized = false;
bool ok = video_buffer_init(&screen->vb); bool ok = sc_video_buffer_init(&screen->vb);
if (!ok) { if (!ok) {
LOGE("Could not initialize video buffer"); LOGE("Could not initialize video buffer");
return false; return false;
@ -454,7 +454,7 @@ error_destroy_window:
error_destroy_fps_counter: error_destroy_fps_counter:
fps_counter_destroy(&screen->fps_counter); fps_counter_destroy(&screen->fps_counter);
error_destroy_video_buffer: error_destroy_video_buffer:
video_buffer_destroy(&screen->vb); sc_video_buffer_destroy(&screen->vb);
return false; return false;
} }
@ -489,7 +489,7 @@ screen_destroy(struct screen *screen) {
SDL_DestroyRenderer(screen->renderer); SDL_DestroyRenderer(screen->renderer);
SDL_DestroyWindow(screen->window); SDL_DestroyWindow(screen->window);
fps_counter_destroy(&screen->fps_counter); fps_counter_destroy(&screen->fps_counter);
video_buffer_destroy(&screen->vb); sc_video_buffer_destroy(&screen->vb);
} }
static void static void
@ -595,7 +595,7 @@ update_texture(struct screen *screen, const AVFrame *frame) {
static bool static bool
screen_update_frame(struct screen *screen) { screen_update_frame(struct screen *screen) {
av_frame_unref(screen->frame); av_frame_unref(screen->frame);
video_buffer_consume(&screen->vb, screen->frame); sc_video_buffer_consume(&screen->vb, screen->frame);
AVFrame *frame = screen->frame; AVFrame *frame = screen->frame;
fps_counter_add_rendered_frame(&screen->fps_counter); fps_counter_add_rendered_frame(&screen->fps_counter);

View File

@ -8,6 +8,7 @@
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "coords.h" #include "coords.h"
#include "fps_counter.h"
#include "opengl.h" #include "opengl.h"
#include "trait/frame_sink.h" #include "trait/frame_sink.h"
#include "video_buffer.h" #include "video_buffer.h"
@ -19,7 +20,7 @@ struct screen {
bool open; // track the open/close state to assert correct behavior bool open; // track the open/close state to assert correct behavior
#endif #endif
struct video_buffer vb; struct sc_video_buffer vb;
struct fps_counter fps_counter; struct fps_counter fps_counter;
SDL_Window *window; SDL_Window *window;

View File

@ -121,7 +121,7 @@ run_v4l2_sink(void *data) {
break; break;
} }
video_buffer_consume(&vs->vb, vs->frame); sc_video_buffer_consume(&vs->vb, vs->frame);
vs->has_frame = false; vs->has_frame = false;
sc_mutex_unlock(&vs->mutex); sc_mutex_unlock(&vs->mutex);
@ -141,7 +141,7 @@ run_v4l2_sink(void *data) {
static bool static bool
sc_v4l2_sink_open(struct sc_v4l2_sink *vs) { sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
bool ok = video_buffer_init(&vs->vb); bool ok = sc_video_buffer_init(&vs->vb);
if (!ok) { if (!ok) {
return false; return false;
} }
@ -275,7 +275,7 @@ error_cond_destroy:
error_mutex_destroy: error_mutex_destroy:
sc_mutex_destroy(&vs->mutex); sc_mutex_destroy(&vs->mutex);
error_video_buffer_destroy: error_video_buffer_destroy:
video_buffer_destroy(&vs->vb); sc_video_buffer_destroy(&vs->vb);
return false; return false;
} }
@ -297,14 +297,14 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
avformat_free_context(vs->format_ctx); avformat_free_context(vs->format_ctx);
sc_cond_destroy(&vs->cond); sc_cond_destroy(&vs->cond);
sc_mutex_destroy(&vs->mutex); sc_mutex_destroy(&vs->mutex);
video_buffer_destroy(&vs->vb); sc_video_buffer_destroy(&vs->vb);
} }
static bool static bool
sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) { sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) {
sc_mutex_lock(&vs->mutex); sc_mutex_lock(&vs->mutex);
bool ok = video_buffer_push(&vs->vb, frame, NULL); bool ok = sc_video_buffer_push(&vs->vb, frame, NULL);
if (!ok) { if (!ok) {
return false; return false;
} }

View File

@ -12,7 +12,7 @@
struct sc_v4l2_sink { struct sc_v4l2_sink {
struct sc_frame_sink frame_sink; // frame sink trait struct sc_frame_sink frame_sink; // frame sink trait
struct video_buffer vb; struct sc_video_buffer vb;
AVFormatContext *format_ctx; AVFormatContext *format_ctx;
AVCodecContext *encoder_ctx; AVCodecContext *encoder_ctx;

View File

@ -7,82 +7,22 @@
#include "util/log.h" #include "util/log.h"
bool bool
video_buffer_init(struct video_buffer *vb) { sc_video_buffer_init(struct sc_video_buffer *vb) {
vb->pending_frame = av_frame_alloc(); return sc_frame_buffer_init(&vb->fb);
if (!vb->pending_frame) {
return false;
}
vb->tmp_frame = av_frame_alloc();
if (!vb->tmp_frame) {
av_frame_free(&vb->pending_frame);
return false;
}
bool ok = sc_mutex_init(&vb->mutex);
if (!ok) {
av_frame_free(&vb->pending_frame);
av_frame_free(&vb->tmp_frame);
return false;
}
// there is initially no frame, so consider it has already been consumed
vb->pending_frame_consumed = true;
return true;
} }
void void
video_buffer_destroy(struct video_buffer *vb) { sc_video_buffer_destroy(struct sc_video_buffer *vb) {
sc_mutex_destroy(&vb->mutex); sc_frame_buffer_destroy(&vb->fb);
av_frame_free(&vb->pending_frame);
av_frame_free(&vb->tmp_frame);
}
static inline void
swap_frames(AVFrame **lhs, AVFrame **rhs) {
AVFrame *tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
} }
bool bool
video_buffer_push(struct video_buffer *vb, const AVFrame *frame, sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame,
bool *previous_frame_skipped) { bool *previous_frame_skipped) {
sc_mutex_lock(&vb->mutex); return sc_frame_buffer_push(&vb->fb, frame, previous_frame_skipped);
// Use a temporary frame to preserve pending_frame in case of error.
// tmp_frame is an empty frame, no need to call av_frame_unref() beforehand.
int r = av_frame_ref(vb->tmp_frame, frame);
if (r) {
LOGE("Could not ref frame: %d", r);
return false;
}
// Now that av_frame_ref() succeeded, we can replace the previous
// pending_frame
swap_frames(&vb->pending_frame, &vb->tmp_frame);
av_frame_unref(vb->tmp_frame);
if (previous_frame_skipped) {
*previous_frame_skipped = !vb->pending_frame_consumed;
}
vb->pending_frame_consumed = false;
sc_mutex_unlock(&vb->mutex);
return true;
} }
void void
video_buffer_consume(struct video_buffer *vb, AVFrame *dst) { sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) {
sc_mutex_lock(&vb->mutex); sc_frame_buffer_consume(&vb->fb, dst);
assert(!vb->pending_frame_consumed);
vb->pending_frame_consumed = true;
av_frame_move_ref(dst, vb->pending_frame);
// av_frame_move_ref() resets its source frame, so no need to call
// av_frame_unref()
sc_mutex_unlock(&vb->mutex);
} }

View File

@ -1,50 +1,30 @@
#ifndef VIDEO_BUFFER_H #ifndef SC_VIDEO_BUFFER_H
#define VIDEO_BUFFER_H #define SC_VIDEO_BUFFER_H
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include "fps_counter.h" #include "frame_buffer.h"
#include "util/thread.h"
// forward declarations // forward declarations
typedef struct AVFrame AVFrame; typedef struct AVFrame AVFrame;
/** struct sc_video_buffer {
* A video buffer holds 1 pending frame, which is the last frame received from struct sc_frame_buffer fb;
* the producer (typically, the decoder).
*
* If a pending frame has not been consumed when the producer pushes a new
* frame, then it is lost. The intent is to always provide access to the very
* last frame to minimize latency.
*
* The producer and the consumer typically do not live in the same thread.
* That's the reason why the callback on_frame_available() does not provide the
* frame as parameter: the consumer might post an event to its own thread to
* retrieve the pending frame from there, and that frame may have changed since
* the callback if producer pushed a new one in between.
*/
struct video_buffer {
AVFrame *pending_frame;
AVFrame *tmp_frame; // To preserve the pending frame on error
sc_mutex mutex;
bool pending_frame_consumed;
}; };
bool bool
video_buffer_init(struct video_buffer *vb); sc_video_buffer_init(struct sc_video_buffer *vb);
void void
video_buffer_destroy(struct video_buffer *vb); sc_video_buffer_destroy(struct sc_video_buffer *vb);
bool bool
video_buffer_push(struct video_buffer *vb, const AVFrame *frame, bool *skipped); sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame,
bool *skipped);
void void
video_buffer_consume(struct video_buffer *vb, AVFrame *dst); sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst);
#endif #endif