libvpxdec.c 11.1 KB
Newer Older
James Zern's avatar
James Zern committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Copyright (c) 2010, Google, Inc.
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
23
 * VP8/9 decoder support via libvpx
James Zern's avatar
James Zern committed
24 25 26
 */

#define VPX_CODEC_DISABLE_COMPAT 1
27 28
#include <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h>
James Zern's avatar
James Zern committed
29

30
#include "libavutil/common.h"
31
#include "libavutil/imgutils.h"
32
#include "libavutil/intreadwrite.h"
James Zern's avatar
James Zern committed
33
#include "avcodec.h"
34
#include "internal.h"
35
#include "libvpx.h"
36
#include "profiles.h"
James Zern's avatar
James Zern committed
37

38
typedef struct VPxDecoderContext {
James Zern's avatar
James Zern committed
39
    struct vpx_codec_ctx decoder;
40 41
    struct vpx_codec_ctx decoder_alpha;
    int has_alpha_channel;
42
} VPxContext;
James Zern's avatar
James Zern committed
43

Luca Barbato's avatar
Luca Barbato committed
44
static av_cold int vpx_init(AVCodecContext *avctx,
45 46
                            const struct vpx_codec_iface *iface,
                            int is_alpha_decoder)
James Zern's avatar
James Zern committed
47
{
48
    VPxContext *ctx = avctx->priv_data;
James Zern's avatar
James Zern committed
49 50 51 52 53 54 55 56
    struct vpx_codec_dec_cfg deccfg = {
        /* token partitions+1 would be a decent choice */
        .threads = FFMIN(avctx->thread_count, 16)
    };

    av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str());
    av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config());

57 58 59
    if (vpx_codec_dec_init(
            is_alpha_decoder ? &ctx->decoder_alpha : &ctx->decoder,
            iface, &deccfg, 0) != VPX_CODEC_OK) {
James Zern's avatar
James Zern committed
60 61 62 63 64 65 66 67 68
        const char *error = vpx_codec_error(&ctx->decoder);
        av_log(avctx, AV_LOG_ERROR, "Failed to initialize decoder: %s\n",
               error);
        return AVERROR(EINVAL);
    }

    return 0;
}

69
// returns 0 on success, AVERROR_INVALIDDATA otherwise
70 71
static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img,
                       int has_alpha_channel)
72
{
73 74 75 76 77
#if VPX_IMAGE_ABI_VERSION >= 3
    static const enum AVColorSpace colorspaces[8] = {
        AVCOL_SPC_UNSPECIFIED, AVCOL_SPC_BT470BG, AVCOL_SPC_BT709, AVCOL_SPC_SMPTE170M,
        AVCOL_SPC_SMPTE240M, AVCOL_SPC_BT2020_NCL, AVCOL_SPC_RESERVED, AVCOL_SPC_RGB,
    };
78 79 80 81 82 83
#if VPX_IMAGE_ABI_VERSION >= 4
    static const enum AVColorRange color_ranges[] = {
        AVCOL_RANGE_MPEG, AVCOL_RANGE_JPEG
    };
    avctx->color_range = color_ranges[img->range];
#endif
84
    avctx->colorspace = colorspaces[img->cs];
85
#endif
86 87 88
    if (avctx->codec_id == AV_CODEC_ID_VP8 && img->fmt != VPX_IMG_FMT_I420)
        return AVERROR_INVALIDDATA;
    switch (img->fmt) {
89
    case VPX_IMG_FMT_I420:
90 91
        if (avctx->codec_id == AV_CODEC_ID_VP9)
            avctx->profile = FF_PROFILE_VP9_0;
92 93
        avctx->pix_fmt =
            has_alpha_channel ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P;
94
        return 0;
95
#if CONFIG_LIBVPX_VP9_DECODER
96
    case VPX_IMG_FMT_I422:
97
        avctx->profile = FF_PROFILE_VP9_1;
98 99
        avctx->pix_fmt = AV_PIX_FMT_YUV422P;
        return 0;
100 101
#if VPX_IMAGE_ABI_VERSION >= 3
    case VPX_IMG_FMT_I440:
102
        avctx->profile = FF_PROFILE_VP9_1;
103 104 105
        avctx->pix_fmt = AV_PIX_FMT_YUV440P;
        return 0;
#endif
106
    case VPX_IMG_FMT_I444:
107
        avctx->profile = FF_PROFILE_VP9_1;
108 109 110 111
#if VPX_IMAGE_ABI_VERSION >= 3
        avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ?
                         AV_PIX_FMT_GBRP : AV_PIX_FMT_YUV444P;
#else
112
        avctx->pix_fmt = AV_PIX_FMT_YUV444P;
113
#endif
114 115 116
        return 0;
#ifdef VPX_IMG_FMT_HIGHBITDEPTH
    case VPX_IMG_FMT_I42016:
117
        avctx->profile = FF_PROFILE_VP9_2;
118
        if (img->bit_depth == 10) {
119
            avctx->pix_fmt = AV_PIX_FMT_YUV420P10;
120
            return 0;
121
        } else if (img->bit_depth == 12) {
122
            avctx->pix_fmt = AV_PIX_FMT_YUV420P12;
123
            return 0;
124 125 126 127
        } else {
            return AVERROR_INVALIDDATA;
        }
    case VPX_IMG_FMT_I42216:
128
        avctx->profile = FF_PROFILE_VP9_3;
129
        if (img->bit_depth == 10) {
130
            avctx->pix_fmt = AV_PIX_FMT_YUV422P10;
131 132
            return 0;
        } else if (img->bit_depth == 12) {
133
            avctx->pix_fmt = AV_PIX_FMT_YUV422P12;
134 135 136 137
            return 0;
        } else {
            return AVERROR_INVALIDDATA;
        }
138 139
#if VPX_IMAGE_ABI_VERSION >= 3
    case VPX_IMG_FMT_I44016:
140
        avctx->profile = FF_PROFILE_VP9_3;
141
        if (img->bit_depth == 10) {
142
            avctx->pix_fmt = AV_PIX_FMT_YUV440P10;
143 144
            return 0;
        } else if (img->bit_depth == 12) {
145
            avctx->pix_fmt = AV_PIX_FMT_YUV440P12;
146 147 148 149 150
            return 0;
        } else {
            return AVERROR_INVALIDDATA;
        }
#endif
151
    case VPX_IMG_FMT_I44416:
152
        avctx->profile = FF_PROFILE_VP9_3;
153
        if (img->bit_depth == 10) {
154 155
#if VPX_IMAGE_ABI_VERSION >= 3
            avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ?
156
                             AV_PIX_FMT_GBRP10 : AV_PIX_FMT_YUV444P10;
157
#else
158
            avctx->pix_fmt = AV_PIX_FMT_YUV444P10;
159
#endif
160 161
            return 0;
        } else if (img->bit_depth == 12) {
162 163
#if VPX_IMAGE_ABI_VERSION >= 3
            avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ?
164
                             AV_PIX_FMT_GBRP12 : AV_PIX_FMT_YUV444P12;
165
#else
166
            avctx->pix_fmt = AV_PIX_FMT_YUV444P12;
167
#endif
168 169 170 171
            return 0;
        } else {
            return AVERROR_INVALIDDATA;
        }
172
#endif
173
#endif
174 175
    default:
        return AVERROR_INVALIDDATA;
176 177 178
    }
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
static int decode_frame(AVCodecContext *avctx, vpx_codec_ctx_t *decoder,
                        uint8_t *data, uint32_t data_sz)
{
    if (vpx_codec_decode(decoder, data, data_sz, NULL, 0) != VPX_CODEC_OK) {
        const char *error  = vpx_codec_error(decoder);
        const char *detail = vpx_codec_error_detail(decoder);

        av_log(avctx, AV_LOG_ERROR, "Failed to decode frame: %s\n", error);
        if (detail) {
            av_log(avctx, AV_LOG_ERROR, "  Additional information: %s\n",
                   detail);
        }
        return AVERROR_INVALIDDATA;
    }
    return 0;
}

196
static int vpx_decode(AVCodecContext *avctx,
197
                      void *data, int *got_frame, AVPacket *avpkt)
James Zern's avatar
James Zern committed
198
{
199
    VPxContext *ctx = avctx->priv_data;
James Zern's avatar
James Zern committed
200 201
    AVFrame *picture = data;
    const void *iter = NULL;
202 203
    const void *iter_alpha = NULL;
    struct vpx_image *img, *img_alpha;
204
    int ret;
205 206
    uint8_t *side_data = NULL;
    int side_data_size = 0;
James Zern's avatar
James Zern committed
207

208 209 210
    ret = decode_frame(avctx, &ctx->decoder, avpkt->data, avpkt->size);
    if (ret)
        return ret;
James Zern's avatar
James Zern committed
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    side_data = av_packet_get_side_data(avpkt,
                                        AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
                                        &side_data_size);
    if (side_data_size > 1) {
        const uint64_t additional_id = AV_RB64(side_data);
        side_data += 8;
        side_data_size -= 8;
        if (additional_id == 1) {  // 1 stands for alpha channel data.
            if (!ctx->has_alpha_channel) {
                ctx->has_alpha_channel = 1;
                ret = vpx_init(avctx,
#if CONFIG_LIBVPX_VP8_DECODER && CONFIG_LIBVPX_VP9_DECODER
                               (avctx->codec_id == AV_CODEC_ID_VP8) ?
                               &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
#elif CONFIG_LIBVPX_VP8_DECODER
                               &vpx_codec_vp8_dx_algo,
#else
                               &vpx_codec_vp9_dx_algo,
#endif
                               1);
                if (ret)
                    return ret;
            }
            ret = decode_frame(avctx, &ctx->decoder_alpha, side_data,
                               side_data_size);
            if (ret)
                return ret;
        }
James Zern's avatar
James Zern committed
240 241
    }

242 243 244 245 246 247
    if ((img = vpx_codec_get_frame(&ctx->decoder, &iter)) &&
        (!ctx->has_alpha_channel ||
         (img_alpha = vpx_codec_get_frame(&ctx->decoder_alpha, &iter_alpha)))) {
        uint8_t *planes[4];
        int linesizes[4];
        if ((ret = set_pix_fmt(avctx, img, ctx->has_alpha_channel)) < 0) {
248 249 250 251 252 253 254 255
#ifdef VPX_IMG_FMT_HIGHBITDEPTH
            av_log(avctx, AV_LOG_ERROR, "Unsupported output colorspace (%d) / bit_depth (%d)\n",
                   img->fmt, img->bit_depth);
#else
            av_log(avctx, AV_LOG_ERROR, "Unsupported output colorspace (%d) / bit_depth (%d)\n",
                   img->fmt, 8);
#endif
            return ret;
James Zern's avatar
James Zern committed
256 257 258 259 260
        }

        if ((int) img->d_w != avctx->width || (int) img->d_h != avctx->height) {
            av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
                   avctx->width, avctx->height, img->d_w, img->d_h);
261 262 263
            ret = ff_set_dimensions(avctx, img->d_w, img->d_h);
            if (ret < 0)
                return ret;
James Zern's avatar
James Zern committed
264
        }
265 266
        if ((ret = ff_get_buffer(avctx, picture, 0)) < 0)
            return ret;
267 268 269 270 271 272 273 274 275 276 277 278 279

        planes[0] = img->planes[VPX_PLANE_Y];
        planes[1] = img->planes[VPX_PLANE_U];
        planes[2] = img->planes[VPX_PLANE_V];
        planes[3] =
            ctx->has_alpha_channel ? img_alpha->planes[VPX_PLANE_Y] : NULL;
        linesizes[0] = img->stride[VPX_PLANE_Y];
        linesizes[1] = img->stride[VPX_PLANE_U];
        linesizes[2] = img->stride[VPX_PLANE_V];
        linesizes[3] =
            ctx->has_alpha_channel ? img_alpha->stride[VPX_PLANE_Y] : 0;
        av_image_copy(picture->data, picture->linesize, (const uint8_t**)planes,
                      linesizes, avctx->pix_fmt, img->d_w, img->d_h);
280
        *got_frame           = 1;
James Zern's avatar
James Zern committed
281 282 283 284
    }
    return avpkt->size;
}

285
static av_cold int vpx_free(AVCodecContext *avctx)
James Zern's avatar
James Zern committed
286
{
287
    VPxContext *ctx = avctx->priv_data;
James Zern's avatar
James Zern committed
288
    vpx_codec_destroy(&ctx->decoder);
289 290
    if (ctx->has_alpha_channel)
        vpx_codec_destroy(&ctx->decoder_alpha);
James Zern's avatar
James Zern committed
291 292 293
    return 0;
}

294 295 296
#if CONFIG_LIBVPX_VP8_DECODER
static av_cold int vp8_init(AVCodecContext *avctx)
{
297
    return vpx_init(avctx, &vpx_codec_vp8_dx_algo, 0);
298 299 300
}

AVCodec ff_libvpx_vp8_decoder = {
301
    .name           = "libvpx",
302
    .long_name      = NULL_IF_CONFIG_SMALL("libvpx VP8"),
303
    .type           = AVMEDIA_TYPE_VIDEO,
304
    .id             = AV_CODEC_ID_VP8,
305
    .priv_data_size = sizeof(VPxContext),
306
    .init           = vp8_init,
307 308
    .close          = vpx_free,
    .decode         = vpx_decode,
309
    .capabilities   = AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_DR1,
James Zern's avatar
James Zern committed
310
};
311
#endif /* CONFIG_LIBVPX_VP8_DECODER */
Luca Barbato's avatar
Luca Barbato committed
312

313
#if CONFIG_LIBVPX_VP9_DECODER
Luca Barbato's avatar
Luca Barbato committed
314 315
static av_cold int vp9_init(AVCodecContext *avctx)
{
316
    return vpx_init(avctx, &vpx_codec_vp9_dx_algo, 0);
Luca Barbato's avatar
Luca Barbato committed
317 318 319 320
}

AVCodec ff_libvpx_vp9_decoder = {
    .name           = "libvpx-vp9",
321
    .long_name      = NULL_IF_CONFIG_SMALL("libvpx VP9"),
Luca Barbato's avatar
Luca Barbato committed
322 323
    .type           = AVMEDIA_TYPE_VIDEO,
    .id             = AV_CODEC_ID_VP9,
324
    .priv_data_size = sizeof(VPxContext),
Luca Barbato's avatar
Luca Barbato committed
325
    .init           = vp9_init,
326 327
    .close          = vpx_free,
    .decode         = vpx_decode,
328
    .capabilities   = AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_DR1,
329
    .init_static_data = ff_vp9_init_static,
330
    .profiles       = NULL_IF_CONFIG_SMALL(ff_vp9_profiles),
Luca Barbato's avatar
Luca Barbato committed
331
};
332
#endif /* CONFIG_LIBVPX_VP9_DECODER */