diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/dav1d/src/lib.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/dav1d/src/lib.c | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/third_party/dav1d/src/lib.c b/third_party/dav1d/src/lib.c new file mode 100644 index 0000000000..eca22ebe03 --- /dev/null +++ b/third_party/dav1d/src/lib.c @@ -0,0 +1,802 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "vcs_version.h" + +#include <errno.h> +#include <string.h> + +#if defined(__linux__) && defined(HAVE_DLSYM) +#include <dlfcn.h> +#endif + +#include "dav1d/dav1d.h" +#include "dav1d/data.h" + +#include "common/validate.h" + +#include "src/cpu.h" +#include "src/fg_apply.h" +#include "src/internal.h" +#include "src/log.h" +#include "src/obu.h" +#include "src/qm.h" +#include "src/ref.h" +#include "src/thread_task.h" +#include "src/wedge.h" + +static COLD void init_internal(void) { + dav1d_init_cpu(); + dav1d_init_interintra_masks(); + dav1d_init_qm_tables(); + dav1d_init_thread(); + dav1d_init_wedge_masks(); +} + +COLD const char *dav1d_version(void) { + return DAV1D_VERSION; +} + +COLD void dav1d_default_settings(Dav1dSettings *const s) { + s->n_threads = 0; + s->max_frame_delay = 0; + s->apply_grain = 1; + s->allocator.cookie = NULL; + s->allocator.alloc_picture_callback = dav1d_default_picture_alloc; + s->allocator.release_picture_callback = dav1d_default_picture_release; + s->logger.cookie = NULL; + s->logger.callback = dav1d_log_default_callback; + s->operating_point = 0; + s->all_layers = 1; // just until the tests are adjusted + s->frame_size_limit = 0; + s->strict_std_compliance = 0; + s->output_invisible_frames = 0; + s->inloop_filters = DAV1D_INLOOPFILTER_ALL; + s->decode_frame_type = DAV1D_DECODEFRAMETYPE_ALL; +} + +static void close_internal(Dav1dContext **const c_out, int flush); + +NO_SANITIZE("cfi-icall") // CFI is broken with dlsym() +static COLD size_t get_stack_size_internal(const pthread_attr_t *const thread_attr) { +#if defined(__linux__) && defined(HAVE_DLSYM) && defined(__GLIBC__) + /* glibc has an issue where the size of the TLS is subtracted from the stack + * size instead of allocated separately. As a result the specified stack + * size may be insufficient when used in an application with large amounts + * of TLS data. The following is a workaround to compensate for that. + * See https://sourceware.org/bugzilla/show_bug.cgi?id=11787 */ + size_t (*const get_minstack)(const pthread_attr_t*) = + dlsym(RTLD_DEFAULT, "__pthread_get_minstack"); + if (get_minstack) + return get_minstack(thread_attr) - PTHREAD_STACK_MIN; +#endif + return 0; +} + +static COLD void get_num_threads(Dav1dContext *const c, const Dav1dSettings *const s, + unsigned *n_tc, unsigned *n_fc) +{ + /* ceil(sqrt(n)) */ + static const uint8_t fc_lut[49] = { + 1, /* 1 */ + 2, 2, 2, /* 2- 4 */ + 3, 3, 3, 3, 3, /* 5- 9 */ + 4, 4, 4, 4, 4, 4, 4, /* 10-16 */ + 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 17-25 */ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 26-36 */ + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 37-49 */ + }; + *n_tc = s->n_threads ? s->n_threads : + iclip(dav1d_num_logical_processors(c), 1, DAV1D_MAX_THREADS); + *n_fc = s->max_frame_delay ? umin(s->max_frame_delay, *n_tc) : + *n_tc < 50 ? fc_lut[*n_tc - 1] : 8; // min(8, ceil(sqrt(n))) +} + +COLD int dav1d_get_frame_delay(const Dav1dSettings *const s) { + unsigned n_tc, n_fc; + validate_input_or_ret(s != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->n_threads >= 0 && + s->n_threads <= DAV1D_MAX_THREADS, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->max_frame_delay >= 0 && + s->max_frame_delay <= DAV1D_MAX_FRAME_DELAY, DAV1D_ERR(EINVAL)); + + get_num_threads(NULL, s, &n_tc, &n_fc); + return n_fc; +} + +COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) { + static pthread_once_t initted = PTHREAD_ONCE_INIT; + pthread_once(&initted, init_internal); + + validate_input_or_ret(c_out != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->n_threads >= 0 && + s->n_threads <= DAV1D_MAX_THREADS, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->max_frame_delay >= 0 && + s->max_frame_delay <= DAV1D_MAX_FRAME_DELAY, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->allocator.alloc_picture_callback != NULL, + DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->allocator.release_picture_callback != NULL, + DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->operating_point >= 0 && + s->operating_point <= 31, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->decode_frame_type >= DAV1D_DECODEFRAMETYPE_ALL && + s->decode_frame_type <= DAV1D_DECODEFRAMETYPE_KEY, DAV1D_ERR(EINVAL)); + + pthread_attr_t thread_attr; + if (pthread_attr_init(&thread_attr)) return DAV1D_ERR(ENOMEM); + size_t stack_size = 1024 * 1024 + get_stack_size_internal(&thread_attr); + + pthread_attr_setstacksize(&thread_attr, stack_size); + + Dav1dContext *const c = *c_out = dav1d_alloc_aligned(sizeof(*c), 64); + if (!c) goto error; + memset(c, 0, sizeof(*c)); + + c->allocator = s->allocator; + c->logger = s->logger; + c->apply_grain = s->apply_grain; + c->operating_point = s->operating_point; + c->all_layers = s->all_layers; + c->frame_size_limit = s->frame_size_limit; + c->strict_std_compliance = s->strict_std_compliance; + c->output_invisible_frames = s->output_invisible_frames; + c->inloop_filters = s->inloop_filters; + c->decode_frame_type = s->decode_frame_type; + + dav1d_data_props_set_defaults(&c->cached_error_props); + + if (dav1d_mem_pool_init(&c->seq_hdr_pool) || + dav1d_mem_pool_init(&c->frame_hdr_pool) || + dav1d_mem_pool_init(&c->segmap_pool) || + dav1d_mem_pool_init(&c->refmvs_pool) || + dav1d_mem_pool_init(&c->cdf_pool)) + { + goto error; + } + + if (c->allocator.alloc_picture_callback == dav1d_default_picture_alloc && + c->allocator.release_picture_callback == dav1d_default_picture_release) + { + if (c->allocator.cookie) goto error; + if (dav1d_mem_pool_init(&c->picture_pool)) goto error; + c->allocator.cookie = c->picture_pool; + } else if (c->allocator.alloc_picture_callback == dav1d_default_picture_alloc || + c->allocator.release_picture_callback == dav1d_default_picture_release) + { + goto error; + } + + /* On 32-bit systems extremely large frame sizes can cause overflows in + * dav1d_decode_frame() malloc size calculations. Prevent that from occuring + * by enforcing a maximum frame size limit, chosen to roughly correspond to + * the largest size possible to decode without exhausting virtual memory. */ + if (sizeof(size_t) < 8 && s->frame_size_limit - 1 >= 8192 * 8192) { + c->frame_size_limit = 8192 * 8192; + if (s->frame_size_limit) + dav1d_log(c, "Frame size limit reduced from %u to %u.\n", + s->frame_size_limit, c->frame_size_limit); + } + + c->flush = &c->flush_mem; + atomic_init(c->flush, 0); + + get_num_threads(c, s, &c->n_tc, &c->n_fc); + + c->fc = dav1d_alloc_aligned(sizeof(*c->fc) * c->n_fc, 32); + if (!c->fc) goto error; + memset(c->fc, 0, sizeof(*c->fc) * c->n_fc); + + c->tc = dav1d_alloc_aligned(sizeof(*c->tc) * c->n_tc, 64); + if (!c->tc) goto error; + memset(c->tc, 0, sizeof(*c->tc) * c->n_tc); + if (c->n_tc > 1) { + if (pthread_mutex_init(&c->task_thread.lock, NULL)) goto error; + if (pthread_cond_init(&c->task_thread.cond, NULL)) { + pthread_mutex_destroy(&c->task_thread.lock); + goto error; + } + if (pthread_cond_init(&c->task_thread.delayed_fg.cond, NULL)) { + pthread_cond_destroy(&c->task_thread.cond); + pthread_mutex_destroy(&c->task_thread.lock); + goto error; + } + c->task_thread.cur = c->n_fc; + atomic_init(&c->task_thread.reset_task_cur, UINT_MAX); + atomic_init(&c->task_thread.cond_signaled, 0); + c->task_thread.inited = 1; + } + + if (c->n_fc > 1) { + c->frame_thread.out_delayed = + calloc(c->n_fc, sizeof(*c->frame_thread.out_delayed)); + if (!c->frame_thread.out_delayed) goto error; + } + for (unsigned n = 0; n < c->n_fc; n++) { + Dav1dFrameContext *const f = &c->fc[n]; + if (c->n_tc > 1) { + if (pthread_mutex_init(&f->task_thread.lock, NULL)) goto error; + if (pthread_cond_init(&f->task_thread.cond, NULL)) { + pthread_mutex_destroy(&f->task_thread.lock); + goto error; + } + if (pthread_mutex_init(&f->task_thread.pending_tasks.lock, NULL)) { + pthread_cond_destroy(&f->task_thread.cond); + pthread_mutex_destroy(&f->task_thread.lock); + goto error; + } + } + f->c = c; + f->task_thread.ttd = &c->task_thread; + f->lf.last_sharpness = -1; + dav1d_refmvs_init(&f->rf); + } + + for (unsigned m = 0; m < c->n_tc; m++) { + Dav1dTaskContext *const t = &c->tc[m]; + t->f = &c->fc[0]; + t->task_thread.ttd = &c->task_thread; + t->c = c; + memset(t->cf_16bpc, 0, sizeof(t->cf_16bpc)); + if (c->n_tc > 1) { + if (pthread_mutex_init(&t->task_thread.td.lock, NULL)) goto error; + if (pthread_cond_init(&t->task_thread.td.cond, NULL)) { + pthread_mutex_destroy(&t->task_thread.td.lock); + goto error; + } + if (pthread_create(&t->task_thread.td.thread, &thread_attr, dav1d_worker_task, t)) { + pthread_cond_destroy(&t->task_thread.td.cond); + pthread_mutex_destroy(&t->task_thread.td.lock); + goto error; + } + t->task_thread.td.inited = 1; + } + } + dav1d_refmvs_dsp_init(&c->refmvs_dsp); + + // intra edge tree + c->intra_edge.root[BL_128X128] = &c->intra_edge.branch_sb128[0].node; + dav1d_init_mode_tree(c->intra_edge.root[BL_128X128], c->intra_edge.tip_sb128, 1); + c->intra_edge.root[BL_64X64] = &c->intra_edge.branch_sb64[0].node; + dav1d_init_mode_tree(c->intra_edge.root[BL_64X64], c->intra_edge.tip_sb64, 0); + + pthread_attr_destroy(&thread_attr); + + return 0; + +error: + if (c) close_internal(c_out, 0); + pthread_attr_destroy(&thread_attr); + return DAV1D_ERR(ENOMEM); +} + +static void dummy_free(const uint8_t *const data, void *const user_data) { + assert(data && !user_data); +} + +int dav1d_parse_sequence_header(Dav1dSequenceHeader *const out, + const uint8_t *const ptr, const size_t sz) +{ + Dav1dData buf = { 0 }; + int res; + + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + + Dav1dSettings s; + dav1d_default_settings(&s); + s.n_threads = 1; + s.logger.callback = NULL; + + Dav1dContext *c; + res = dav1d_open(&c, &s); + if (res < 0) return res; + + if (ptr) { + res = dav1d_data_wrap_internal(&buf, ptr, sz, dummy_free, NULL); + if (res < 0) goto error; + } + + while (buf.sz > 0) { + res = dav1d_parse_obus(c, &buf, 1); + if (res < 0) goto error; + + assert((size_t)res <= buf.sz); + buf.sz -= res; + buf.data += res; + } + + if (!c->seq_hdr) { + res = DAV1D_ERR(ENOENT); + goto error; + } + + memcpy(out, c->seq_hdr, sizeof(*out)); + + res = 0; +error: + dav1d_data_unref_internal(&buf); + dav1d_close(&c); + + return res; +} + +static int has_grain(const Dav1dPicture *const pic) +{ + const Dav1dFilmGrainData *fgdata = &pic->frame_hdr->film_grain.data; + return fgdata->num_y_points || fgdata->num_uv_points[0] || + fgdata->num_uv_points[1] || (fgdata->clip_to_restricted_range && + fgdata->chroma_scaling_from_luma); +} + +static int output_image(Dav1dContext *const c, Dav1dPicture *const out) +{ + int res = 0; + + Dav1dThreadPicture *const in = (c->all_layers || !c->max_spatial_id) + ? &c->out : &c->cache; + if (!c->apply_grain || !has_grain(&in->p)) { + dav1d_picture_move_ref(out, &in->p); + dav1d_thread_picture_unref(in); + goto end; + } + + res = dav1d_apply_grain(c, out, &in->p); + dav1d_thread_picture_unref(in); +end: + if (!c->all_layers && c->max_spatial_id && c->out.p.data[0]) { + dav1d_thread_picture_move_ref(in, &c->out); + } + return res; +} + +static int output_picture_ready(Dav1dContext *const c, const int drain) { + if (c->cached_error) return 1; + if (!c->all_layers && c->max_spatial_id) { + if (c->out.p.data[0] && c->cache.p.data[0]) { + if (c->max_spatial_id == c->cache.p.frame_hdr->spatial_id || + c->out.flags & PICTURE_FLAG_NEW_TEMPORAL_UNIT) + return 1; + dav1d_thread_picture_unref(&c->cache); + dav1d_thread_picture_move_ref(&c->cache, &c->out); + return 0; + } else if (c->cache.p.data[0] && drain) { + return 1; + } else if (c->out.p.data[0]) { + dav1d_thread_picture_move_ref(&c->cache, &c->out); + return 0; + } + } + + return !!c->out.p.data[0]; +} + +static int drain_picture(Dav1dContext *const c, Dav1dPicture *const out) { + unsigned drain_count = 0; + int drained = 0; + do { + const unsigned next = c->frame_thread.next; + Dav1dFrameContext *const f = &c->fc[next]; + pthread_mutex_lock(&c->task_thread.lock); + while (f->n_tile_data > 0) + pthread_cond_wait(&f->task_thread.cond, + &f->task_thread.ttd->lock); + Dav1dThreadPicture *const out_delayed = + &c->frame_thread.out_delayed[next]; + if (out_delayed->p.data[0] || atomic_load(&f->task_thread.error)) { + unsigned first = atomic_load(&c->task_thread.first); + if (first + 1U < c->n_fc) + atomic_fetch_add(&c->task_thread.first, 1U); + else + atomic_store(&c->task_thread.first, 0); + atomic_compare_exchange_strong(&c->task_thread.reset_task_cur, + &first, UINT_MAX); + if (c->task_thread.cur && c->task_thread.cur < c->n_fc) + c->task_thread.cur--; + drained = 1; + } else if (drained) { + pthread_mutex_unlock(&c->task_thread.lock); + break; + } + if (++c->frame_thread.next == c->n_fc) + c->frame_thread.next = 0; + pthread_mutex_unlock(&c->task_thread.lock); + const int error = f->task_thread.retval; + if (error) { + f->task_thread.retval = 0; + dav1d_data_props_copy(&c->cached_error_props, &out_delayed->p.m); + dav1d_thread_picture_unref(out_delayed); + return error; + } + if (out_delayed->p.data[0]) { + const unsigned progress = + atomic_load_explicit(&out_delayed->progress[1], + memory_order_relaxed); + if ((out_delayed->visible || c->output_invisible_frames) && + progress != FRAME_ERROR) + { + dav1d_thread_picture_ref(&c->out, out_delayed); + c->event_flags |= dav1d_picture_get_event_flags(out_delayed); + } + dav1d_thread_picture_unref(out_delayed); + if (output_picture_ready(c, 0)) + return output_image(c, out); + } + } while (++drain_count < c->n_fc); + + if (output_picture_ready(c, 1)) + return output_image(c, out); + + return DAV1D_ERR(EAGAIN); +} + +static int gen_picture(Dav1dContext *const c) +{ + int res; + Dav1dData *const in = &c->in; + + if (output_picture_ready(c, 0)) + return 0; + + while (in->sz > 0) { + res = dav1d_parse_obus(c, in, 0); + if (res < 0) { + dav1d_data_unref_internal(in); + } else { + assert((size_t)res <= in->sz); + in->sz -= res; + in->data += res; + if (!in->sz) dav1d_data_unref_internal(in); + } + if (output_picture_ready(c, 0)) + break; + if (res < 0) + return res; + } + + return 0; +} + +int dav1d_send_data(Dav1dContext *const c, Dav1dData *const in) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(in != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(in->data == NULL || in->sz, DAV1D_ERR(EINVAL)); + + if (in->data) + c->drain = 0; + if (c->in.data) + return DAV1D_ERR(EAGAIN); + dav1d_data_ref(&c->in, in); + + int res = gen_picture(c); + if (!res) + dav1d_data_unref_internal(in); + + return res; +} + +int dav1d_get_picture(Dav1dContext *const c, Dav1dPicture *const out) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + + const int drain = c->drain; + c->drain = 1; + + int res = gen_picture(c); + if (res < 0) + return res; + + if (c->cached_error) { + const int res = c->cached_error; + c->cached_error = 0; + return res; + } + + if (output_picture_ready(c, c->n_fc == 1)) + return output_image(c, out); + + if (c->n_fc > 1 && drain) + return drain_picture(c, out); + + return DAV1D_ERR(EAGAIN); +} + +int dav1d_apply_grain(Dav1dContext *const c, Dav1dPicture *const out, + const Dav1dPicture *const in) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(in != NULL, DAV1D_ERR(EINVAL)); + + if (!has_grain(in)) { + dav1d_picture_ref(out, in); + return 0; + } + + int res = dav1d_picture_alloc_copy(c, out, in->p.w, in); + if (res < 0) goto error; + + if (c->n_tc > 1) { + dav1d_task_delayed_fg(c, out, in); + } else { + switch (out->p.bpc) { +#if CONFIG_8BPC + case 8: + dav1d_apply_grain_8bpc(&c->dsp[0].fg, out, in); + break; +#endif +#if CONFIG_16BPC + case 10: + case 12: + dav1d_apply_grain_16bpc(&c->dsp[(out->p.bpc >> 1) - 4].fg, out, in); + break; +#endif + default: abort(); + } + } + + return 0; + +error: + dav1d_picture_unref_internal(out); + return res; +} + +void dav1d_flush(Dav1dContext *const c) { + dav1d_data_unref_internal(&c->in); + if (c->out.p.frame_hdr) + dav1d_thread_picture_unref(&c->out); + if (c->cache.p.frame_hdr) + dav1d_thread_picture_unref(&c->cache); + + c->drain = 0; + c->cached_error = 0; + + for (int i = 0; i < 8; i++) { + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_ref_dec(&c->refs[i].segmap); + dav1d_ref_dec(&c->refs[i].refmvs); + dav1d_cdf_thread_unref(&c->cdf[i]); + } + c->frame_hdr = NULL; + c->seq_hdr = NULL; + dav1d_ref_dec(&c->seq_hdr_ref); + + c->mastering_display = NULL; + c->content_light = NULL; + c->itut_t35 = NULL; + dav1d_ref_dec(&c->mastering_display_ref); + dav1d_ref_dec(&c->content_light_ref); + dav1d_ref_dec(&c->itut_t35_ref); + + dav1d_data_props_unref_internal(&c->cached_error_props); + + if (c->n_fc == 1 && c->n_tc == 1) return; + atomic_store(c->flush, 1); + + // stop running tasks in worker threads + if (c->n_tc > 1) { + pthread_mutex_lock(&c->task_thread.lock); + for (unsigned i = 0; i < c->n_tc; i++) { + Dav1dTaskContext *const tc = &c->tc[i]; + while (!tc->task_thread.flushed) { + pthread_cond_wait(&tc->task_thread.td.cond, &c->task_thread.lock); + } + } + for (unsigned i = 0; i < c->n_fc; i++) { + c->fc[i].task_thread.task_head = NULL; + c->fc[i].task_thread.task_tail = NULL; + c->fc[i].task_thread.task_cur_prev = NULL; + c->fc[i].task_thread.pending_tasks.head = NULL; + c->fc[i].task_thread.pending_tasks.tail = NULL; + atomic_init(&c->fc[i].task_thread.pending_tasks.merge, 0); + } + atomic_init(&c->task_thread.first, 0); + c->task_thread.cur = c->n_fc; + atomic_store(&c->task_thread.reset_task_cur, UINT_MAX); + atomic_store(&c->task_thread.cond_signaled, 0); + pthread_mutex_unlock(&c->task_thread.lock); + } + + // wait for threads to complete flushing + if (c->n_fc > 1) { + for (unsigned n = 0, next = c->frame_thread.next; n < c->n_fc; n++, next++) { + if (next == c->n_fc) next = 0; + Dav1dFrameContext *const f = &c->fc[next]; + dav1d_decode_frame_exit(f, -1); + f->n_tile_data = 0; + f->task_thread.retval = 0; + Dav1dThreadPicture *out_delayed = &c->frame_thread.out_delayed[next]; + if (out_delayed->p.frame_hdr) { + dav1d_thread_picture_unref(out_delayed); + } + } + c->frame_thread.next = 0; + } + atomic_store(c->flush, 0); +} + +COLD void dav1d_close(Dav1dContext **const c_out) { + validate_input(c_out != NULL); + close_internal(c_out, 1); +} + +static COLD void close_internal(Dav1dContext **const c_out, int flush) { + Dav1dContext *const c = *c_out; + if (!c) return; + + if (flush) dav1d_flush(c); + + if (c->tc) { + struct TaskThreadData *ttd = &c->task_thread; + if (ttd->inited) { + pthread_mutex_lock(&ttd->lock); + for (unsigned n = 0; n < c->n_tc && c->tc[n].task_thread.td.inited; n++) + c->tc[n].task_thread.die = 1; + pthread_cond_broadcast(&ttd->cond); + pthread_mutex_unlock(&ttd->lock); + for (unsigned n = 0; n < c->n_tc; n++) { + Dav1dTaskContext *const pf = &c->tc[n]; + if (!pf->task_thread.td.inited) break; + pthread_join(pf->task_thread.td.thread, NULL); + pthread_cond_destroy(&pf->task_thread.td.cond); + pthread_mutex_destroy(&pf->task_thread.td.lock); + } + pthread_cond_destroy(&ttd->delayed_fg.cond); + pthread_cond_destroy(&ttd->cond); + pthread_mutex_destroy(&ttd->lock); + } + dav1d_free_aligned(c->tc); + } + + for (unsigned n = 0; c->fc && n < c->n_fc; n++) { + Dav1dFrameContext *const f = &c->fc[n]; + + // clean-up threading stuff + if (c->n_fc > 1) { + freep(&f->tile_thread.lowest_pixel_mem); + freep(&f->frame_thread.b); + dav1d_freep_aligned(&f->frame_thread.pal_idx); + dav1d_freep_aligned(&f->frame_thread.cf); + freep(&f->frame_thread.tile_start_off); + dav1d_freep_aligned(&f->frame_thread.pal); + freep(&f->frame_thread.cbi); + } + if (c->n_tc > 1) { + pthread_mutex_destroy(&f->task_thread.pending_tasks.lock); + pthread_cond_destroy(&f->task_thread.cond); + pthread_mutex_destroy(&f->task_thread.lock); + } + freep(&f->frame_thread.frame_progress); + freep(&f->task_thread.tasks); + freep(&f->task_thread.tile_tasks[0]); + dav1d_free_aligned(f->ts); + dav1d_free_aligned(f->ipred_edge[0]); + free(f->a); + free(f->tile); + free(f->lf.mask); + free(f->lf.lr_mask); + free(f->lf.level); + free(f->lf.tx_lpf_right_edge[0]); + free(f->lf.start_of_tile_row); + dav1d_refmvs_clear(&f->rf); + dav1d_free_aligned(f->lf.cdef_line_buf); + dav1d_free_aligned(f->lf.lr_line_buf); + } + dav1d_free_aligned(c->fc); + if (c->n_fc > 1 && c->frame_thread.out_delayed) { + for (unsigned n = 0; n < c->n_fc; n++) + if (c->frame_thread.out_delayed[n].p.frame_hdr) + dav1d_thread_picture_unref(&c->frame_thread.out_delayed[n]); + free(c->frame_thread.out_delayed); + } + for (int n = 0; n < c->n_tile_data; n++) + dav1d_data_unref_internal(&c->tile[n].data); + free(c->tile); + for (int n = 0; n < 8; n++) { + dav1d_cdf_thread_unref(&c->cdf[n]); + if (c->refs[n].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[n].p); + dav1d_ref_dec(&c->refs[n].refmvs); + dav1d_ref_dec(&c->refs[n].segmap); + } + dav1d_ref_dec(&c->seq_hdr_ref); + dav1d_ref_dec(&c->frame_hdr_ref); + + dav1d_ref_dec(&c->mastering_display_ref); + dav1d_ref_dec(&c->content_light_ref); + dav1d_ref_dec(&c->itut_t35_ref); + + dav1d_mem_pool_end(c->seq_hdr_pool); + dav1d_mem_pool_end(c->frame_hdr_pool); + dav1d_mem_pool_end(c->segmap_pool); + dav1d_mem_pool_end(c->refmvs_pool); + dav1d_mem_pool_end(c->cdf_pool); + dav1d_mem_pool_end(c->picture_pool); + + dav1d_freep_aligned(c_out); +} + +int dav1d_get_event_flags(Dav1dContext *const c, enum Dav1dEventFlags *const flags) { + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(flags != NULL, DAV1D_ERR(EINVAL)); + + *flags = c->event_flags; + c->event_flags = 0; + return 0; +} + +int dav1d_get_decode_error_data_props(Dav1dContext *const c, Dav1dDataProps *const out) { + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + + dav1d_data_props_unref_internal(out); + *out = c->cached_error_props; + dav1d_data_props_set_defaults(&c->cached_error_props); + + return 0; +} + +void dav1d_picture_unref(Dav1dPicture *const p) { + dav1d_picture_unref_internal(p); +} + +uint8_t *dav1d_data_create(Dav1dData *const buf, const size_t sz) { + return dav1d_data_create_internal(buf, sz); +} + +int dav1d_data_wrap(Dav1dData *const buf, const uint8_t *const ptr, + const size_t sz, + void (*const free_callback)(const uint8_t *data, + void *user_data), + void *const user_data) +{ + return dav1d_data_wrap_internal(buf, ptr, sz, free_callback, user_data); +} + +int dav1d_data_wrap_user_data(Dav1dData *const buf, + const uint8_t *const user_data, + void (*const free_callback)(const uint8_t *user_data, + void *cookie), + void *const cookie) +{ + return dav1d_data_wrap_user_data_internal(buf, + user_data, + free_callback, + cookie); +} + +void dav1d_data_unref(Dav1dData *const buf) { + dav1d_data_unref_internal(buf); +} + +void dav1d_data_props_unref(Dav1dDataProps *const props) { + dav1d_data_props_unref_internal(props); +} |