Home | History | Annotate | Download | only in vp9
      1 /*
      2  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include "vpx/vpx_decoder.h"
     15 #include "vpx/vp8dx.h"
     16 #include "vpx/internal/vpx_codec_internal.h"
     17 #include "./vpx_version.h"
     18 #include "vp9/common/vp9_frame_buffers.h"
     19 #include "vp9/decoder/vp9_decoder.h"
     20 #include "vp9/decoder/vp9_read_bit_buffer.h"
     21 #include "vp9/vp9_iface_common.h"
     22 
     23 #define VP9_CAP_POSTPROC (CONFIG_VP9_POSTPROC ? VPX_CODEC_CAP_POSTPROC : 0)
     24 typedef vpx_codec_stream_info_t  vp9_stream_info_t;
     25 
     26 struct vpx_codec_alg_priv {
     27   vpx_codec_priv_t        base;
     28   vpx_codec_dec_cfg_t     cfg;
     29   vp9_stream_info_t       si;
     30   int                     decoder_init;
     31   struct VP9Decompressor *pbi;
     32   int                     postproc_cfg_set;
     33   vp8_postproc_cfg_t      postproc_cfg;
     34 #if CONFIG_POSTPROC_VISUALIZER
     35   unsigned int            dbg_postproc_flag;
     36   int                     dbg_color_ref_frame_flag;
     37   int                     dbg_color_mb_modes_flag;
     38   int                     dbg_color_b_modes_flag;
     39   int                     dbg_display_mv_flag;
     40 #endif
     41   vpx_image_t             img;
     42   int                     img_setup;
     43   int                     img_avail;
     44   int                     invert_tile_order;
     45 
     46   // External frame buffer info to save for VP9 common.
     47   void *ext_priv;  // Private data associated with the external frame buffers.
     48   vpx_get_frame_buffer_cb_fn_t get_ext_fb_cb;
     49   vpx_release_frame_buffer_cb_fn_t release_ext_fb_cb;
     50 };
     51 
     52 static vpx_codec_err_t vp9_init(vpx_codec_ctx_t *ctx,
     53                                 vpx_codec_priv_enc_mr_cfg_t *data) {
     54   // This function only allocates space for the vpx_codec_alg_priv_t
     55   // structure. More memory may be required at the time the stream
     56   // information becomes known.
     57   (void)data;
     58   if (!ctx->priv) {
     59     void *base = vpx_memalign(32, sizeof(vpx_codec_alg_priv_t));
     60     if (base == NULL)
     61       return VPX_CODEC_MEM_ERROR;
     62 
     63     memset(base, 0, sizeof(vpx_codec_alg_priv_t));
     64     ctx->priv = (vpx_codec_priv_t *)base;
     65     ctx->priv->sz = sizeof(*ctx->priv);
     66     ctx->priv->iface = ctx->iface;
     67     ctx->priv->alg_priv = (vpx_codec_alg_priv_t *)base;
     68     ctx->priv->alg_priv->si.sz = sizeof(ctx->priv->alg_priv->si);
     69     ctx->priv->init_flags = ctx->init_flags;
     70 
     71     if (ctx->config.dec) {
     72       // Update the reference to the config structure to an internal copy.
     73       ctx->priv->alg_priv->cfg = *ctx->config.dec;
     74       ctx->config.dec = &ctx->priv->alg_priv->cfg;
     75     }
     76   }
     77 
     78   return VPX_CODEC_OK;
     79 }
     80 
     81 static vpx_codec_err_t vp9_destroy(vpx_codec_alg_priv_t *ctx) {
     82   if (ctx->pbi)
     83     vp9_remove_decompressor(ctx->pbi);
     84 
     85   return VPX_CODEC_OK;
     86 }
     87 
     88 static vpx_codec_err_t vp9_peek_si(const uint8_t *data, unsigned int data_sz,
     89                                    vpx_codec_stream_info_t *si) {
     90   if (data_sz <= 8) return VPX_CODEC_UNSUP_BITSTREAM;
     91   if (data + data_sz <= data) return VPX_CODEC_INVALID_PARAM;
     92 
     93   si->is_kf = 0;
     94   si->w = si->h = 0;
     95 
     96   {
     97     struct vp9_read_bit_buffer rb = { data, data + data_sz, 0, NULL, NULL };
     98     const int frame_marker = vp9_rb_read_literal(&rb, 2);
     99     const int version = vp9_rb_read_bit(&rb);
    100     (void) vp9_rb_read_bit(&rb);  // unused version bit
    101 
    102     if (frame_marker != VP9_FRAME_MARKER)
    103       return VPX_CODEC_UNSUP_BITSTREAM;
    104     if (version > 1) return VPX_CODEC_UNSUP_BITSTREAM;
    105 
    106     if (vp9_rb_read_bit(&rb)) {  // show an existing frame
    107       return VPX_CODEC_OK;
    108     }
    109 
    110     si->is_kf = !vp9_rb_read_bit(&rb);
    111     if (si->is_kf) {
    112       const int sRGB = 7;
    113       int colorspace;
    114 
    115       rb.bit_offset += 1;  // show frame
    116       rb.bit_offset += 1;  // error resilient
    117 
    118       if (vp9_rb_read_literal(&rb, 8) != VP9_SYNC_CODE_0 ||
    119           vp9_rb_read_literal(&rb, 8) != VP9_SYNC_CODE_1 ||
    120           vp9_rb_read_literal(&rb, 8) != VP9_SYNC_CODE_2) {
    121         return VPX_CODEC_UNSUP_BITSTREAM;
    122       }
    123 
    124       colorspace = vp9_rb_read_literal(&rb, 3);
    125       if (colorspace != sRGB) {
    126         rb.bit_offset += 1;  // [16,235] (including xvycc) vs [0,255] range
    127         if (version == 1) {
    128           rb.bit_offset += 2;  // subsampling x/y
    129           rb.bit_offset += 1;  // has extra plane
    130         }
    131       } else {
    132         if (version == 1) {
    133           rb.bit_offset += 1;  // has extra plane
    134         } else {
    135           // RGB is only available in version 1
    136           return VPX_CODEC_UNSUP_BITSTREAM;
    137         }
    138       }
    139 
    140       // TODO(jzern): these are available on non-keyframes in intra only mode.
    141       si->w = vp9_rb_read_literal(&rb, 16) + 1;
    142       si->h = vp9_rb_read_literal(&rb, 16) + 1;
    143     }
    144   }
    145 
    146   return VPX_CODEC_OK;
    147 }
    148 
    149 static vpx_codec_err_t vp9_get_si(vpx_codec_alg_priv_t    *ctx,
    150                                   vpx_codec_stream_info_t *si) {
    151   const size_t sz = (si->sz >= sizeof(vp9_stream_info_t))
    152                        ? sizeof(vp9_stream_info_t)
    153                        : sizeof(vpx_codec_stream_info_t);
    154   memcpy(si, &ctx->si, sz);
    155   si->sz = (unsigned int)sz;
    156 
    157   return VPX_CODEC_OK;
    158 }
    159 
    160 
    161 static vpx_codec_err_t update_error_state(vpx_codec_alg_priv_t *ctx,
    162                            const struct vpx_internal_error_info *error) {
    163   if (error->error_code)
    164     ctx->base.err_detail = error->has_detail ? error->detail : NULL;
    165 
    166   return error->error_code;
    167 }
    168 
    169 static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
    170                                   const uint8_t **data, unsigned int data_sz,
    171                                   void *user_priv, int64_t deadline) {
    172   vpx_codec_err_t res = VPX_CODEC_OK;
    173 
    174   ctx->img_avail = 0;
    175 
    176   // Determine the stream parameters. Note that we rely on peek_si to
    177   // validate that we have a buffer that does not wrap around the top
    178   // of the heap.
    179   if (!ctx->si.h)
    180     res = ctx->base.iface->dec.peek_si(*data, data_sz, &ctx->si);
    181 
    182   /* Initialize the decoder instance on the first frame*/
    183   if (!res && !ctx->decoder_init) {
    184     VP9D_CONFIG oxcf;
    185     struct VP9Decompressor *optr;
    186 
    187     vp9_initialize_dec();
    188 
    189     oxcf.width = ctx->si.w;
    190     oxcf.height = ctx->si.h;
    191     oxcf.version = 9;
    192     oxcf.max_threads = ctx->cfg.threads;
    193     oxcf.inv_tile_order = ctx->invert_tile_order;
    194     optr = vp9_create_decompressor(&oxcf);
    195 
    196     // If postprocessing was enabled by the application and a
    197     // configuration has not been provided, default it.
    198     if (!ctx->postproc_cfg_set &&
    199         (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC)) {
    200       ctx->postproc_cfg.post_proc_flag = VP8_DEBLOCK | VP8_DEMACROBLOCK;
    201       ctx->postproc_cfg.deblocking_level = 4;
    202       ctx->postproc_cfg.noise_level = 0;
    203     }
    204 
    205     if (!optr) {
    206       res = VPX_CODEC_ERROR;
    207     } else {
    208       VP9D_COMP *const pbi = (VP9D_COMP*)optr;
    209       VP9_COMMON *const cm = &pbi->common;
    210 
    211       // Set index to not initialized.
    212       cm->new_fb_idx = -1;
    213 
    214       if (ctx->get_ext_fb_cb != NULL && ctx->release_ext_fb_cb != NULL) {
    215         cm->get_fb_cb = ctx->get_ext_fb_cb;
    216         cm->release_fb_cb = ctx->release_ext_fb_cb;
    217         cm->cb_priv = ctx->ext_priv;
    218       } else {
    219         cm->get_fb_cb = vp9_get_frame_buffer;
    220         cm->release_fb_cb = vp9_release_frame_buffer;
    221 
    222         if (vp9_alloc_internal_frame_buffers(&cm->int_frame_buffers))
    223           vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
    224                              "Failed to initialize internal frame buffers");
    225         cm->cb_priv = &cm->int_frame_buffers;
    226       }
    227 
    228       ctx->pbi = optr;
    229     }
    230 
    231     ctx->decoder_init = 1;
    232   }
    233 
    234   if (!res && ctx->pbi) {
    235     VP9D_COMP *const pbi = ctx->pbi;
    236     VP9_COMMON *const cm = &pbi->common;
    237     YV12_BUFFER_CONFIG sd;
    238     int64_t time_stamp = 0, time_end_stamp = 0;
    239     vp9_ppflags_t flags = {0};
    240 
    241     if (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC) {
    242       flags.post_proc_flag =
    243 #if CONFIG_POSTPROC_VISUALIZER
    244           (ctx->dbg_color_ref_frame_flag ? VP9D_DEBUG_CLR_FRM_REF_BLKS : 0) |
    245           (ctx->dbg_color_mb_modes_flag ? VP9D_DEBUG_CLR_BLK_MODES : 0) |
    246           (ctx->dbg_color_b_modes_flag ? VP9D_DEBUG_CLR_BLK_MODES : 0) |
    247           (ctx->dbg_display_mv_flag ? VP9D_DEBUG_DRAW_MV : 0) |
    248 #endif
    249           ctx->postproc_cfg.post_proc_flag;
    250 
    251       flags.deblocking_level = ctx->postproc_cfg.deblocking_level;
    252       flags.noise_level = ctx->postproc_cfg.noise_level;
    253 #if CONFIG_POSTPROC_VISUALIZER
    254       flags.display_ref_frame_flag = ctx->dbg_color_ref_frame_flag;
    255       flags.display_mb_modes_flag = ctx->dbg_color_mb_modes_flag;
    256       flags.display_b_modes_flag = ctx->dbg_color_b_modes_flag;
    257       flags.display_mv_flag = ctx->dbg_display_mv_flag;
    258 #endif
    259     }
    260 
    261     if (vp9_receive_compressed_data(pbi, data_sz, data, deadline))
    262       res = update_error_state(ctx, &cm->error);
    263 
    264     if (!res && 0 == vp9_get_raw_frame(pbi, &sd, &time_stamp,
    265                                        &time_end_stamp, &flags)) {
    266       yuvconfig2image(&ctx->img, &sd, user_priv);
    267 
    268       ctx->img.fb_priv = cm->frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv;
    269       ctx->img_avail = 1;
    270     }
    271   }
    272 
    273   return res;
    274 }
    275 
    276 static void parse_superframe_index(const uint8_t *data, size_t data_sz,
    277                                    uint32_t sizes[8], int *count) {
    278   uint8_t marker;
    279 
    280   assert(data_sz);
    281   marker = data[data_sz - 1];
    282   *count = 0;
    283 
    284   if ((marker & 0xe0) == 0xc0) {
    285     const uint32_t frames = (marker & 0x7) + 1;
    286     const uint32_t mag = ((marker >> 3) & 0x3) + 1;
    287     const size_t index_sz = 2 + mag * frames;
    288 
    289     if (data_sz >= index_sz && data[data_sz - index_sz] == marker) {
    290       // found a valid superframe index
    291       uint32_t i, j;
    292       const uint8_t *x = data + data_sz - index_sz + 1;
    293 
    294       for (i = 0; i < frames; i++) {
    295         uint32_t this_sz = 0;
    296 
    297         for (j = 0; j < mag; j++)
    298           this_sz |= (*x++) << (j * 8);
    299         sizes[i] = this_sz;
    300       }
    301 
    302       *count = frames;
    303     }
    304   }
    305 }
    306 
    307 static vpx_codec_err_t vp9_decode(vpx_codec_alg_priv_t  *ctx,
    308                                   const uint8_t         *data,
    309                                   unsigned int           data_sz,
    310                                   void                  *user_priv,
    311                                   long                   deadline) {
    312   const uint8_t *data_start = data;
    313   const uint8_t *data_end = data + data_sz;
    314   vpx_codec_err_t res = VPX_CODEC_OK;
    315   uint32_t sizes[8];
    316   int frames_this_pts, frame_count = 0;
    317 
    318   if (data == NULL || data_sz == 0) return VPX_CODEC_INVALID_PARAM;
    319 
    320   parse_superframe_index(data, data_sz, sizes, &frames_this_pts);
    321 
    322   do {
    323     // Skip over the superframe index, if present
    324     if (data_sz && (*data_start & 0xe0) == 0xc0) {
    325       const uint8_t marker = *data_start;
    326       const uint32_t frames = (marker & 0x7) + 1;
    327       const uint32_t mag = ((marker >> 3) & 0x3) + 1;
    328       const uint32_t index_sz = 2 + mag * frames;
    329 
    330       if (data_sz >= index_sz && data_start[index_sz - 1] == marker) {
    331         data_start += index_sz;
    332         data_sz -= index_sz;
    333         if (data_start < data_end)
    334           continue;
    335         else
    336           break;
    337       }
    338     }
    339 
    340     // Use the correct size for this frame, if an index is present.
    341     if (frames_this_pts) {
    342       uint32_t this_sz = sizes[frame_count];
    343 
    344       if (data_sz < this_sz) {
    345         ctx->base.err_detail = "Invalid frame size in index";
    346         return VPX_CODEC_CORRUPT_FRAME;
    347       }
    348 
    349       data_sz = this_sz;
    350       frame_count++;
    351     }
    352 
    353     res = decode_one(ctx, &data_start, data_sz, user_priv, deadline);
    354     assert(data_start >= data);
    355     assert(data_start <= data_end);
    356 
    357     /* Early exit if there was a decode error */
    358     if (res)
    359       break;
    360 
    361     /* Account for suboptimal termination by the encoder. */
    362     while (data_start < data_end && *data_start == 0)
    363       data_start++;
    364 
    365     data_sz = (unsigned int)(data_end - data_start);
    366   } while (data_start < data_end);
    367   return res;
    368 }
    369 
    370 static vpx_image_t *vp9_get_frame(vpx_codec_alg_priv_t  *ctx,
    371                                   vpx_codec_iter_t      *iter) {
    372   vpx_image_t *img = NULL;
    373 
    374   if (ctx->img_avail) {
    375     /* iter acts as a flip flop, so an image is only returned on the first
    376      * call to get_frame.
    377      */
    378     if (!(*iter)) {
    379       img = &ctx->img;
    380       *iter = img;
    381     }
    382   }
    383   ctx->img_avail = 0;
    384 
    385   return img;
    386 }
    387 
    388 static vpx_codec_err_t vp9_set_fb_fn(
    389     vpx_codec_alg_priv_t *ctx,
    390     vpx_get_frame_buffer_cb_fn_t cb_get,
    391     vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv) {
    392   if (cb_get == NULL || cb_release == NULL) {
    393     return VPX_CODEC_INVALID_PARAM;
    394   } else if (ctx->pbi == NULL) {
    395     // If the decoder has already been initialized, do not accept changes to
    396     // the frame buffer functions.
    397     ctx->get_ext_fb_cb = cb_get;
    398     ctx->release_ext_fb_cb = cb_release;
    399     ctx->ext_priv = cb_priv;
    400     return VPX_CODEC_OK;
    401   }
    402 
    403   return VPX_CODEC_ERROR;
    404 }
    405 
    406 static vpx_codec_err_t set_reference(vpx_codec_alg_priv_t *ctx, int ctr_id,
    407                                      va_list args) {
    408   vpx_ref_frame_t *const data = va_arg(args, vpx_ref_frame_t *);
    409   (void)ctr_id;
    410   if (data) {
    411     vpx_ref_frame_t *const frame = (vpx_ref_frame_t *)data;
    412     YV12_BUFFER_CONFIG sd;
    413 
    414     image2yuvconfig(&frame->img, &sd);
    415     return vp9_set_reference_dec(&ctx->pbi->common,
    416                                  (VP9_REFFRAME)frame->frame_type, &sd);
    417   } else {
    418     return VPX_CODEC_INVALID_PARAM;
    419   }
    420 }
    421 
    422 static vpx_codec_err_t copy_reference(vpx_codec_alg_priv_t *ctx, int ctr_id,
    423                                       va_list args) {
    424   vpx_ref_frame_t *data = va_arg(args, vpx_ref_frame_t *);
    425   (void)ctr_id;
    426   if (data) {
    427     vpx_ref_frame_t *frame = (vpx_ref_frame_t *)data;
    428     YV12_BUFFER_CONFIG sd;
    429 
    430     image2yuvconfig(&frame->img, &sd);
    431 
    432     return vp9_copy_reference_dec(ctx->pbi,
    433                                   (VP9_REFFRAME)frame->frame_type, &sd);
    434   } else {
    435     return VPX_CODEC_INVALID_PARAM;
    436   }
    437 }
    438 
    439 static vpx_codec_err_t get_reference(vpx_codec_alg_priv_t *ctx, int ctr_id,
    440                                      va_list args) {
    441   vp9_ref_frame_t *data = va_arg(args, vp9_ref_frame_t *);
    442   (void)ctr_id;
    443   if (data) {
    444     YV12_BUFFER_CONFIG* fb;
    445 
    446     vp9_get_reference_dec(ctx->pbi, data->idx, &fb);
    447     yuvconfig2image(&data->img, fb, NULL);
    448     return VPX_CODEC_OK;
    449   } else {
    450     return VPX_CODEC_INVALID_PARAM;
    451   }
    452 }
    453 
    454 static vpx_codec_err_t set_postproc(vpx_codec_alg_priv_t *ctx, int ctr_id,
    455                                     va_list args) {
    456 #if CONFIG_VP9_POSTPROC
    457   vp8_postproc_cfg_t *data = va_arg(args, vp8_postproc_cfg_t *);
    458 
    459   if (data) {
    460     ctx->postproc_cfg_set = 1;
    461     ctx->postproc_cfg = *((vp8_postproc_cfg_t *)data);
    462     return VPX_CODEC_OK;
    463   } else {
    464     return VPX_CODEC_INVALID_PARAM;
    465   }
    466 #else
    467   (void)ctr_id;
    468   (void)ctx;
    469   (void)args;
    470   return VPX_CODEC_INCAPABLE;
    471 #endif
    472 }
    473 
    474 static vpx_codec_err_t set_dbg_options(vpx_codec_alg_priv_t *ctx, int ctrl_id,
    475                                        va_list args) {
    476 #if CONFIG_POSTPROC_VISUALIZER && CONFIG_POSTPROC
    477   int data = va_arg(args, int);
    478 
    479 #define MAP(id, var) case id: var = data; break;
    480 
    481   switch (ctrl_id) {
    482       MAP(VP8_SET_DBG_COLOR_REF_FRAME,   ctx->dbg_color_ref_frame_flag);
    483       MAP(VP8_SET_DBG_COLOR_MB_MODES,    ctx->dbg_color_mb_modes_flag);
    484       MAP(VP8_SET_DBG_COLOR_B_MODES,     ctx->dbg_color_b_modes_flag);
    485       MAP(VP8_SET_DBG_DISPLAY_MV,        ctx->dbg_display_mv_flag);
    486   }
    487 
    488   return VPX_CODEC_OK;
    489 #else
    490   (void)ctrl_id;
    491   (void)ctx;
    492   (void)args;
    493   return VPX_CODEC_INCAPABLE;
    494 #endif
    495 }
    496 
    497 static vpx_codec_err_t get_last_ref_updates(vpx_codec_alg_priv_t *ctx,
    498                                             int ctrl_id, va_list args) {
    499   int *const update_info = va_arg(args, int *);
    500 
    501   (void)ctrl_id;
    502   if (update_info) {
    503     if (ctx->pbi)
    504       *update_info = ctx->pbi->refresh_frame_flags;
    505     else
    506       return VPX_CODEC_ERROR;
    507     return VPX_CODEC_OK;
    508   } else {
    509     return VPX_CODEC_INVALID_PARAM;
    510   }
    511 }
    512 
    513 
    514 static vpx_codec_err_t get_frame_corrupted(vpx_codec_alg_priv_t *ctx,
    515                                            int ctrl_id, va_list args) {
    516   int *corrupted = va_arg(args, int *);
    517   (void)ctrl_id;
    518 
    519   if (corrupted) {
    520     if (ctx->pbi)
    521       *corrupted = ctx->pbi->common.frame_to_show->corrupted;
    522     else
    523       return VPX_CODEC_ERROR;
    524     return VPX_CODEC_OK;
    525   } else {
    526     return VPX_CODEC_INVALID_PARAM;
    527   }
    528 }
    529 
    530 static vpx_codec_err_t get_display_size(vpx_codec_alg_priv_t *ctx,
    531                                         int ctrl_id, va_list args) {
    532   int *const display_size = va_arg(args, int *);
    533   (void)ctrl_id;
    534 
    535   if (display_size) {
    536     if (ctx->pbi) {
    537       const VP9_COMMON *const cm = &ctx->pbi->common;
    538       display_size[0] = cm->display_width;
    539       display_size[1] = cm->display_height;
    540     } else {
    541       return VPX_CODEC_ERROR;
    542     }
    543     return VPX_CODEC_OK;
    544   } else {
    545     return VPX_CODEC_INVALID_PARAM;
    546   }
    547 }
    548 
    549 static vpx_codec_err_t set_invert_tile_order(vpx_codec_alg_priv_t *ctx,
    550                                              int ctrl_id,
    551                                              va_list args) {
    552   (void)ctrl_id;
    553   (void)ctx;
    554   ctx->invert_tile_order = va_arg(args, int);
    555   return VPX_CODEC_OK;
    556 }
    557 
    558 static vpx_codec_ctrl_fn_map_t ctf_maps[] = {
    559   {VP8_SET_REFERENCE,             set_reference},
    560   {VP8_COPY_REFERENCE,            copy_reference},
    561   {VP8_SET_POSTPROC,              set_postproc},
    562   {VP8_SET_DBG_COLOR_REF_FRAME,   set_dbg_options},
    563   {VP8_SET_DBG_COLOR_MB_MODES,    set_dbg_options},
    564   {VP8_SET_DBG_COLOR_B_MODES,     set_dbg_options},
    565   {VP8_SET_DBG_DISPLAY_MV,        set_dbg_options},
    566   {VP8D_GET_LAST_REF_UPDATES,     get_last_ref_updates},
    567   {VP8D_GET_FRAME_CORRUPTED,      get_frame_corrupted},
    568   {VP9_GET_REFERENCE,             get_reference},
    569   {VP9D_GET_DISPLAY_SIZE,         get_display_size},
    570   {VP9_INVERT_TILE_DECODE_ORDER,  set_invert_tile_order},
    571   { -1, NULL},
    572 };
    573 
    574 
    575 #ifndef VERSION_STRING
    576 #define VERSION_STRING
    577 #endif
    578 CODEC_INTERFACE(vpx_codec_vp9_dx) = {
    579   "WebM Project VP9 Decoder" VERSION_STRING,
    580   VPX_CODEC_INTERNAL_ABI_VERSION,
    581   VPX_CODEC_CAP_DECODER | VP9_CAP_POSTPROC |
    582       VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER,
    583   /* vpx_codec_caps_t          caps; */
    584   vp9_init,         /* vpx_codec_init_fn_t       init; */
    585   vp9_destroy,      /* vpx_codec_destroy_fn_t    destroy; */
    586   ctf_maps,         /* vpx_codec_ctrl_fn_map_t  *ctrl_maps; */
    587   NOT_IMPLEMENTED,  /* vpx_codec_get_mmap_fn_t   get_mmap; */
    588   NOT_IMPLEMENTED,  /* vpx_codec_set_mmap_fn_t   set_mmap; */
    589   { // NOLINT
    590     vp9_peek_si,      /* vpx_codec_peek_si_fn_t    peek_si; */
    591     vp9_get_si,       /* vpx_codec_get_si_fn_t     get_si; */
    592     vp9_decode,       /* vpx_codec_decode_fn_t     decode; */
    593     vp9_get_frame,    /* vpx_codec_frame_get_fn_t  frame_get; */
    594     vp9_set_fb_fn,    /* vpx_codec_set_fb_fn_t     set_fb_fn; */
    595   },
    596   { // NOLINT
    597     /* encoder functions */
    598     NOT_IMPLEMENTED,
    599     NOT_IMPLEMENTED,
    600     NOT_IMPLEMENTED,
    601     NOT_IMPLEMENTED,
    602     NOT_IMPLEMENTED,
    603     NOT_IMPLEMENTED,
    604     NOT_IMPLEMENTED
    605   }
    606 };
    607