mirror of
https://github.com/libretro/RetroArch
synced 2025-04-01 04:20:27 +00:00
Remove video fifo in ffmpeg core.
The video fifo can be removed, since we have a ring buffer in it's place. This removes unneeded copy operations and as a positive side improves overall decoding speed. Makes 8k60p SW and 4k60p HW decoding possible on my system. For now the ring buffer is 32 images deep. This limitation will be removed, once audio and video decoder have their own packet handling.
This commit is contained in:
parent
6eed40f80c
commit
c6309d963d
@ -1966,7 +1966,7 @@ endif
|
||||
ifeq ($(HAVE_FFMPEG), 1)
|
||||
OBJ += record/drivers/record_ffmpeg.o \
|
||||
cores/libretro-ffmpeg/ffmpeg_core.o \
|
||||
cores/libretro-ffmpeg/swsbuffer.o \
|
||||
cores/libretro-ffmpeg/video_buffer.o \
|
||||
$(LIBRETRO_COMM_DIR)/rthreads/tpool.o
|
||||
|
||||
LIBS += $(AVCODEC_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(SWRESAMPLE_LIBS) $(FFMPEG_LIBS)
|
||||
|
@ -19,7 +19,7 @@ SWRESAMPLE_DIR := $(BASE_DIR)/libswresample
|
||||
INCFLAGS += -I$(BASE_DIR) -I$(CORE_DIR) -I$(LIBRETRO_COMM_DIR)/include -I$(LIBRETRO_COMM_DIR)/include/compat
|
||||
|
||||
LIBRETRO_SOURCE += $(CORE_DIR)/ffmpeg_core.c \
|
||||
$(CORE_DIR)/swsbuffer.c \
|
||||
$(CORE_DIR)/video_buffer.c \
|
||||
$(LIBRETRO_COMM_DIR)/rthreads/tpool.c \
|
||||
$(LIBRETRO_COMM_DIR)/queues/fifo_queue.c \
|
||||
$(LIBRETRO_COMM_DIR)/rthreads/rthreads.c
|
||||
|
@ -50,7 +50,7 @@ extern "C" {
|
||||
#include <rthreads/tpool.h>
|
||||
#include <queues/fifo_queue.h>
|
||||
#include <string/stdstring.h>
|
||||
#include "swsbuffer.h"
|
||||
#include "video_buffer.h"
|
||||
|
||||
#include <libretro.h>
|
||||
#ifdef RARCH_INTERNAL
|
||||
@ -90,7 +90,7 @@ static enum AVColorSpace colorspace;
|
||||
|
||||
static unsigned sw_decoder_threads;
|
||||
static unsigned sw_sws_threads;
|
||||
static swsbuffer_t *swsbuffer;
|
||||
static video_buffer_t *video_buffer;
|
||||
static tpool_t *tpool;
|
||||
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
@ -141,20 +141,17 @@ static double pts_bias;
|
||||
|
||||
/* Threaded FIFOs. */
|
||||
static volatile bool decode_thread_dead;
|
||||
static fifo_buffer_t *video_decode_fifo;
|
||||
static fifo_buffer_t *audio_decode_fifo;
|
||||
static scond_t *fifo_cond;
|
||||
static scond_t *fifo_decode_cond;
|
||||
static slock_t *fifo_lock;
|
||||
static slock_t *decode_thread_lock;
|
||||
static sthread_t *decode_thread_handle;
|
||||
static double decode_last_video_time;
|
||||
static double decode_last_audio_time;
|
||||
static bool main_sleeping;
|
||||
|
||||
static uint32_t *video_frame_temp_buffer;
|
||||
|
||||
static bool main_sleeping;
|
||||
|
||||
/* Seeking. */
|
||||
static bool do_seek;
|
||||
static double seek_time;
|
||||
@ -459,7 +456,7 @@ static void check_variables(bool firststart)
|
||||
{
|
||||
sw_decoder_threads = strtoul(sw_threads_var.value, NULL, 0);
|
||||
}
|
||||
/* Scale the sws threads based on core count but use at min 2 and max 4 threads */
|
||||
/* Scale the sws threads based on core count but use at least 2 and at most 4 threads */
|
||||
sw_sws_threads = MIN(MAX(2, sw_decoder_threads / 2), 4);
|
||||
}
|
||||
}
|
||||
@ -492,8 +489,9 @@ static void seek_frame(int seek_frames)
|
||||
}
|
||||
audio_frames = frame_cnt * media.sample_rate / media.interpolate_fps;
|
||||
|
||||
if (video_decode_fifo)
|
||||
fifo_clear(video_decode_fifo);
|
||||
tpool_wait(tpool);
|
||||
video_buffer_clear(video_buffer);
|
||||
|
||||
if (audio_decode_fifo)
|
||||
fifo_clear(audio_decode_fifo);
|
||||
scond_signal(fifo_decode_cond);
|
||||
@ -692,24 +690,19 @@ void CORE_PREFIX(retro_run)(void)
|
||||
|
||||
while (!decode_thread_dead && min_pts > frames[1].pts)
|
||||
{
|
||||
size_t to_read_frame_bytes;
|
||||
int64_t pts = 0;
|
||||
|
||||
slock_lock(fifo_lock);
|
||||
to_read_frame_bytes = media.width * media.height * sizeof(uint32_t) + sizeof(int64_t);
|
||||
|
||||
while (!decode_thread_dead && fifo_read_avail(video_decode_fifo) < to_read_frame_bytes)
|
||||
{
|
||||
main_sleeping = true;
|
||||
scond_signal(fifo_decode_cond);
|
||||
scond_wait(fifo_cond, fifo_lock);
|
||||
main_sleeping = false;
|
||||
}
|
||||
if (!decode_thread_dead)
|
||||
video_buffer_wait_for_finished_slot(video_buffer);
|
||||
|
||||
if (!decode_thread_dead)
|
||||
{
|
||||
uint32_t *data = video_frame_temp_buffer;
|
||||
fifo_read(video_decode_fifo, &pts, sizeof(int64_t));
|
||||
|
||||
video_decoder_context_t *ctx = NULL;
|
||||
video_buffer_get_finished_slot(video_buffer, &ctx);
|
||||
pts = ctx->pts;
|
||||
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
||||
if (use_gl)
|
||||
{
|
||||
@ -723,7 +716,11 @@ void CORE_PREFIX(retro_run)(void)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
fifo_read(video_decode_fifo, data, media.width * media.height * sizeof(uint32_t));
|
||||
const uint8_t *src = ctx->target->data[0];
|
||||
int stride = ctx->target->linesize[0];
|
||||
int width = media.width * sizeof(uint32_t);
|
||||
for (unsigned y = 0; y < media.height; y++, src += stride, data += width/4)
|
||||
memcpy(data, src, width);
|
||||
|
||||
#ifndef HAVE_OPENGLES
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
@ -744,14 +741,17 @@ void CORE_PREFIX(retro_run)(void)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
fifo_read(video_decode_fifo, data, media.width * media.height * sizeof(uint32_t));
|
||||
const uint8_t *src = ctx->target->data[0];
|
||||
int stride = ctx->target->linesize[0];
|
||||
size_t width = media.width * sizeof(uint32_t);
|
||||
for (unsigned y = 0; y < media.height; y++, src += stride, data += width/4)
|
||||
memcpy(data, src, width);
|
||||
|
||||
dupe = false;
|
||||
}
|
||||
video_buffer_open_slot(video_buffer, ctx);
|
||||
}
|
||||
|
||||
scond_signal(fifo_decode_cond);
|
||||
slock_unlock(fifo_lock);
|
||||
|
||||
frames[1].pts = av_q2d(fctx->streams[video_stream_index]->time_base) * pts;
|
||||
}
|
||||
|
||||
@ -1289,7 +1289,7 @@ static void sws_worker_thread(void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
AVFrame *tmp_frame = NULL;
|
||||
sws_context_t *ctx = (sws_context_t*) arg;
|
||||
video_decoder_context_t *ctx = (video_decoder_context_t*) arg;
|
||||
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
if (hw_decoding_enabled)
|
||||
@ -1318,75 +1318,25 @@ static void sws_worker_thread(void *arg)
|
||||
#endif
|
||||
}
|
||||
|
||||
swsbuffer_finish_slot(swsbuffer, ctx);
|
||||
}
|
||||
ctx->pts = ctx->source->best_effort_timestamp;
|
||||
|
||||
#ifdef HAVE_SSA
|
||||
static void add_frame_to_fifo(size_t frame_size, ASS_Track *ass_track_active)
|
||||
#else
|
||||
static void add_frame_to_fifo(size_t frame_size)
|
||||
#endif
|
||||
{
|
||||
sws_context_t *ctx = NULL;
|
||||
|
||||
swsbuffer_get_finished_slot(swsbuffer, &ctx);
|
||||
size_t decoded_size;
|
||||
int64_t pts = ctx->source->best_effort_timestamp;
|
||||
double video_time = pts * av_q2d(fctx->streams[video_stream_index]->time_base);
|
||||
|
||||
#ifdef HAVE_SSA
|
||||
if (ass_render && ass_track_active)
|
||||
double video_time = ctx->pts * av_q2d(fctx->streams[video_stream_index]->time_base);
|
||||
if (ass_render && ctx->ass_track_active)
|
||||
{
|
||||
int change = 0;
|
||||
ASS_Image *img = ass_render_frame(ass_render, ass_track_active,
|
||||
ASS_Image *img = ass_render_frame(ass_render, ctx->ass_track_active,
|
||||
1000 * video_time, &change);
|
||||
|
||||
/*
|
||||
* Do it on CPU for now.
|
||||
* We're in a thread anyways, so shouldn't really matter.
|
||||
*/
|
||||
render_ass_img(ctx->target, img);
|
||||
}
|
||||
#endif
|
||||
|
||||
decoded_size = frame_size + sizeof(pts);
|
||||
|
||||
slock_lock(fifo_lock);
|
||||
while (!decode_thread_dead && (video_decode_fifo != NULL)
|
||||
&& fifo_write_avail(video_decode_fifo) < decoded_size)
|
||||
{
|
||||
if (!main_sleeping)
|
||||
scond_wait(fifo_decode_cond, fifo_lock);
|
||||
else
|
||||
{
|
||||
fifo_clear(video_decode_fifo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
decode_last_video_time = video_time;
|
||||
if (!decode_thread_dead)
|
||||
{
|
||||
int stride;
|
||||
unsigned y;
|
||||
const uint8_t *src = NULL;
|
||||
|
||||
fifo_write(video_decode_fifo, &pts, sizeof(pts));
|
||||
src = ctx->target->data[0];
|
||||
stride = ctx->target->linesize[0];
|
||||
|
||||
for (y = 0; y < media.height; y++, src += stride)
|
||||
fifo_write(video_decode_fifo, src, media.width * sizeof(uint32_t));
|
||||
}
|
||||
scond_signal(fifo_cond);
|
||||
slock_unlock(fifo_lock);
|
||||
|
||||
av_frame_unref(ctx->source);
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
av_frame_unref(ctx->hw_source);
|
||||
#endif
|
||||
|
||||
swsbuffer_open_slot(swsbuffer, ctx);
|
||||
video_buffer_finish_slot(video_buffer, ctx);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSA
|
||||
@ -1396,7 +1346,24 @@ static void decode_video(AVCodecContext *ctx, AVPacket *pkt, size_t frame_size)
|
||||
#endif
|
||||
{
|
||||
int ret = 0;
|
||||
sws_context_t *sws_ctx = NULL;
|
||||
video_decoder_context_t *decoder_ctx = NULL;
|
||||
|
||||
/* Stop decoding thread until video_buffer is not full again */
|
||||
while (!decode_thread_dead && !video_buffer_has_open_slot(video_buffer))
|
||||
{
|
||||
/* If we don't buffer enough video frames we can run into a deadlock.
|
||||
* for now drop frames in this case. This could happen with MP4 files
|
||||
* since the often save the audio frames into the stream.
|
||||
* Longterm solution: audio and video decoding in their own threads
|
||||
* with their own file handle. */
|
||||
if (main_sleeping)
|
||||
{
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Thread: Video deadlock detected.\n");
|
||||
tpool_wait(tpool);
|
||||
video_buffer_clear(video_buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = avcodec_send_packet(ctx, pkt)) < 0)
|
||||
{
|
||||
@ -1408,24 +1375,11 @@ static void decode_video(AVCodecContext *ctx, AVPacket *pkt, size_t frame_size)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop decoding thread until swsbuffer is not full again */
|
||||
while (!swsbuffer_has_open_slot(swsbuffer))
|
||||
while (!decode_thread_dead && video_buffer_has_open_slot(video_buffer))
|
||||
{
|
||||
while(swsbuffer_has_finished_slot(swsbuffer))
|
||||
{
|
||||
#ifdef HAVE_SSA
|
||||
add_frame_to_fifo(frame_size, ass_track_active);
|
||||
#else
|
||||
add_frame_to_fifo(frame_size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
video_buffer_get_open_slot(video_buffer, &decoder_ctx);
|
||||
|
||||
while (swsbuffer_has_open_slot(swsbuffer))
|
||||
{
|
||||
swsbuffer_get_open_slot(swsbuffer, &sws_ctx);
|
||||
|
||||
ret = avcodec_receive_frame(ctx, sws_ctx->source);
|
||||
ret = avcodec_receive_frame(ctx, decoder_ctx->source);
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
{
|
||||
ret = -42;
|
||||
@ -1444,7 +1398,7 @@ static void decode_video(AVCodecContext *ctx, AVPacket *pkt, size_t frame_size)
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
if (hw_decoding_enabled)
|
||||
/* Copy data from VRAM to RAM */
|
||||
if ((ret = av_hwframe_transfer_data(sws_ctx->hw_source, sws_ctx->source, 0)) < 0)
|
||||
if ((ret = av_hwframe_transfer_data(decoder_ctx->hw_source, decoder_ctx->source, 0)) < 0)
|
||||
{
|
||||
#ifdef __cplusplus
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Error transferring the data to system memory: %d\n", ret);
|
||||
@ -1455,21 +1409,16 @@ static void decode_video(AVCodecContext *ctx, AVPacket *pkt, size_t frame_size)
|
||||
}
|
||||
#endif
|
||||
|
||||
tpool_add_work(tpool, sws_worker_thread, sws_ctx);
|
||||
|
||||
while(swsbuffer_has_finished_slot(swsbuffer))
|
||||
{
|
||||
#ifdef HAVE_SSA
|
||||
add_frame_to_fifo(frame_size, ass_track_active);
|
||||
#else
|
||||
add_frame_to_fifo(frame_size);
|
||||
decoder_ctx->ass_track_active = ass_track_active;
|
||||
#endif
|
||||
}
|
||||
|
||||
tpool_add_work(tpool, sws_worker_thread, decoder_ctx);
|
||||
|
||||
end:
|
||||
if (ret < 0)
|
||||
{
|
||||
swsbuffer_return_open_slot(swsbuffer, sws_ctx);
|
||||
video_buffer_return_open_slot(video_buffer, decoder_ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1523,7 +1472,7 @@ static int16_t *decode_audio(AVCodecContext *ctx, AVPacket *pkt,
|
||||
(const uint8_t**)frame->data,
|
||||
frame->nb_samples);
|
||||
|
||||
pts = av_frame_get_best_effort_timestamp(frame);
|
||||
pts = frame->best_effort_timestamp;
|
||||
slock_lock(fifo_lock);
|
||||
|
||||
while (!decode_thread_dead && fifo_write_avail(audio_decode_fifo) < required_buffer)
|
||||
@ -1559,7 +1508,6 @@ static void decode_thread_seek(double time)
|
||||
if (seek_to < 0)
|
||||
seek_to = 0;
|
||||
|
||||
decode_last_video_time = time;
|
||||
decode_last_audio_time = time;
|
||||
|
||||
if(avformat_seek_file(fctx, -1, INT64_MIN, seek_to, INT64_MAX, 0) < 0)
|
||||
@ -1606,7 +1554,7 @@ static void decode_thread(void *data)
|
||||
if (video_stream_index >= 0)
|
||||
{
|
||||
frame_size = avpicture_get_size(PIX_FMT_RGB32, media.width, media.height);
|
||||
swsbuffer = swsbuffer_create(sw_sws_threads, frame_size, media.width, media.height);
|
||||
video_buffer = video_buffer_create(32, frame_size, media.width, media.height);
|
||||
tpool = tpool_create(sw_sws_threads);
|
||||
log_cb(RETRO_LOG_INFO, "[FFMPEG] Configured filtering threads: %d\n", sw_sws_threads);
|
||||
}
|
||||
@ -1631,20 +1579,18 @@ static void decode_thread(void *data)
|
||||
|
||||
if (seek)
|
||||
{
|
||||
if (video_stream_index >= 0)
|
||||
{
|
||||
tpool_wait(tpool);
|
||||
swsbuffer_clear(swsbuffer);
|
||||
}
|
||||
|
||||
decode_thread_seek(seek_time_thread);
|
||||
|
||||
slock_lock(fifo_lock);
|
||||
do_seek = false;
|
||||
seek_time = 0.0;
|
||||
|
||||
if (video_decode_fifo)
|
||||
fifo_clear(video_decode_fifo);
|
||||
if (video_stream_index >= 0)
|
||||
{
|
||||
tpool_wait(tpool);
|
||||
video_buffer_clear(video_buffer);
|
||||
}
|
||||
|
||||
if (audio_decode_fifo)
|
||||
fifo_clear(audio_decode_fifo);
|
||||
|
||||
@ -1719,12 +1665,6 @@ static void decode_thread(void *data)
|
||||
av_frame_free(&aud_frame);
|
||||
av_freep(&audio_buffer);
|
||||
|
||||
if (video_stream_index >= 0)
|
||||
{
|
||||
tpool_destroy(tpool);
|
||||
swsbuffer_destroy(swsbuffer);
|
||||
}
|
||||
|
||||
slock_lock(fifo_lock);
|
||||
decode_thread_dead = true;
|
||||
scond_signal(fifo_cond);
|
||||
@ -1836,8 +1776,12 @@ void CORE_PREFIX(retro_unload_game)(void)
|
||||
if (decode_thread_handle)
|
||||
{
|
||||
slock_lock(fifo_lock);
|
||||
|
||||
tpool_wait(tpool);
|
||||
video_buffer_clear(video_buffer);
|
||||
decode_thread_dead = true;
|
||||
scond_signal(fifo_decode_cond);
|
||||
|
||||
slock_unlock(fifo_lock);
|
||||
sthread_join(decode_thread_handle);
|
||||
}
|
||||
@ -1852,8 +1796,6 @@ void CORE_PREFIX(retro_unload_game)(void)
|
||||
if (decode_thread_lock)
|
||||
slock_free(decode_thread_lock);
|
||||
|
||||
if (video_decode_fifo)
|
||||
fifo_free(video_decode_fifo);
|
||||
if (audio_decode_fifo)
|
||||
fifo_free(audio_decode_fifo);
|
||||
|
||||
@ -1861,10 +1803,8 @@ void CORE_PREFIX(retro_unload_game)(void)
|
||||
fifo_decode_cond = NULL;
|
||||
fifo_lock = NULL;
|
||||
decode_thread_lock = NULL;
|
||||
video_decode_fifo = NULL;
|
||||
audio_decode_fifo = NULL;
|
||||
|
||||
decode_last_video_time = 0.0;
|
||||
decode_last_audio_time = 0.0;
|
||||
|
||||
frames[0].pts = frames[1].pts = 0.0;
|
||||
@ -1994,11 +1934,6 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info)
|
||||
|
||||
if (video_stream_index >= 0 || is_fft)
|
||||
{
|
||||
/* video fifo is 2 frames deep */
|
||||
video_decode_fifo = fifo_new(
|
||||
media.width * media.height * sizeof(uint32_t) * 2
|
||||
);
|
||||
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
||||
use_gl = true;
|
||||
hw_render.context_reset = context_reset;
|
||||
@ -2014,15 +1949,15 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info)
|
||||
if (!CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
||||
{
|
||||
use_gl = false;
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot initialize HW render.");
|
||||
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot initialize HW render.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (audio_streams_num > 0)
|
||||
{
|
||||
/* audio fifo is 1 second deep */
|
||||
/* audio fifo is 4 seconds deep */
|
||||
audio_decode_fifo = fifo_new(
|
||||
media.sample_rate * sizeof(int16_t) * 2
|
||||
media.sample_rate * sizeof(int16_t) * 2 * 4
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,185 +0,0 @@
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include <rthreads/rthreads.h>
|
||||
|
||||
#include "swsbuffer.h"
|
||||
|
||||
enum kbStatus
|
||||
{
|
||||
KB_OPEN,
|
||||
KB_IN_PROGRESS,
|
||||
KB_FINISHED
|
||||
};
|
||||
|
||||
struct swsbuffer
|
||||
{
|
||||
sws_context_t *buffer;
|
||||
enum kbStatus *status;
|
||||
size_t size;
|
||||
slock_t *lock;
|
||||
int64_t head;
|
||||
int64_t tail;
|
||||
};
|
||||
|
||||
swsbuffer_t *swsbuffer_create(size_t num, int frame_size, int width, int height)
|
||||
{
|
||||
swsbuffer_t *b = malloc(sizeof (swsbuffer_t));
|
||||
if (b == NULL)
|
||||
return NULL;
|
||||
|
||||
b->status = malloc(sizeof(enum kbStatus) * num);
|
||||
if (b->status == NULL)
|
||||
return NULL;
|
||||
for (int i = 0; i < num; i++)
|
||||
b->status[i] = KB_OPEN;
|
||||
|
||||
b->lock = slock_new();
|
||||
if (b->lock == NULL)
|
||||
return NULL;
|
||||
|
||||
b->buffer = malloc(sizeof(sws_context_t) * num);
|
||||
if (b->buffer == NULL)
|
||||
return NULL;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
b->buffer[i].index = i;
|
||||
b->buffer[i].sws = sws_alloc_context();
|
||||
b->buffer[i].source = av_frame_alloc();
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
b->buffer[i].hw_source = av_frame_alloc();
|
||||
#endif
|
||||
b->buffer[i].target = av_frame_alloc();
|
||||
b->buffer[i].frame_buf = av_malloc(frame_size);
|
||||
|
||||
avpicture_fill((AVPicture*)b->buffer[i].target, (const uint8_t*)b->buffer[i].frame_buf,
|
||||
PIX_FMT_RGB32, width, height);
|
||||
}
|
||||
|
||||
b->size = num;
|
||||
b->head = 0;
|
||||
b->tail = 0;
|
||||
return b;
|
||||
}
|
||||
|
||||
void swsbuffer_destroy(swsbuffer_t *swsbuffer)
|
||||
{
|
||||
if (swsbuffer != NULL) {
|
||||
slock_free(swsbuffer->lock);
|
||||
free(swsbuffer->status);
|
||||
for (int i = 0; i < swsbuffer->size; i++)
|
||||
{
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
av_frame_free(&swsbuffer->buffer[i].hw_source);
|
||||
#endif
|
||||
av_frame_free(&swsbuffer->buffer[i].source);
|
||||
av_frame_free(&swsbuffer->buffer[i].target);
|
||||
av_freep(&swsbuffer->buffer[i].frame_buf);
|
||||
sws_freeContext(swsbuffer->buffer[i].sws);
|
||||
}
|
||||
free(swsbuffer->buffer);
|
||||
free(swsbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void swsbuffer_clear(swsbuffer_t *swsbuffer)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
swsbuffer->head = 0;
|
||||
swsbuffer->tail = 0;
|
||||
for (int i = 0; i < swsbuffer->size; i++)
|
||||
swsbuffer->status[i] = KB_OPEN;
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
void swsbuffer_get_open_slot(swsbuffer_t *swsbuffer, sws_context_t **context)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[swsbuffer->head] == KB_OPEN)
|
||||
{
|
||||
*context = &swsbuffer->buffer[swsbuffer->head];
|
||||
swsbuffer->status[swsbuffer->head] = KB_IN_PROGRESS;
|
||||
swsbuffer->head++;
|
||||
swsbuffer->head %= swsbuffer->size;
|
||||
}
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
void swsbuffer_return_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[context->index] == KB_IN_PROGRESS)
|
||||
{
|
||||
swsbuffer->status[context->index] = KB_OPEN;
|
||||
swsbuffer->head--;
|
||||
swsbuffer->head %= swsbuffer->size;
|
||||
}
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
void swsbuffer_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[context->index] == KB_FINISHED)
|
||||
{
|
||||
swsbuffer->status[context->index] = KB_OPEN;
|
||||
swsbuffer->tail++;
|
||||
swsbuffer->tail %= (swsbuffer->size);
|
||||
}
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
void swsbuffer_get_finished_slot(swsbuffer_t *swsbuffer, sws_context_t **context)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[swsbuffer->tail] == KB_FINISHED)
|
||||
*context = &swsbuffer->buffer[swsbuffer->tail];
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
void swsbuffer_finish_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
|
||||
{
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[context->index] == KB_IN_PROGRESS)
|
||||
swsbuffer->status[context->index] = KB_FINISHED;
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
}
|
||||
|
||||
bool swsbuffer_has_open_slot(swsbuffer_t *swsbuffer)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[swsbuffer->head] == KB_OPEN)
|
||||
ret = true;
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool swsbuffer_has_finished_slot(swsbuffer_t *swsbuffer)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
slock_lock(swsbuffer->lock);
|
||||
|
||||
if (swsbuffer->status[swsbuffer->tail] == KB_FINISHED)
|
||||
ret = true;
|
||||
|
||||
slock_unlock(swsbuffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
#ifndef __LIBRETRO_SDK_SWSBUFFER_H__
|
||||
#define __LIBRETRO_SDK_SWSBUFFER_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#include <libavutil/frame.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifndef PIX_FMT_RGB32
|
||||
#define PIX_FMT_RGB32 AV_PIX_FMT_RGB32
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sws_context
|
||||
*
|
||||
* Context object for the sws worker threads.
|
||||
*
|
||||
*/
|
||||
struct sws_context
|
||||
{
|
||||
int index;
|
||||
struct SwsContext *sws;
|
||||
AVFrame *source;
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
AVFrame *hw_source;
|
||||
#endif
|
||||
AVFrame *target;
|
||||
void *frame_buf;
|
||||
};
|
||||
typedef struct sws_context sws_context_t;
|
||||
|
||||
/**
|
||||
* swsbuffer
|
||||
*
|
||||
* The swsbuffer is a ring buffer, that can be used as a
|
||||
* buffer for many workers while keeping the order.
|
||||
*
|
||||
* It is thread safe in a sensem that it is designed to work
|
||||
* with one work coordinator, that allocates work slots for
|
||||
* workers threads to work on and later collect the work
|
||||
* product in the same order, as the slots were allocated.
|
||||
*
|
||||
*/
|
||||
struct swsbuffer;
|
||||
typedef struct swsbuffer swsbuffer_t;
|
||||
|
||||
/**
|
||||
* swsbuffer_create:
|
||||
* @num : Size of the buffer.
|
||||
* @frame_size : Size of the target frame.
|
||||
* @width : Width of the target frame.
|
||||
* @height : Height of the target frame.
|
||||
*
|
||||
* Create a swsbuffer.
|
||||
*
|
||||
* Returns: swsbuffer.
|
||||
*/
|
||||
swsbuffer_t *swsbuffer_create(size_t num, int frame_size, int width, int height);
|
||||
|
||||
/**
|
||||
* swsbuffer_destroy:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
*
|
||||
* Destory a swsbuffer.
|
||||
*
|
||||
* Does also free the buffer allocated with swsbuffer_create().
|
||||
* User has to shut down any external worker threads that may have
|
||||
* a reference to this swsbuffer.
|
||||
*
|
||||
**/
|
||||
void swsbuffer_destroy(swsbuffer_t *swsbuffer);
|
||||
|
||||
/**
|
||||
* swsbuffer_clear:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
*
|
||||
* Clears a swsbuffer.
|
||||
*
|
||||
**/
|
||||
void swsbuffer_clear(swsbuffer_t *swsbuffer);
|
||||
|
||||
/**
|
||||
* swsbuffer_get_open_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
* @contex : sws context.
|
||||
*
|
||||
* Returns the next open context inside the ring buffer
|
||||
* and it's index. The status of the slot will be marked as
|
||||
* 'in progress' until slot is marked as finished with
|
||||
* swsbuffer_finish_slot();
|
||||
*
|
||||
**/
|
||||
void swsbuffer_get_open_slot(swsbuffer_t *swsbuffer, sws_context_t **context);
|
||||
|
||||
/**
|
||||
* swsbuffer_return_open_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
* @contex : sws context.
|
||||
*
|
||||
* Marks the given sws context that is "in progress" as "open" again.
|
||||
*
|
||||
**/
|
||||
void swsbuffer_return_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context);
|
||||
|
||||
/**
|
||||
* swsbuffer_open_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Sets the status of the given context from "finished" to "open".
|
||||
* The slot is then available for producers to claim again with swsbuffer_get_open_slot().
|
||||
**/
|
||||
void swsbuffer_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context);
|
||||
|
||||
/**
|
||||
* swsbuffer_get_finished_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Returns a reference for the next context inside
|
||||
* the ring buffer. User needs to use swsbuffer_open_slot()
|
||||
* to open the slot in the ringbuffer for the next
|
||||
* work assignment. User is free to re-allocate or
|
||||
* re-use the context.
|
||||
*
|
||||
*/
|
||||
void swsbuffer_get_finished_slot(swsbuffer_t *swsbuffer, sws_context_t **context);
|
||||
|
||||
/**
|
||||
* swsbuffer_finish_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Sets the status of the given context from "in progress" to "finished".
|
||||
* This is normally done by a producer. User can then retrieve the finished work
|
||||
* context by calling swsbuffer_get_finished_slot().
|
||||
*/
|
||||
void swsbuffer_finish_slot(swsbuffer_t *swsbuffer, sws_context_t *context);
|
||||
|
||||
/**
|
||||
* swsbuffer_has_open_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
*
|
||||
* Returns true if the buffer has a open slot available.
|
||||
*/
|
||||
bool swsbuffer_has_open_slot(swsbuffer_t *swsbuffer);
|
||||
|
||||
/**
|
||||
* swsbuffer_has_finished_slot:
|
||||
* @swsbuffer : sswsbuffer.
|
||||
*
|
||||
* Returns true if the buffers next slot is finished and a
|
||||
* context available.
|
||||
*/
|
||||
bool swsbuffer_has_finished_slot(swsbuffer_t *swsbuffer);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
244
cores/libretro-ffmpeg/video_buffer.c
Normal file
244
cores/libretro-ffmpeg/video_buffer.c
Normal file
@ -0,0 +1,244 @@
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include <rthreads/rthreads.h>
|
||||
|
||||
#include "video_buffer.h"
|
||||
|
||||
enum kbStatus
|
||||
{
|
||||
KB_OPEN,
|
||||
KB_IN_PROGRESS,
|
||||
KB_FINISHED
|
||||
};
|
||||
|
||||
struct video_buffer
|
||||
{
|
||||
video_decoder_context_t *buffer;
|
||||
enum kbStatus *status;
|
||||
size_t size;
|
||||
slock_t *lock;
|
||||
scond_t *open_cond;
|
||||
scond_t *finished_cond;
|
||||
int64_t head;
|
||||
int64_t tail;
|
||||
};
|
||||
|
||||
video_buffer_t *video_buffer_create(size_t num, int frame_size, int width, int height)
|
||||
{
|
||||
video_buffer_t *b = malloc(sizeof (video_buffer_t));
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
b->lock = NULL;
|
||||
b->open_cond = NULL;
|
||||
b->finished_cond = NULL;
|
||||
b->buffer = NULL;
|
||||
b->size = num;
|
||||
b->head = 0;
|
||||
b->tail = 0;
|
||||
|
||||
b->status = malloc(sizeof(enum kbStatus) * num);
|
||||
if (!b->status)
|
||||
goto fail;
|
||||
for (int i = 0; i < num; i++)
|
||||
b->status[i] = KB_OPEN;
|
||||
|
||||
b->lock = slock_new();
|
||||
b->open_cond = scond_new();
|
||||
b->finished_cond = scond_new();
|
||||
if (!b->lock || !b->open_cond || !b->finished_cond)
|
||||
goto fail;
|
||||
|
||||
b->buffer = malloc(sizeof(video_decoder_context_t) * num);
|
||||
if (!b->buffer)
|
||||
goto fail;
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
b->buffer[i].index = i;
|
||||
b->buffer[i].pts = 0;
|
||||
b->buffer[i].sws = sws_alloc_context();
|
||||
b->buffer[i].source = av_frame_alloc();
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
b->buffer[i].hw_source = av_frame_alloc();
|
||||
#endif
|
||||
b->buffer[i].target = av_frame_alloc();
|
||||
b->buffer[i].frame_buf = av_malloc(frame_size);
|
||||
|
||||
avpicture_fill((AVPicture*)b->buffer[i].target, (const uint8_t*)b->buffer[i].frame_buf,
|
||||
PIX_FMT_RGB32, width, height);
|
||||
|
||||
if (!b->buffer[i].sws ||
|
||||
!b->buffer[i].source ||
|
||||
!b->buffer[i].hw_source ||
|
||||
!b->buffer[i].target ||
|
||||
!b->buffer[i].frame_buf)
|
||||
goto fail;
|
||||
}
|
||||
return b;
|
||||
|
||||
fail:
|
||||
video_buffer_destroy(b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void video_buffer_destroy(video_buffer_t *video_buffer)
|
||||
{
|
||||
if (!video_buffer)
|
||||
return;
|
||||
|
||||
slock_free(video_buffer->lock);
|
||||
scond_free(video_buffer->open_cond);
|
||||
scond_free(video_buffer->finished_cond);
|
||||
free(video_buffer->status);
|
||||
if (video_buffer->buffer)
|
||||
for (int i = 0; i < video_buffer->size; i++)
|
||||
{
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
av_frame_free(&video_buffer->buffer[i].hw_source);
|
||||
#endif
|
||||
av_frame_free(&video_buffer->buffer[i].source);
|
||||
av_frame_free(&video_buffer->buffer[i].target);
|
||||
av_freep(&video_buffer->buffer[i].frame_buf);
|
||||
sws_freeContext(video_buffer->buffer[i].sws);
|
||||
}
|
||||
free(video_buffer->buffer);
|
||||
free(video_buffer);
|
||||
}
|
||||
|
||||
void video_buffer_clear(video_buffer_t *video_buffer)
|
||||
{
|
||||
if (!video_buffer)
|
||||
return;
|
||||
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
scond_signal(video_buffer->open_cond);
|
||||
scond_signal(video_buffer->finished_cond);
|
||||
|
||||
video_buffer->head = 0;
|
||||
video_buffer->tail = 0;
|
||||
for (int i = 0; i < video_buffer->size; i++)
|
||||
video_buffer->status[i] = KB_OPEN;
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
void video_buffer_get_open_slot(video_buffer_t *video_buffer, video_decoder_context_t **context)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[video_buffer->head] == KB_OPEN)
|
||||
{
|
||||
*context = &video_buffer->buffer[video_buffer->head];
|
||||
video_buffer->status[video_buffer->head] = KB_IN_PROGRESS;
|
||||
video_buffer->head++;
|
||||
video_buffer->head %= video_buffer->size;
|
||||
}
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
void video_buffer_return_open_slot(video_buffer_t *video_buffer, video_decoder_context_t *context)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[context->index] == KB_IN_PROGRESS)
|
||||
{
|
||||
video_buffer->status[context->index] = KB_OPEN;
|
||||
video_buffer->head--;
|
||||
video_buffer->head %= video_buffer->size;
|
||||
}
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
void video_buffer_open_slot(video_buffer_t *video_buffer, video_decoder_context_t *context)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[context->index] == KB_FINISHED)
|
||||
{
|
||||
video_buffer->status[context->index] = KB_OPEN;
|
||||
video_buffer->tail++;
|
||||
video_buffer->tail %= (video_buffer->size);
|
||||
scond_signal(video_buffer->open_cond);
|
||||
}
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
void video_buffer_get_finished_slot(video_buffer_t *video_buffer, video_decoder_context_t **context)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[video_buffer->tail] == KB_FINISHED)
|
||||
*context = &video_buffer->buffer[video_buffer->tail];
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
void video_buffer_finish_slot(video_buffer_t *video_buffer, video_decoder_context_t *context)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[context->index] == KB_IN_PROGRESS)
|
||||
{
|
||||
video_buffer->status[context->index] = KB_FINISHED;
|
||||
scond_signal(video_buffer->finished_cond);
|
||||
}
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
}
|
||||
|
||||
bool video_buffer_wait_for_open_slot(video_buffer_t *video_buffer)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
while (video_buffer->status[video_buffer->head] != KB_OPEN)
|
||||
scond_wait(video_buffer->open_cond, video_buffer->lock);
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool video_buffer_wait_for_finished_slot(video_buffer_t *video_buffer)
|
||||
{
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
while (video_buffer->status[video_buffer->tail] != KB_FINISHED)
|
||||
scond_wait(video_buffer->finished_cond, video_buffer->lock);
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool video_buffer_has_open_slot(video_buffer_t *video_buffer)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[video_buffer->head] == KB_OPEN)
|
||||
ret = true;
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool video_buffer_has_finished_slot(video_buffer_t *video_buffer)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
slock_lock(video_buffer->lock);
|
||||
|
||||
if (video_buffer->status[video_buffer->tail] == KB_FINISHED)
|
||||
ret = true;
|
||||
|
||||
slock_unlock(video_buffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
196
cores/libretro-ffmpeg/video_buffer.h
Normal file
196
cores/libretro-ffmpeg/video_buffer.h
Normal file
@ -0,0 +1,196 @@
|
||||
#ifndef __LIBRETRO_SDK_SWSBUFFER_H__
|
||||
#define __LIBRETRO_SDK_SWSBUFFER_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef HAVE_SSA
|
||||
#include <ass/ass.h>
|
||||
#endif
|
||||
|
||||
#include <libavutil/frame.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifndef PIX_FMT_RGB32
|
||||
#define PIX_FMT_RGB32 AV_PIX_FMT_RGB32
|
||||
#endif
|
||||
|
||||
/**
|
||||
* video_decoder_context
|
||||
*
|
||||
* Context object for the sws worker threads.
|
||||
*
|
||||
*/
|
||||
struct video_decoder_context
|
||||
{
|
||||
int index;
|
||||
int64_t pts;
|
||||
struct SwsContext *sws;
|
||||
AVFrame *source;
|
||||
#if LIBAVUTIL_VERSION_MAJOR > 55
|
||||
AVFrame *hw_source;
|
||||
#endif
|
||||
AVFrame *target;
|
||||
#ifdef HAVE_SSA
|
||||
ASS_Track *ass_track_active;
|
||||
#endif
|
||||
uint8_t *frame_buf;
|
||||
};
|
||||
typedef struct video_decoder_context video_decoder_context_t;
|
||||
|
||||
/**
|
||||
* video_buffer
|
||||
*
|
||||
* The video_buffer is a ring buffer, that can be used as a
|
||||
* buffer for many workers while keeping the order.
|
||||
*
|
||||
* It is thread safe in a sensem that it is designed to work
|
||||
* with one work coordinator, that allocates work slots for
|
||||
* workers threads to work on and later collect the work
|
||||
* product in the same order, as the slots were allocated.
|
||||
*
|
||||
*/
|
||||
struct video_buffer;
|
||||
typedef struct video_buffer video_buffer_t;
|
||||
|
||||
/**
|
||||
* video_buffer_create:
|
||||
* @num : Size of the buffer.
|
||||
* @frame_size : Size of the target frame.
|
||||
* @width : Width of the target frame.
|
||||
* @height : Height of the target frame.
|
||||
*
|
||||
* Create a video_buffer.
|
||||
*
|
||||
* Returns: A video buffer.
|
||||
*/
|
||||
video_buffer_t *video_buffer_create(size_t num, int frame_size, int width, int height);
|
||||
|
||||
/**
|
||||
* video_buffer_destroy:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Destory a video_buffer.
|
||||
*
|
||||
* Does also free the buffer allocated with video_buffer_create().
|
||||
* User has to shut down any external worker threads that may have
|
||||
* a reference to this video_buffer.
|
||||
*
|
||||
**/
|
||||
void video_buffer_destroy(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
* video_buffer_clear:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Clears a video_buffer.
|
||||
*
|
||||
**/
|
||||
void video_buffer_clear(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
* video_buffer_get_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @contex : sws context.
|
||||
*
|
||||
* Returns the next open context inside the ring buffer
|
||||
* and it's index. The status of the slot will be marked as
|
||||
* 'in progress' until slot is marked as finished with
|
||||
* video_buffer_finish_slot();
|
||||
*
|
||||
**/
|
||||
void video_buffer_get_open_slot(video_buffer_t *video_buffer, video_decoder_context_t **context);
|
||||
|
||||
/**
|
||||
* video_buffer_return_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @contex : sws context.
|
||||
*
|
||||
* Marks the given sws context that is "in progress" as "open" again.
|
||||
*
|
||||
**/
|
||||
void video_buffer_return_open_slot(video_buffer_t *video_buffer, video_decoder_context_t *context);
|
||||
|
||||
/**
|
||||
* video_buffer_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Sets the status of the given context from "finished" to "open".
|
||||
* The slot is then available for producers to claim again with video_buffer_get_open_slot().
|
||||
**/
|
||||
void video_buffer_open_slot(video_buffer_t *video_buffer, video_decoder_context_t *context);
|
||||
|
||||
/**
|
||||
* video_buffer_get_finished_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Returns a reference for the next context inside
|
||||
* the ring buffer. User needs to use video_buffer_open_slot()
|
||||
* to open the slot in the ringbuffer for the next
|
||||
* work assignment. User is free to re-allocate or
|
||||
* re-use the context.
|
||||
*
|
||||
*/
|
||||
void video_buffer_get_finished_slot(video_buffer_t *video_buffer, video_decoder_context_t **context);
|
||||
|
||||
/**
|
||||
* video_buffer_finish_slot:
|
||||
* @video_buffer : video buffer.
|
||||
* @context : sws context.
|
||||
*
|
||||
* Sets the status of the given context from "in progress" to "finished".
|
||||
* This is normally done by a producer. User can then retrieve the finished work
|
||||
* context by calling video_buffer_get_finished_slot().
|
||||
*/
|
||||
void video_buffer_finish_slot(video_buffer_t *video_buffer, video_decoder_context_t *context);
|
||||
|
||||
/**
|
||||
* video_buffer_wait_for_open_slot:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Blocks until open slot is available.
|
||||
*
|
||||
* Returns true if the buffer has a open slot available.
|
||||
*/
|
||||
bool video_buffer_wait_for_open_slot(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
* video_buffer_wait_for_finished_slot:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Blocks until finished slot is available.
|
||||
*
|
||||
* Returns true if the buffers next slot is finished and a
|
||||
* context available.
|
||||
*/
|
||||
bool video_buffer_wait_for_finished_slot(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
* bool video_buffer_has_open_slot(video_buffer_t *video_buffer)
|
||||
:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Returns true if the buffer has a open slot available.
|
||||
*/
|
||||
bool video_buffer_has_open_slot(video_buffer_t *video_buffer);
|
||||
|
||||
/**
|
||||
* video_buffer_has_finished_slot:
|
||||
* @video_buffer : video buffer.
|
||||
*
|
||||
* Returns true if the buffers next slot is finished and a
|
||||
* context available.
|
||||
*/
|
||||
bool video_buffer_has_finished_slot(video_buffer_t *video_buffer);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user