Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163#include <assert.h>#include <stdatomic.h>#include <stdlib.h>
#include <libavutil/buffer.h>
#include "misc/dispatch.h"#include "mpv_talloc.h"#include "osdep/threads.h"#include "video/mp_image.h"
#include "dr_helper.h"
struct dr_helper { mp_mutex thread_lock; mp_thread_id thread_id; bool thread_valid; // (POSIX defines no "unset" mp_thread 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);
mp_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, }; mp_mutex_init(&dr->thread_lock); return dr;}
void dr_helper_acquire_thread(struct dr_helper *dr){ mp_mutex_lock(&dr->thread_lock); assert(!dr->thread_valid); // fails on API user errors dr->thread_valid = true; dr->thread_id = mp_thread_current_id(); mp_mutex_unlock(&dr->thread_lock);}
void dr_helper_release_thread(struct dr_helper *dr){ mp_mutex_lock(&dr->thread_lock); // Fails on API user errors. assert(dr->thread_valid); assert(mp_thread_id_equal(dr->thread_id, mp_thread_current_id())); dr->thread_valid = false; mp_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;
mp_mutex_lock(&dr->thread_lock); bool on_this_thread = dr->thread_valid && mp_thread_id_equal(ctx->dr->thread_id, mp_thread_current_id()); mp_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;}