| /* |
| * 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 <errno.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "common/intops.h" |
| #include "common/validate.h" |
| |
| #include "src/internal.h" |
| #include "src/log.h" |
| #include "src/picture.h" |
| #include "src/ref.h" |
| #include "src/thread.h" |
| #include "src/thread_task.h" |
| |
| int dav1d_default_picture_alloc(Dav1dPicture *const p, void *const cookie) { |
| assert(sizeof(Dav1dMemPoolBuffer) <= DAV1D_PICTURE_ALIGNMENT); |
| const int hbd = p->p.bpc > 8; |
| const int aligned_w = (p->p.w + 127) & ~127; |
| const int aligned_h = (p->p.h + 127) & ~127; |
| const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400; |
| const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420; |
| const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444; |
| ptrdiff_t y_stride = aligned_w << hbd; |
| ptrdiff_t uv_stride = has_chroma ? y_stride >> ss_hor : 0; |
| /* Due to how mapping of addresses to sets works in most L1 and L2 cache |
| * implementations, strides of multiples of certain power-of-two numbers |
| * may cause multiple rows of the same superblock to map to the same set, |
| * causing evictions of previous rows resulting in a reduction in cache |
| * hit rate. Avoid that by slightly padding the stride when necessary. */ |
| if (!(y_stride & 1023)) |
| y_stride += DAV1D_PICTURE_ALIGNMENT; |
| if (!(uv_stride & 1023) && has_chroma) |
| uv_stride += DAV1D_PICTURE_ALIGNMENT; |
| p->stride[0] = y_stride; |
| p->stride[1] = uv_stride; |
| const size_t y_sz = y_stride * aligned_h; |
| const size_t uv_sz = uv_stride * (aligned_h >> ss_ver); |
| const size_t pic_size = y_sz + 2 * uv_sz; |
| |
| Dav1dMemPoolBuffer *const buf = dav1d_mem_pool_pop(cookie, pic_size + |
| DAV1D_PICTURE_ALIGNMENT - |
| sizeof(Dav1dMemPoolBuffer)); |
| if (!buf) return DAV1D_ERR(ENOMEM); |
| p->allocator_data = buf; |
| |
| uint8_t *const data = buf->data; |
| p->data[0] = data; |
| p->data[1] = has_chroma ? data + y_sz : NULL; |
| p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL; |
| |
| return 0; |
| } |
| |
| void dav1d_default_picture_release(Dav1dPicture *const p, void *const cookie) { |
| dav1d_mem_pool_push(cookie, p->allocator_data); |
| } |
| |
| struct pic_ctx_context { |
| struct Dav1dRef *plane_ref[3]; /* MUST BE FIRST */ |
| enum Dav1dPixelLayout layout; |
| void *extra_ptr; /* MUST BE AT THE END */ |
| }; |
| |
| struct plane_ctx_context { |
| Dav1dPicAllocator allocator; |
| Dav1dPicture pic; |
| }; |
| |
| static void free_buffer(const uint8_t *const data, void *const user_data) { |
| struct pic_ctx_context *pic_ctx = user_data; |
| const int planes = pic_ctx->layout != DAV1D_PIXEL_LAYOUT_I400 ? 3 : 1; |
| |
| for (int i = 0; i < planes; i++) |
| dav1d_ref_dec(&pic_ctx->plane_ref[i]); |
| free(pic_ctx); |
| } |
| |
| static void free_plane_buffer(const uint8_t *const data, void *const user_data) { |
| struct plane_ctx_context *plane_ctx = user_data; |
| |
| plane_ctx->allocator.release_picture_callback(&plane_ctx->pic, |
| plane_ctx->allocator.cookie); |
| free(plane_ctx); |
| } |
| |
| static int picture_alloc_with_edges(Dav1dContext *const c, |
| Dav1dPicture *const p, |
| const int w, const int h, |
| Dav1dSequenceHeader *const seq_hdr, Dav1dRef *const seq_hdr_ref, |
| Dav1dFrameHeader *const frame_hdr, Dav1dRef *const frame_hdr_ref, |
| Dav1dContentLightLevel *const content_light, Dav1dRef *const content_light_ref, |
| Dav1dMasteringDisplay *const mastering_display, Dav1dRef *const mastering_display_ref, |
| Dav1dITUTT35 *const itut_t35, Dav1dRef *const itut_t35_ref, |
| const int bpc, |
| const Dav1dDataProps *const props, |
| Dav1dPicAllocator *const p_allocator, |
| const size_t extra, void **const extra_ptr) |
| { |
| if (p->data[0]) { |
| dav1d_log(c, "Picture already allocated!\n"); |
| return -1; |
| } |
| assert(bpc > 0 && bpc <= 16); |
| |
| struct pic_ctx_context *pic_ctx = malloc(extra + sizeof(struct pic_ctx_context)); |
| if (pic_ctx == NULL) |
| return DAV1D_ERR(ENOMEM); |
| memset(pic_ctx, 0, sizeof(struct pic_ctx_context)); |
| |
| p->p.w = w; |
| p->p.h = h; |
| p->seq_hdr = seq_hdr; |
| p->frame_hdr = frame_hdr; |
| p->content_light = content_light; |
| p->mastering_display = mastering_display; |
| p->itut_t35 = itut_t35; |
| p->p.layout = seq_hdr->layout; |
| p->p.bpc = bpc; |
| dav1d_data_props_set_defaults(&p->m); |
| const int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie); |
| if (res < 0) { |
| free(pic_ctx); |
| return res; |
| } |
| |
| pic_ctx->layout = p->p.layout; |
| |
| if (!(p->ref = dav1d_ref_wrap(p->data[0], free_buffer, pic_ctx))) { |
| p_allocator->release_picture_callback(p, p_allocator->cookie); |
| free(pic_ctx); |
| dav1d_log(c, "Failed to wrap picture: %s\n", strerror(errno)); |
| return DAV1D_ERR(ENOMEM); |
| } |
| |
| struct plane_ctx_context *plane_ctx = malloc(sizeof(struct plane_ctx_context)); |
| if (plane_ctx == NULL){ |
| dav1d_ref_dec(&p->ref); |
| p_allocator->release_picture_callback(p, p_allocator->cookie); |
| return DAV1D_ERR(ENOMEM); |
| } |
| |
| plane_ctx->allocator = *p_allocator; |
| plane_ctx->pic = *p; |
| |
| pic_ctx->plane_ref[0] = dav1d_ref_wrap(p->data[0], free_plane_buffer, plane_ctx); |
| if (!pic_ctx->plane_ref[0]) { |
| dav1d_ref_dec(&p->ref); |
| p_allocator->release_picture_callback(p, p_allocator->cookie); |
| free(plane_ctx); |
| dav1d_log(c, "Failed to wrap picture plane: %s\n", strerror(errno)); |
| return DAV1D_ERR(ENOMEM); |
| } |
| |
| const int planes = p->p.layout != DAV1D_PIXEL_LAYOUT_I400 ? 3 : 1; |
| for (int i = 1; i < planes; i++) { |
| pic_ctx->plane_ref[i] = pic_ctx->plane_ref[0]; |
| dav1d_ref_inc(pic_ctx->plane_ref[i]); |
| } |
| |
| p->seq_hdr_ref = seq_hdr_ref; |
| if (seq_hdr_ref) dav1d_ref_inc(seq_hdr_ref); |
| |
| p->frame_hdr_ref = frame_hdr_ref; |
| if (frame_hdr_ref) dav1d_ref_inc(frame_hdr_ref); |
| |
| dav1d_data_props_copy(&p->m, props); |
| |
| if (extra && extra_ptr) |
| *extra_ptr = &pic_ctx->extra_ptr; |
| |
| p->content_light_ref = content_light_ref; |
| if (content_light_ref) dav1d_ref_inc(content_light_ref); |
| |
| p->mastering_display_ref = mastering_display_ref; |
| if (mastering_display_ref) dav1d_ref_inc(mastering_display_ref); |
| |
| p->itut_t35_ref = itut_t35_ref; |
| if (itut_t35_ref) dav1d_ref_inc(itut_t35_ref); |
| |
| return 0; |
| } |
| |
| int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f, |
| const int bpc) |
| { |
| Dav1dThreadPicture *const p = &f->sr_cur; |
| const int have_frame_mt = c->n_fc > 1; |
| |
| const int res = |
| picture_alloc_with_edges(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height, |
| f->seq_hdr, f->seq_hdr_ref, |
| f->frame_hdr, f->frame_hdr_ref, |
| c->content_light, c->content_light_ref, |
| c->mastering_display, c->mastering_display_ref, |
| c->itut_t35, c->itut_t35_ref, |
| bpc, &f->tile[0].data.m, &c->allocator, |
| have_frame_mt ? sizeof(atomic_int) * 2 : 0, |
| (void **) &p->progress); |
| if (res) return res; |
| |
| // Must be removed from the context after being attached to the frame |
| dav1d_ref_dec(&c->itut_t35_ref); |
| c->itut_t35 = NULL; |
| |
| // Don't clear these flags from c->frame_flags if the frame is not visible. |
| // This way they will be added to the next visible frame too. |
| const int flags_mask = (f->frame_hdr->show_frame || c->output_invisible_frames) |
| ? 0 : (PICTURE_FLAG_NEW_SEQUENCE | PICTURE_FLAG_NEW_OP_PARAMS_INFO); |
| p->flags = c->frame_flags; |
| c->frame_flags &= flags_mask; |
| |
| p->visible = f->frame_hdr->show_frame; |
| p->showable = f->frame_hdr->showable_frame; |
| if (have_frame_mt) { |
| atomic_init(&p->progress[0], 0); |
| atomic_init(&p->progress[1], 0); |
| } |
| return res; |
| } |
| |
| int dav1d_picture_alloc_copy(Dav1dContext *const c, Dav1dPicture *const dst, const int w, |
| const Dav1dPicture *const src) |
| { |
| struct pic_ctx_context *const pic_ctx = src->ref->user_data; |
| struct plane_ctx_context *const plane_ctx = pic_ctx->plane_ref[0]->user_data; |
| const int res = picture_alloc_with_edges(c, dst, w, src->p.h, |
| src->seq_hdr, src->seq_hdr_ref, |
| src->frame_hdr, src->frame_hdr_ref, |
| src->content_light, src->content_light_ref, |
| src->mastering_display, src->mastering_display_ref, |
| src->itut_t35, src->itut_t35_ref, |
| src->p.bpc, &src->m, &plane_ctx->allocator, |
| 0, NULL); |
| return res; |
| } |
| |
| void dav1d_picture_ref(Dav1dPicture *const dst, const Dav1dPicture *const src) { |
| validate_input(dst != NULL); |
| validate_input(dst->data[0] == NULL); |
| validate_input(src != NULL); |
| |
| if (src->ref) { |
| validate_input(src->data[0] != NULL); |
| dav1d_ref_inc(src->ref); |
| } |
| if (src->frame_hdr_ref) dav1d_ref_inc(src->frame_hdr_ref); |
| if (src->seq_hdr_ref) dav1d_ref_inc(src->seq_hdr_ref); |
| if (src->m.user_data.ref) dav1d_ref_inc(src->m.user_data.ref); |
| if (src->content_light_ref) dav1d_ref_inc(src->content_light_ref); |
| if (src->mastering_display_ref) dav1d_ref_inc(src->mastering_display_ref); |
| if (src->itut_t35_ref) dav1d_ref_inc(src->itut_t35_ref); |
| *dst = *src; |
| } |
| |
| void dav1d_picture_move_ref(Dav1dPicture *const dst, Dav1dPicture *const src) { |
| validate_input(dst != NULL); |
| validate_input(dst->data[0] == NULL); |
| validate_input(src != NULL); |
| |
| if (src->ref) |
| validate_input(src->data[0] != NULL); |
| |
| *dst = *src; |
| memset(src, 0, sizeof(*src)); |
| } |
| |
| void dav1d_thread_picture_ref(Dav1dThreadPicture *const dst, |
| const Dav1dThreadPicture *const src) |
| { |
| dav1d_picture_ref(&dst->p, &src->p); |
| dst->visible = src->visible; |
| dst->showable = src->showable; |
| dst->progress = src->progress; |
| dst->flags = src->flags; |
| } |
| |
| void dav1d_thread_picture_move_ref(Dav1dThreadPicture *const dst, |
| Dav1dThreadPicture *const src) |
| { |
| dav1d_picture_move_ref(&dst->p, &src->p); |
| dst->visible = src->visible; |
| dst->showable = src->showable; |
| dst->progress = src->progress; |
| dst->flags = src->flags; |
| memset(src, 0, sizeof(*src)); |
| } |
| |
| void dav1d_picture_unref_internal(Dav1dPicture *const p) { |
| validate_input(p != NULL); |
| |
| if (p->ref) { |
| validate_input(p->data[0] != NULL); |
| dav1d_ref_dec(&p->ref); |
| } |
| dav1d_ref_dec(&p->seq_hdr_ref); |
| dav1d_ref_dec(&p->frame_hdr_ref); |
| dav1d_ref_dec(&p->m.user_data.ref); |
| dav1d_ref_dec(&p->content_light_ref); |
| dav1d_ref_dec(&p->mastering_display_ref); |
| dav1d_ref_dec(&p->itut_t35_ref); |
| memset(p, 0, sizeof(*p)); |
| dav1d_data_props_set_defaults(&p->m); |
| } |
| |
| void dav1d_thread_picture_unref(Dav1dThreadPicture *const p) { |
| dav1d_picture_unref_internal(&p->p); |
| |
| p->progress = NULL; |
| } |
| |
| enum Dav1dEventFlags dav1d_picture_get_event_flags(const Dav1dThreadPicture *const p) { |
| if (!p->flags) |
| return 0; |
| |
| enum Dav1dEventFlags flags = 0; |
| if (p->flags & PICTURE_FLAG_NEW_SEQUENCE) |
| flags |= DAV1D_EVENT_FLAG_NEW_SEQUENCE; |
| if (p->flags & PICTURE_FLAG_NEW_OP_PARAMS_INFO) |
| flags |= DAV1D_EVENT_FLAG_NEW_OP_PARAMS_INFO; |
| |
| return flags; |
| } |