| @@ -0,0 +1,270 @@ |
| 1 | +/* |
| 2 | + * Copyright (c) 2018 Ronald S. Bultje <rsbultje gmail com> |
| 3 | + * Copyright (c) 2018 James Almer <jamrial gmail com> |
| 4 | + * |
| 5 | + * This file is part of FFmpeg. |
| 6 | + * |
| 7 | + * FFmpeg is free software; you can redistribute it and/or |
| 8 | + * modify it under the terms of the GNU Lesser General Public |
| 9 | + * License as published by the Free Software Foundation; either |
| 10 | + * version 2.1 of the License, or (at your option) any later version. |
| 11 | + * |
| 12 | + * FFmpeg is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | + * Lesser General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU Lesser General Public |
| 18 | + * License along with FFmpeg; if not, write to the Free Software |
| 19 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | + */ |
| 21 | + |
| 22 | +#include <dav1d/dav1d.h> |
| 23 | + |
| 24 | +#include "libavutil/avassert.h" |
| 25 | +#include "libavutil/fifo.h" |
| 26 | +#include "libavutil/opt.h" |
| 27 | + |
| 28 | +#include "avcodec.h" |
| 29 | +#include "decode.h" |
| 30 | +#include "internal.h" |
| 31 | + |
| 32 | +typedef struct Libdav1dContext { |
| 33 | + AVClass *class; |
| 34 | + Dav1dContext *c; |
| 35 | + |
| 36 | + AVFifoBuffer *cache; |
| 37 | + Dav1dData data; |
| 38 | + int tile_threads; |
| 39 | +} Libdav1dContext; |
| 40 | + |
| 41 | +static av_cold int libdav1d_init(AVCodecContext *c) |
| 42 | +{ |
| 43 | + Libdav1dContext *dav1d = c->priv_data; |
| 44 | + Dav1dSettings s; |
| 45 | + int res; |
| 46 | + |
| 47 | + av_log(c, AV_LOG_INFO, "libdav1d %s\n", dav1d_version()); |
| 48 | + |
| 49 | + dav1d_default_settings(&s); |
| 50 | + s.n_tile_threads = dav1d->tile_threads; |
| 51 | + s.n_frame_threads = FFMIN(c->thread_count ? c->thread_count : av_cpu_count(), 256); |
| 52 | + |
| 53 | + dav1d->cache = av_fifo_alloc(8 * sizeof(AVPacket)); |
| 54 | + if (!dav1d->cache) |
| 55 | + return AVERROR(ENOMEM); |
| 56 | + |
| 57 | + res = dav1d_open(&dav1d->c, &s); |
| 58 | + if (res < 0) |
| 59 | + return AVERROR(ENOMEM); |
| 60 | + |
| 61 | + return 0; |
| 62 | +} |
| 63 | + |
| 64 | +static void libdav1d_flush(AVCodecContext *c) |
| 65 | +{ |
| 66 | + Libdav1dContext *dav1d = c->priv_data; |
| 67 | + |
| 68 | + av_fifo_reset(dav1d->cache); |
| 69 | + dav1d_data_unref(&dav1d->data); |
| 70 | + dav1d_flush(dav1d->c); |
| 71 | +} |
| 72 | + |
| 73 | +static int libdav1d_fifo_write(void *src, void *dst, int dst_size) { |
| 74 | + AVPacket *pkt_dst = dst, *pkt_src = src; |
| 75 | + |
| 76 | + av_assert2(dst_size >= sizeof(AVPacket)); |
| 77 | + |
| 78 | + pkt_src->buf = NULL; |
| 79 | + av_packet_free_side_data(pkt_src); |
| 80 | + *pkt_dst = *pkt_src; |
| 81 | + |
| 82 | + return sizeof(AVPacket); |
| 83 | +} |
| 84 | + |
| 85 | +static void libdav1d_data_free(const uint8_t *data, void *opaque) { |
| 86 | + AVBufferRef *buf = opaque; |
| 87 | + |
| 88 | + av_buffer_unref(&buf); |
| 89 | +} |
| 90 | + |
| 91 | +static void libdav1d_frame_free(void *opaque, uint8_t *data) { |
| 92 | + Dav1dPicture p = { 0 }; |
| 93 | + |
| 94 | + p.ref = opaque; |
| 95 | + p.data[0] = (void *) 0x1; // this has to be non-NULL |
| 96 | + dav1d_picture_unref(&p); |
| 97 | +} |
| 98 | + |
| 99 | +static const enum AVPixelFormat pix_fmt[][2] = { |
| 100 | + [DAV1D_PIXEL_LAYOUT_I400] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10 }, |
| 101 | + [DAV1D_PIXEL_LAYOUT_I420] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10 }, |
| 102 | + [DAV1D_PIXEL_LAYOUT_I422] = { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P10 }, |
| 103 | + [DAV1D_PIXEL_LAYOUT_I444] = { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P10 }, |
| 104 | +}; |
| 105 | + |
| 106 | +// TODO: Update once 12bit support is added. |
| 107 | +static const int profile[] = { |
| 108 | + [DAV1D_PIXEL_LAYOUT_I400] = FF_PROFILE_AV1_MAIN, |
| 109 | + [DAV1D_PIXEL_LAYOUT_I420] = FF_PROFILE_AV1_MAIN, |
| 110 | + [DAV1D_PIXEL_LAYOUT_I422] = FF_PROFILE_AV1_PROFESSIONAL, |
| 111 | + [DAV1D_PIXEL_LAYOUT_I444] = FF_PROFILE_AV1_HIGH, |
| 112 | +}; |
| 113 | + |
| 114 | +static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) |
| 115 | +{ |
| 116 | + Libdav1dContext *dav1d = c->priv_data; |
| 117 | + Dav1dData *data = &dav1d->data; |
| 118 | + AVPacket pkt = { 0 }; |
| 119 | + Dav1dPicture p = { 0 }; |
| 120 | + int res; |
| 121 | + |
| 122 | + if (!data->sz) { |
| 123 | + res = ff_decode_get_packet(c, &pkt); |
| 124 | + if (res < 0 && res != AVERROR_EOF) |
| 125 | + return res; |
| 126 | + |
| 127 | + if (pkt.size) { |
| 128 | + if (!av_fifo_space(dav1d->cache)) { |
| 129 | + res = av_fifo_grow(dav1d->cache, 8 * sizeof(pkt)); |
| 130 | + if (res < 0) { |
| 131 | + av_packet_unref(&pkt); |
| 132 | + return res; |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + res = dav1d_data_wrap(data, pkt.data, pkt.size, libdav1d_data_free, pkt.buf); |
| 137 | + if (res < 0) { |
| 138 | + av_packet_unref(&pkt); |
| 139 | + return res; |
| 140 | + } |
| 141 | + |
| 142 | + av_fifo_generic_write(dav1d->cache, &pkt, sizeof(pkt), libdav1d_fifo_write); |
| 143 | + } else { |
| 144 | + data = NULL; |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + res = dav1d_decode(dav1d->c, data, &p); |
| 149 | + if (res < 0) { |
| 150 | + if (res == -EINVAL) |
| 151 | + res = AVERROR_INVALIDDATA; |
| 152 | + else if (res == -EAGAIN && c->internal->draining) |
| 153 | + res = AVERROR_EOF; |
| 154 | + |
| 155 | + return res; |
| 156 | + } |
| 157 | + |
| 158 | + av_assert0(p.data[0] != NULL); |
| 159 | + |
| 160 | + av_fifo_generic_read(dav1d->cache, &pkt, sizeof(pkt), NULL); |
| 161 | + |
| 162 | + frame->buf[0] = av_buffer_create(NULL, 0, libdav1d_frame_free, |
| 163 | + p.ref, AV_BUFFER_FLAG_READONLY); |
| 164 | + if (!frame->buf[0]) { |
| 165 | + dav1d_picture_unref(&p); |
| 166 | + return AVERROR(ENOMEM); |
| 167 | + } |
| 168 | + |
| 169 | + frame->data[0] = p.data[0]; |
| 170 | + frame->data[1] = p.data[1]; |
| 171 | + frame->data[2] = p.data[2]; |
| 172 | + frame->linesize[0] = p.stride[0]; |
| 173 | + frame->linesize[1] = p.stride[1]; |
| 174 | + frame->linesize[2] = p.stride[1]; |
| 175 | + |
| 176 | + c->profile = profile[p.p.layout]; |
| 177 | + frame->format = c->pix_fmt = pix_fmt[p.p.layout][p.p.bpc == 10]; |
| 178 | + frame->width = p.p.w; |
| 179 | + frame->height = p.p.h; |
| 180 | + if (c->width != p.p.w || c->height != p.p.h) { |
| 181 | + res = ff_set_dimensions(c, p.p.w, p.p.h); |
| 182 | + if (res < 0) |
| 183 | + return res; |
| 184 | + } |
| 185 | + |
| 186 | + switch (p.p.chr) { |
| 187 | + case DAV1D_CHR_VERTICAL: |
| 188 | + frame->chroma_location = c->chroma_sample_location = AVCHROMA_LOC_LEFT; |
| 189 | + break; |
| 190 | + case DAV1D_CHR_COLOCATED: |
| 191 | + frame->chroma_location = c->chroma_sample_location = AVCHROMA_LOC_TOPLEFT; |
| 192 | + break; |
| 193 | + } |
| 194 | + frame->colorspace = c->colorspace = (enum AVColorSpace) p.p.mtrx; |
| 195 | + frame->color_primaries = c->color_primaries = (enum AVColorPrimaries) p.p.pri; |
| 196 | + frame->color_trc = c->color_trc = (enum AVColorTransferCharacteristic) p.p.trc; |
| 197 | + frame->color_range = c->color_range = p.p.fullrange ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; |
| 198 | + |
| 199 | + // match timestamps and packet size |
| 200 | + frame->pts = frame->best_effort_timestamp = pkt.pts; |
| 201 | +#if FF_API_PKT_PTS |
| 202 | +FF_DISABLE_DEPRECATION_WARNINGS |
| 203 | + frame->pkt_pts = pkt.pts; |
| 204 | +FF_ENABLE_DEPRECATION_WARNINGS |
| 205 | +#endif |
| 206 | + frame->pkt_dts = pkt.dts; |
| 207 | + frame->pkt_pos = pkt.pos; |
| 208 | + frame->pkt_size = pkt.size; |
| 209 | + frame->pkt_duration = pkt.duration; |
| 210 | + frame->key_frame = p.p.type == DAV1D_FRAME_TYPE_KEY; |
| 211 | + |
| 212 | + switch (p.p.type) { |
| 213 | + case DAV1D_FRAME_TYPE_KEY: |
| 214 | + case DAV1D_FRAME_TYPE_INTRA: |
| 215 | + frame->pict_type = AV_PICTURE_TYPE_I; |
| 216 | + break; |
| 217 | + case DAV1D_FRAME_TYPE_INTER: |
| 218 | + frame->pict_type = AV_PICTURE_TYPE_P; |
| 219 | + break; |
| 220 | + case DAV1D_FRAME_TYPE_SWITCH: |
| 221 | + frame->pict_type = AV_PICTURE_TYPE_SP; |
| 222 | + break; |
| 223 | + default: |
| 224 | + return AVERROR_INVALIDDATA; |
| 225 | + } |
| 226 | + |
| 227 | + return 0; |
| 228 | +} |
| 229 | + |
| 230 | +static av_cold int libdav1d_close(AVCodecContext *c) |
| 231 | +{ |
| 232 | + Libdav1dContext *dav1d = c->priv_data; |
| 233 | + |
| 234 | + av_fifo_freep(&dav1d->cache); |
| 235 | + dav1d_data_unref(&dav1d->data); |
| 236 | + dav1d_close(&dav1d->c); |
| 237 | + |
| 238 | + return 0; |
| 239 | +} |
| 240 | + |
| 241 | +#define OFFSET(x) offsetof(Libdav1dContext, x) |
| 242 | +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM |
| 243 | +static const AVOption libdav1d_options[] = { |
| 244 | + { "tilethreads", "Tile threads", OFFSET(tile_threads), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 64, VD, NULL }, |
| 245 | + { NULL } |
| 246 | +}; |
| 247 | + |
| 248 | +static const AVClass libdav1d_class = { |
| 249 | + .class_name = "libdav1d decoder", |
| 250 | + .item_name = av_default_item_name, |
| 251 | + .option = libdav1d_options, |
| 252 | + .version = LIBAVUTIL_VERSION_INT, |
| 253 | +}; |
| 254 | + |
| 255 | +AVCodec ff_libdav1d_decoder = { |
| 256 | + .name = "libdav1d", |
| 257 | + .long_name = NULL_IF_CONFIG_SMALL("dav1d AV1 decoder by VideoLAN"), |
| 258 | + .type = AVMEDIA_TYPE_VIDEO, |
| 259 | + .id = AV_CODEC_ID_AV1, |
| 260 | + .priv_data_size = sizeof(Libdav1dContext), |
| 261 | + .init = libdav1d_init, |
| 262 | + .close = libdav1d_close, |
| 263 | + .flush = libdav1d_flush, |
| 264 | + .receive_frame = libdav1d_receive_frame, |
| 265 | + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, |
| 266 | + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | |
| 267 | + FF_CODEC_CAP_SETS_PKT_DTS, |
| 268 | + .priv_class = &libdav1d_class, |
| 269 | + .wrapper_name = "libdav1d", |
| 270 | +}; |