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.
This commit is contained in:
parent
336248df08
commit
4d8bcfc68a
5 changed files with 139 additions and 80 deletions
|
@ -10,6 +10,7 @@ src = [
|
|||
'src/event_converter.c',
|
||||
'src/file_handler.c',
|
||||
'src/fps_counter.c',
|
||||
'src/frame_buffer.c',
|
||||
'src/input_manager.c',
|
||||
'src/opengl.c',
|
||||
'src/receiver.c',
|
||||
|
|
88
app/src/frame_buffer.c
Normal file
88
app/src/frame_buffer.c
Normal 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
44
app/src/frame_buffer.h
Normal 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
|
|
@ -8,81 +8,21 @@
|
|||
|
||||
bool
|
||||
sc_video_buffer_init(struct sc_video_buffer *vb) {
|
||||
vb->pending_frame = av_frame_alloc();
|
||||
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;
|
||||
return sc_frame_buffer_init(&vb->fb);
|
||||
}
|
||||
|
||||
void
|
||||
sc_video_buffer_destroy(struct sc_video_buffer *vb) {
|
||||
sc_mutex_destroy(&vb->mutex);
|
||||
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;
|
||||
sc_frame_buffer_destroy(&vb->fb);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame,
|
||||
bool *previous_frame_skipped) {
|
||||
sc_mutex_lock(&vb->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(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;
|
||||
return sc_frame_buffer_push(&vb->fb, frame, previous_frame_skipped);
|
||||
}
|
||||
|
||||
void
|
||||
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) {
|
||||
sc_mutex_lock(&vb->mutex);
|
||||
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);
|
||||
sc_frame_buffer_consume(&vb->fb, dst);
|
||||
}
|
||||
|
|
|
@ -5,27 +5,13 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "frame_buffer.h"
|
||||
|
||||
// forward declarations
|
||||
typedef struct AVFrame AVFrame;
|
||||
|
||||
/**
|
||||
* A video 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_video_buffer {
|
||||
AVFrame *pending_frame;
|
||||
AVFrame *tmp_frame; // To preserve the pending frame on error
|
||||
|
||||
sc_mutex mutex;
|
||||
|
||||
bool pending_frame_consumed;
|
||||
struct sc_frame_buffer fb;
|
||||
};
|
||||
|
||||
bool
|
||||
|
|
Loading…
Reference in a new issue