diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
commit | 51de1d8436100f725f3576aefa24a2bd2057bc28 (patch) | |
tree | c6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /video/out/dr_helper.c | |
parent | Initial commit. (diff) | |
download | mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip |
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'video/out/dr_helper.c')
-rw-r--r-- | video/out/dr_helper.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/video/out/dr_helper.c b/video/out/dr_helper.c new file mode 100644 index 0000000..ac440a7 --- /dev/null +++ b/video/out/dr_helper.c @@ -0,0 +1,162 @@ +#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; +} |