0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00
mpv/video/out/dr_helper.c
NRK d05ef7fdc4 various: sort some standard headers
since i was going to fix the include order of stdatomic, might as well
sort the surrouding includes in accordance with the project's coding
style.

some headers can sometime require specific include order. standard
library headers usually don't. but mpv might "hack into" the standard
headers (e.g pthreads) so that complicates things a bit more.

hopefully nothing breaks. if it does, the style guide is to blame.
2023-10-20 21:31:09 +02:00

163 lines
4.6 KiB
C

#include <assert.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <libavutil/buffer.h>
#include "mpv_talloc.h"
#include "misc/dispatch.h"
#include "video/mp_image.h"
#include "dr_helper.h"
struct dr_helper {
pthread_mutex_t thread_lock;
pthread_t thread;
bool thread_valid; // (POSIX defines no "unset" pthread_t value yet)
struct mp_dispatch_queue *dispatch;
atomic_ullong dr_in_flight;
struct mp_image *(*get_image)(void *ctx, int imgfmt, int w, int h,
int stride_align, int flags);
void *get_image_ctx;
};
static void dr_helper_destroy(void *ptr)
{
struct dr_helper *dr = ptr;
// All references must have been freed on destruction, or we'll have
// dangling pointers.
assert(atomic_load(&dr->dr_in_flight) == 0);
pthread_mutex_destroy(&dr->thread_lock);
}
struct dr_helper *dr_helper_create(struct mp_dispatch_queue *dispatch,
struct mp_image *(*get_image)(void *ctx, int imgfmt, int w, int h,
int stride_align, int flags),
void *get_image_ctx)
{
struct dr_helper *dr = talloc_ptrtype(NULL, dr);
talloc_set_destructor(dr, dr_helper_destroy);
*dr = (struct dr_helper){
.dispatch = dispatch,
.dr_in_flight = 0,
.get_image = get_image,
.get_image_ctx = get_image_ctx,
};
pthread_mutex_init(&dr->thread_lock, NULL);
return dr;
}
void dr_helper_acquire_thread(struct dr_helper *dr)
{
pthread_mutex_lock(&dr->thread_lock);
assert(!dr->thread_valid); // fails on API user errors
dr->thread_valid = true;
dr->thread = pthread_self();
pthread_mutex_unlock(&dr->thread_lock);
}
void dr_helper_release_thread(struct dr_helper *dr)
{
pthread_mutex_lock(&dr->thread_lock);
// Fails on API user errors.
assert(dr->thread_valid);
assert(pthread_equal(dr->thread, pthread_self()));
dr->thread_valid = false;
pthread_mutex_unlock(&dr->thread_lock);
}
struct free_dr_context {
struct dr_helper *dr;
AVBufferRef *ref;
};
static void dr_thread_free(void *ptr)
{
struct free_dr_context *ctx = ptr;
unsigned long long v = atomic_fetch_add(&ctx->dr->dr_in_flight, -1);
assert(v); // value before sub is 0 - unexpected underflow.
av_buffer_unref(&ctx->ref);
talloc_free(ctx);
}
static void free_dr_buffer_on_dr_thread(void *opaque, uint8_t *data)
{
struct free_dr_context *ctx = opaque;
struct dr_helper *dr = ctx->dr;
pthread_mutex_lock(&dr->thread_lock);
bool on_this_thread =
dr->thread_valid && pthread_equal(ctx->dr->thread, pthread_self());
pthread_mutex_unlock(&dr->thread_lock);
// The image could be unreffed even on the DR thread. In practice, this
// matters most on DR destruction.
if (on_this_thread) {
dr_thread_free(ctx);
} else {
mp_dispatch_enqueue(dr->dispatch, dr_thread_free, ctx);
}
}
struct get_image_cmd {
struct dr_helper *dr;
int imgfmt, w, h, stride_align, flags;
struct mp_image *res;
};
static void sync_get_image(void *ptr)
{
struct get_image_cmd *cmd = ptr;
struct dr_helper *dr = cmd->dr;
cmd->res = dr->get_image(dr->get_image_ctx, cmd->imgfmt, cmd->w, cmd->h,
cmd->stride_align, cmd->flags);
if (!cmd->res)
return;
// We require exactly 1 AVBufferRef.
assert(cmd->res->bufs[0]);
assert(!cmd->res->bufs[1]);
// Apply some magic to get it free'd on the DR thread as well. For this to
// work, we create a dummy-ref that aliases the original ref, which is why
// the original ref must be writable in the first place. (A newly allocated
// image should be always writable of course.)
assert(mp_image_is_writeable(cmd->res));
struct free_dr_context *ctx = talloc_zero(NULL, struct free_dr_context);
*ctx = (struct free_dr_context){
.dr = dr,
.ref = cmd->res->bufs[0],
};
AVBufferRef *new_ref = av_buffer_create(ctx->ref->data, ctx->ref->size,
free_dr_buffer_on_dr_thread, ctx, 0);
MP_HANDLE_OOM(new_ref);
cmd->res->bufs[0] = new_ref;
atomic_fetch_add(&dr->dr_in_flight, 1);
}
struct mp_image *dr_helper_get_image(struct dr_helper *dr, int imgfmt,
int w, int h, int stride_align, int flags)
{
struct get_image_cmd cmd = {
.dr = dr,
.imgfmt = imgfmt,
.w = w, .h = h,
.stride_align = stride_align,
.flags = flags,
};
mp_dispatch_run(dr->dispatch, sync_get_image, &cmd);
return cmd.res;
}