picture.c 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * 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 <assert.h>
#include <errno.h>
32
#include <stdint.h>
33 34 35 36 37 38 39 40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common/intops.h"
#include "common/mem.h"
#include "common/validate.h"

41
#include "src/internal.h"
42
#include "src/log.h"
43 44
#include "src/picture.h"
#include "src/ref.h"
45
#include "src/thread.h"
46
#include "src/thread_task.h"
47

48 49 50 51 52 53 54 55 56 57 58 59 60 61
int default_picture_allocator(Dav1dPicture *const p, void *cookie) {
    assert(cookie == NULL);
    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;
    p->stride[0] = aligned_w << hbd;
    p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
    const size_t y_sz = p->stride[0] * aligned_h;
    const size_t uv_sz = p->stride[1] * (aligned_h >> ss_ver);
    const size_t pic_size = y_sz + 2 * uv_sz;

62 63
    uint8_t *data = dav1d_alloc_aligned(pic_size + DAV1D_PICTURE_ALIGNMENT,
                                        DAV1D_PICTURE_ALIGNMENT);
64
    if (data == NULL) {
65
        return -ENOMEM;
66 67 68 69 70 71 72 73 74 75 76 77 78
    }

    p->data[0] = data;
    p->data[1] = has_chroma ? data + y_sz : NULL;
    p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;

#ifndef NDEBUG /* safety check */
    p->allocator_data = data;
#endif

    return 0;
}

79
void default_picture_release(Dav1dPicture *const p, void *cookie) {
80 81
    assert(cookie == NULL);
#ifndef NDEBUG /* safety check */
82
    assert(p->allocator_data == p->data[0]);
83
#endif
84
    dav1d_free_aligned(p->data[0]);
85 86 87 88
}

struct pic_ctx_context {
    Dav1dPicAllocator allocator;
89
    Dav1dPicture pic;
90 91 92
    void *extra_ptr; /* MUST BE AT THE END */
};

93
static void free_buffer(const uint8_t *const data, void *const user_data) {
94 95
    struct pic_ctx_context *pic_ctx = user_data;

96
    pic_ctx->allocator.release_picture_callback(&pic_ctx->pic,
97 98 99 100
                                                pic_ctx->allocator.cookie);
    free(pic_ctx);
}

101
static int picture_alloc_with_edges(Dav1dContext *const c, Dav1dPicture *const p,
102
                                    const int w, const int h,
103 104
                                    Dav1dSequenceHeader *seq_hdr, Dav1dRef *seq_hdr_ref,
                                    Dav1dFrameHeader *frame_hdr,  Dav1dRef *frame_hdr_ref,
105 106
                                    Dav1dContentLightLevel *content_light, Dav1dRef *content_light_ref,
                                    Dav1dMasteringDisplay *mastering_display, Dav1dRef *mastering_display_ref,
107
                                    const int bpc, const Dav1dDataProps *props,
108 109
                                    Dav1dPicAllocator *const p_allocator,
                                    const size_t extra, void **const extra_ptr)
110 111
{
    if (p->data[0]) {
112
        dav1d_log(c, "Picture already allocated!\n");
113 114 115 116
        return -1;
    }
    assert(bpc > 0 && bpc <= 16);

117 118 119 120 121
    struct pic_ctx_context *pic_ctx = malloc(extra + sizeof(struct pic_ctx_context));
    if (pic_ctx == NULL) {
        return -ENOMEM;
    }

122 123
    p->p.w = w;
    p->p.h = h;
124 125
    p->seq_hdr = seq_hdr;
    p->frame_hdr = frame_hdr;
126 127
    p->content_light = content_light;
    p->mastering_display = mastering_display;
128
    p->p.layout = seq_hdr->layout;
129
    p->p.bpc = bpc;
130
    dav1d_data_props_set_defaults(&p->m);
131 132 133
    int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie);
    if (res < 0) {
        free(pic_ctx);
134
        return res;
135 136 137
    }

    pic_ctx->allocator = *p_allocator;
138
    pic_ctx->pic = *p;
139 140

    if (!(p->ref = dav1d_ref_wrap(p->data[0], free_buffer, pic_ctx))) {
141
        p_allocator->release_picture_callback(p, p_allocator->cookie);
142
        free(pic_ctx);
143
        dav1d_log(c, "Failed to wrap picture: %s\n", strerror(errno));
144 145 146
        return -ENOMEM;
    }

147 148 149 150 151 152 153 154
    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);

155 156
    if (extra && extra_ptr)
        *extra_ptr = &pic_ctx->extra_ptr;
157

158 159
    p->content_light_ref = content_light_ref;
    if (content_light_ref) dav1d_ref_inc(content_light_ref);
160

161 162
    p->mastering_display_ref = mastering_display_ref;
    if (mastering_display_ref) dav1d_ref_inc(mastering_display_ref);
163

164 165 166
    return 0;
}

167 168
int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f,
                               const int bpc)
169
{
170 171
    Dav1dThreadPicture *const p = &f->sr_cur;
    p->t = c->n_fc > 1 ? &f->frame_thread.td : NULL;
172 173

    const int res =
174 175 176
        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,
177 178
                                 c->content_light, c->content_light_ref,
                                 c->mastering_display, c->mastering_display_ref,
179 180
                                 bpc, &f->tile[0].data.m, &c->allocator,
                                 p->t != NULL ? sizeof(atomic_int) * 2 : 0,
181
                                 (void **) &p->progress);
182
    if (res) return res;
183

184 185
    p->visible = f->frame_hdr->show_frame;
    if (p->t) {
186 187 188 189 190 191
        atomic_init(&p->progress[0], 0);
        atomic_init(&p->progress[1], 0);
    }
    return res;
}

192
int dav1d_picture_alloc_copy(Dav1dContext *const c, Dav1dPicture *const dst, const int w,
193 194 195
                             const Dav1dPicture *const src)
{
    struct pic_ctx_context *const pic_ctx = src->ref->user_data;
196
    const int res = picture_alloc_with_edges(c, dst, w, src->p.h,
197 198
                                             src->seq_hdr, src->seq_hdr_ref,
                                             src->frame_hdr, src->frame_hdr_ref,
199 200
                                             src->content_light, src->content_light_ref,
                                             src->mastering_display, src->mastering_display_ref,
201
                                             src->p.bpc, &src->m, &pic_ctx->allocator,
202
                                             0, NULL);
203 204 205
    return res;
}

206 207 208 209 210 211 212 213
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);
214 215
        if (src->frame_hdr_ref) dav1d_ref_inc(src->frame_hdr_ref);
        if (src->seq_hdr_ref) dav1d_ref_inc(src->seq_hdr_ref);
216
        if (src->m.user_data.ref) dav1d_ref_inc(src->m.user_data.ref);
217 218
        if (src->content_light_ref) dav1d_ref_inc(src->content_light_ref);
        if (src->mastering_display_ref) dav1d_ref_inc(src->mastering_display_ref);
219 220 221 222
    }
    *dst = *src;
}

223 224 225 226 227 228 229 230 231 232 233 234
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));
}

235 236 237 238 239 240 241 242 243
void dav1d_thread_picture_ref(Dav1dThreadPicture *dst,
                              const Dav1dThreadPicture *src)
{
    dav1d_picture_ref(&dst->p, &src->p);
    dst->t = src->t;
    dst->visible = src->visible;
    dst->progress = src->progress;
}

244
void dav1d_picture_unref_internal(Dav1dPicture *const p) {
245 246 247 248
    validate_input(p != NULL);

    if (p->ref) {
        validate_input(p->data[0] != NULL);
249
        dav1d_ref_dec(&p->ref);
250 251
        dav1d_ref_dec(&p->seq_hdr_ref);
        dav1d_ref_dec(&p->frame_hdr_ref);
252
        dav1d_ref_dec(&p->m.user_data.ref);
253 254
        dav1d_ref_dec(&p->content_light_ref);
        dav1d_ref_dec(&p->mastering_display_ref);
255 256 257 258 259
    }
    memset(p, 0, sizeof(*p));
}

void dav1d_thread_picture_unref(Dav1dThreadPicture *const p) {
260
    dav1d_picture_unref_internal(&p->p);
261 262 263 264 265

    p->t = NULL;
    p->progress = NULL;
}

266 267
int dav1d_thread_picture_wait(const Dav1dThreadPicture *const p,
                              int y_unclipped, const enum PlaneType plane_type)
268 269 270 271
{
    assert(plane_type != PLANE_TYPE_ALL);

    if (!p->t)
272
        return 0;
273 274

    // convert to luma units; include plane delay from loopfilters; clip
275
    const int ss_ver = p->p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
276
    y_unclipped *= 1 << (plane_type & ss_ver); // we rely here on PLANE_TYPE_UV being 1
277
    y_unclipped += (plane_type != PLANE_TYPE_BLOCK) * 8; // delay imposed by loopfilter
skal's avatar
skal committed
278
    const unsigned y = iclip(y_unclipped, 1, p->p.p.h);
279
    atomic_uint *const progress = &p->progress[plane_type != PLANE_TYPE_BLOCK];
280
    unsigned state;
281

282 283
    if ((state = atomic_load_explicit(progress, memory_order_acquire)) >= y)
        return state == FRAME_ERROR;
284 285

    pthread_mutex_lock(&p->t->lock);
286
    while ((state = atomic_load_explicit(progress, memory_order_relaxed)) < y)
287 288
        pthread_cond_wait(&p->t->cond, &p->t->lock);
    pthread_mutex_unlock(&p->t->lock);
289
    return state == FRAME_ERROR;
290 291 292 293 294 295 296 297 298 299 300 301
}

void dav1d_thread_picture_signal(const Dav1dThreadPicture *const p,
                                 const int y, // in pixel units
                                 const enum PlaneType plane_type)
{
    assert(plane_type != PLANE_TYPE_UV);

    if (!p->t)
        return;

    pthread_mutex_lock(&p->t->lock);
302 303 304 305
    if (plane_type != PLANE_TYPE_Y)
        atomic_store(&p->progress[0], y);
    if (plane_type != PLANE_TYPE_BLOCK)
        atomic_store(&p->progress[1], y);
306 307 308
    pthread_cond_broadcast(&p->t->cond);
    pthread_mutex_unlock(&p->t->lock);
}