LCOV - code coverage report
Current view: top level - libavcodec - dxv.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 222 262 84.7 %
Date: 2017-12-10 21:22:29 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /*
       2             :  * Resolume DXV decoder
       3             :  * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@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 <stdint.h>
      23             : 
      24             : #include "libavutil/imgutils.h"
      25             : 
      26             : #include "avcodec.h"
      27             : #include "bytestream.h"
      28             : #include "internal.h"
      29             : #include "lzf.h"
      30             : #include "texturedsp.h"
      31             : #include "thread.h"
      32             : 
      33             : typedef struct DXVContext {
      34             :     TextureDSPContext texdsp;
      35             :     GetByteContext gbc;
      36             : 
      37             :     uint8_t *tex_data;  // Compressed texture
      38             :     int tex_rat;        // Compression ratio
      39             :     int tex_step;       // Distance between blocks
      40             :     int64_t tex_size;   // Texture size
      41             : 
      42             :     /* Optimal number of slices for parallel decoding */
      43             :     int slice_count;
      44             : 
      45             :     /* Pointer to the selected decompression function */
      46             :     int (*tex_funct)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block);
      47             : } DXVContext;
      48             : 
      49           4 : static int decompress_texture_thread(AVCodecContext *avctx, void *arg,
      50             :                                      int slice, int thread_nb)
      51             : {
      52           4 :     DXVContext *ctx = avctx->priv_data;
      53           4 :     AVFrame *frame = arg;
      54           4 :     const uint8_t *d = ctx->tex_data;
      55           4 :     int w_block = avctx->coded_width / TEXTURE_BLOCK_W;
      56           4 :     int h_block = avctx->coded_height / TEXTURE_BLOCK_H;
      57             :     int x, y;
      58             :     int start_slice, end_slice;
      59           4 :     int base_blocks_per_slice = h_block / ctx->slice_count;
      60           4 :     int remainder_blocks = h_block % ctx->slice_count;
      61             : 
      62             :     /* When the frame height (in blocks) doesn't divide evenly between the
      63             :      * number of slices, spread the remaining blocks evenly between the first
      64             :      * operations */
      65           4 :     start_slice = slice * base_blocks_per_slice;
      66             :     /* Add any extra blocks (one per slice) that have been added
      67             :      * before this slice */
      68           4 :     start_slice += FFMIN(slice, remainder_blocks);
      69             : 
      70           4 :     end_slice = start_slice + base_blocks_per_slice;
      71             :     /* Add an extra block if there are remainder blocks to be accounted for */
      72           4 :     if (slice < remainder_blocks)
      73           0 :         end_slice++;
      74             : 
      75        1092 :     for (y = start_slice; y < end_slice; y++) {
      76        1088 :         uint8_t *p = frame->data[0] + y * frame->linesize[0] * TEXTURE_BLOCK_H;
      77        1088 :         int off  = y * w_block;
      78      523328 :         for (x = 0; x < w_block; x++) {
      79     1044480 :             ctx->tex_funct(p + x * 16, frame->linesize[0],
      80      522240 :                            d + (off + x) * ctx->tex_step);
      81             :         }
      82             :     }
      83             : 
      84           4 :     return 0;
      85             : }
      86             : 
      87             : /* This scheme addresses already decoded elements depending on 2-bit status:
      88             :  *   0 -> copy new element
      89             :  *   1 -> copy one element from position -x
      90             :  *   2 -> copy one element from position -(get_byte() + 2) * x
      91             :  *   3 -> copy one element from position -(get_16le() + 0x102) * x
      92             :  * x is always 2 for dxt1 and 4 for dxt5. */
      93             : #define CHECKPOINT(x)                                                         \
      94             :     do {                                                                      \
      95             :         if (state == 0) {                                                     \
      96             :             value = bytestream2_get_le32(gbc);                                \
      97             :             state = 16;                                                       \
      98             :         }                                                                     \
      99             :         op = value & 0x3;                                                     \
     100             :         value >>= 2;                                                          \
     101             :         state--;                                                              \
     102             :         switch (op) {                                                         \
     103             :         case 1:                                                               \
     104             :             idx = x;                                                          \
     105             :             break;                                                            \
     106             :         case 2:                                                               \
     107             :             idx = (bytestream2_get_byte(gbc) + 2) * x;                        \
     108             :             if (idx > pos) {                                                  \
     109             :                 av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos);       \
     110             :                 return AVERROR_INVALIDDATA;                                   \
     111             :             }                                                                 \
     112             :             break;                                                            \
     113             :         case 3:                                                               \
     114             :             idx = (bytestream2_get_le16(gbc) + 0x102) * x;                    \
     115             :             if (idx > pos) {                                                  \
     116             :                 av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos);       \
     117             :                 return AVERROR_INVALIDDATA;                                   \
     118             :             }                                                                 \
     119             :             break;                                                            \
     120             :         }                                                                     \
     121             :     } while(0)
     122             : 
     123           1 : static int dxv_decompress_dxt1(AVCodecContext *avctx)
     124             : {
     125           1 :     DXVContext *ctx = avctx->priv_data;
     126           1 :     GetByteContext *gbc = &ctx->gbc;
     127             :     uint32_t value, prev, op;
     128           1 :     int idx = 0, state = 0;
     129           1 :     int pos = 2;
     130             : 
     131             :     /* Copy the first two elements */
     132           1 :     AV_WL32(ctx->tex_data, bytestream2_get_le32(gbc));
     133           1 :     AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc));
     134             : 
     135             :     /* Process input until the whole texture has been filled */
     136      130561 :     while (pos + 2 <= ctx->tex_size / 4) {
     137      130559 :         CHECKPOINT(2);
     138             : 
     139             :         /* Copy two elements from a previous offset or from the input buffer */
     140      130559 :         if (op) {
     141      127455 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     142      127455 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     143      127455 :             pos++;
     144             : 
     145      127455 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     146      127455 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     147      127455 :             pos++;
     148             :         } else {
     149        3104 :             CHECKPOINT(2);
     150             : 
     151        3104 :             if (op)
     152         801 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     153             :             else
     154        2303 :                 prev = bytestream2_get_le32(gbc);
     155        3104 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     156        3104 :             pos++;
     157             : 
     158        3104 :             CHECKPOINT(2);
     159             : 
     160        3104 :             if (op)
     161        1598 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     162             :             else
     163        1506 :                 prev = bytestream2_get_le32(gbc);
     164        3104 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     165        3104 :             pos++;
     166             :         }
     167             :     }
     168             : 
     169           1 :     return 0;
     170             : }
     171             : 
     172           1 : static int dxv_decompress_dxt5(AVCodecContext *avctx)
     173             : {
     174           1 :     DXVContext *ctx = avctx->priv_data;
     175           1 :     GetByteContext *gbc = &ctx->gbc;
     176             :     uint32_t value, op;
     177           1 :     int idx, prev, state = 0;
     178           1 :     int pos = 4;
     179           1 :     int run = 0;
     180             :     int probe, check;
     181             : 
     182             :     /* Copy the first four elements */
     183           1 :     AV_WL32(ctx->tex_data +  0, bytestream2_get_le32(gbc));
     184           1 :     AV_WL32(ctx->tex_data +  4, bytestream2_get_le32(gbc));
     185           1 :     AV_WL32(ctx->tex_data +  8, bytestream2_get_le32(gbc));
     186           1 :     AV_WL32(ctx->tex_data + 12, bytestream2_get_le32(gbc));
     187             : 
     188             :     /* Process input until the whole texture has been filled */
     189      125706 :     while (pos + 2 <= ctx->tex_size / 4) {
     190      125704 :         if (run) {
     191      125700 :             run--;
     192             : 
     193      125700 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     194      125700 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     195      125700 :             pos++;
     196      125700 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     197      125700 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     198      125700 :             pos++;
     199             :         } else {
     200           4 :             if (bytestream2_get_bytes_left(gbc) < 1)
     201           0 :                 return AVERROR_INVALIDDATA;
     202           4 :             if (state == 0) {
     203           1 :                 value = bytestream2_get_le32(gbc);
     204           1 :                 state = 16;
     205             :             }
     206           4 :             op = value & 0x3;
     207           4 :             value >>= 2;
     208           4 :             state--;
     209             : 
     210           4 :             switch (op) {
     211           2 :             case 0:
     212             :                 /* Long copy */
     213           2 :                 check = bytestream2_get_byte(gbc) + 1;
     214           2 :                 if (check == 256) {
     215             :                     do {
     216           2 :                         probe = bytestream2_get_le16(gbc);
     217           2 :                         check += probe;
     218           2 :                     } while (probe == 0xFFFF);
     219             :                 }
     220        4861 :                 while (check && pos + 4 <= ctx->tex_size / 4) {
     221        4857 :                     prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     222        4857 :                     AV_WL32(ctx->tex_data + 4 * pos, prev);
     223        4857 :                     pos++;
     224             : 
     225        4857 :                     prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     226        4857 :                     AV_WL32(ctx->tex_data + 4 * pos, prev);
     227        4857 :                     pos++;
     228             : 
     229        4857 :                     prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     230        4857 :                     AV_WL32(ctx->tex_data + 4 * pos, prev);
     231        4857 :                     pos++;
     232             : 
     233        4857 :                     prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     234        4857 :                     AV_WL32(ctx->tex_data + 4 * pos, prev);
     235        4857 :                     pos++;
     236             : 
     237        4857 :                     check--;
     238             :                 }
     239             : 
     240             :                 /* Restart (or exit) the loop */
     241           2 :                 continue;
     242             :                 break;
     243           1 :             case 1:
     244             :                 /* Load new run value */
     245           1 :                 run = bytestream2_get_byte(gbc);
     246           1 :                 if (run == 255) {
     247             :                     do {
     248           2 :                         probe = bytestream2_get_le16(gbc);
     249           2 :                         run += probe;
     250           2 :                     } while (probe == 0xFFFF);
     251             :                 }
     252             : 
     253             :                 /* Copy two dwords from previous data */
     254           1 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     255           1 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     256           1 :                 pos++;
     257             : 
     258           1 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
     259           1 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     260           1 :                 pos++;
     261           1 :                 break;
     262           0 :             case 2:
     263             :                 /* Copy two dwords from a previous index */
     264           0 :                 idx = 8 + bytestream2_get_le16(gbc);
     265           0 :                 if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4)
     266           0 :                     return AVERROR_INVALIDDATA;
     267           0 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     268           0 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     269           0 :                 pos++;
     270             : 
     271           0 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     272           0 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     273           0 :                 pos++;
     274           0 :                 break;
     275           1 :             case 3:
     276             :                 /* Copy two dwords from input */
     277           1 :                 prev = bytestream2_get_le32(gbc);
     278           1 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     279           1 :                 pos++;
     280             : 
     281           1 :                 prev = bytestream2_get_le32(gbc);
     282           1 :                 AV_WL32(ctx->tex_data + 4 * pos, prev);
     283           1 :                 pos++;
     284           1 :                 break;
     285             :             }
     286             :         }
     287             : 
     288      125702 :         CHECKPOINT(4);
     289      125702 :         if (pos + 2 > ctx->tex_size / 4)
     290           0 :             return AVERROR_INVALIDDATA;
     291             : 
     292             :         /* Copy two elements from a previous offset or from the input buffer */
     293      125702 :         if (op) {
     294      122596 :             if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4)
     295           0 :                 return AVERROR_INVALIDDATA;
     296      122596 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     297      122596 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     298      122596 :             pos++;
     299             : 
     300      122596 :             prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     301      122596 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     302      122596 :             pos++;
     303             :         } else {
     304        3106 :             CHECKPOINT(4);
     305             : 
     306        3106 :             if (op && (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4))
     307           0 :                 return AVERROR_INVALIDDATA;
     308        3106 :             if (op)
     309         804 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     310             :             else
     311        2302 :                 prev = bytestream2_get_le32(gbc);
     312        3106 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     313        3106 :             pos++;
     314             : 
     315        3106 :             CHECKPOINT(4);
     316             : 
     317        3106 :             if (op)
     318        1596 :                 prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
     319             :             else
     320        1510 :                 prev = bytestream2_get_le32(gbc);
     321        3106 :             AV_WL32(ctx->tex_data + 4 * pos, prev);
     322        3106 :             pos++;
     323             :         }
     324             :     }
     325             : 
     326           1 :     return 0;
     327             : }
     328             : 
     329           2 : static int dxv_decompress_lzf(AVCodecContext *avctx)
     330             : {
     331           2 :     DXVContext *ctx = avctx->priv_data;
     332           2 :     return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size);
     333             : }
     334             : 
     335           0 : static int dxv_decompress_raw(AVCodecContext *avctx)
     336             : {
     337           0 :     DXVContext *ctx = avctx->priv_data;
     338           0 :     GetByteContext *gbc = &ctx->gbc;
     339             : 
     340           0 :     if (bytestream2_get_bytes_left(gbc) < ctx->tex_size)
     341           0 :         return AVERROR_INVALIDDATA;
     342             : 
     343           0 :     bytestream2_get_buffer(gbc, ctx->tex_data, ctx->tex_size);
     344           0 :     return 0;
     345             : }
     346             : 
     347           4 : static int dxv_decode(AVCodecContext *avctx, void *data,
     348             :                       int *got_frame, AVPacket *avpkt)
     349             : {
     350           4 :     DXVContext *ctx = avctx->priv_data;
     351             :     ThreadFrame tframe;
     352           4 :     GetByteContext *gbc = &ctx->gbc;
     353             :     int (*decompress_tex)(AVCodecContext *avctx);
     354             :     const char *msgcomp, *msgtext;
     355             :     uint32_t tag;
     356           4 :     int version_major, version_minor = 0;
     357           4 :     int size = 0, old_type = 0;
     358             :     int ret;
     359             : 
     360           4 :     bytestream2_init(gbc, avpkt->data, avpkt->size);
     361             : 
     362           4 :     tag = bytestream2_get_le32(gbc);
     363           4 :     switch (tag) {
     364           1 :     case MKBETAG('D', 'X', 'T', '1'):
     365           1 :         decompress_tex = dxv_decompress_dxt1;
     366           1 :         ctx->tex_funct = ctx->texdsp.dxt1_block;
     367           1 :         ctx->tex_rat   = 8;
     368           1 :         ctx->tex_step  = 8;
     369           1 :         msgcomp = "DXTR1";
     370           1 :         msgtext = "DXT1";
     371           1 :         break;
     372           1 :     case MKBETAG('D', 'X', 'T', '5'):
     373           1 :         decompress_tex = dxv_decompress_dxt5;
     374           1 :         ctx->tex_funct = ctx->texdsp.dxt5_block;
     375           1 :         ctx->tex_rat   = 4;
     376           1 :         ctx->tex_step  = 16;
     377           1 :         msgcomp = "DXTR5";
     378           1 :         msgtext = "DXT5";
     379           1 :         break;
     380           0 :     case MKBETAG('Y', 'C', 'G', '6'):
     381             :     case MKBETAG('Y', 'G', '1', '0'):
     382           0 :         avpriv_report_missing_feature(avctx, "Tag 0x%08"PRIX32, tag);
     383           0 :         return AVERROR_PATCHWELCOME;
     384           2 :     default:
     385             :         /* Old version does not have a real header, just size and type. */
     386           2 :         size = tag & 0x00FFFFFF;
     387           2 :         old_type = tag >> 24;
     388           2 :         version_major = (old_type & 0x0F) - 1;
     389             : 
     390           2 :         if (old_type & 0x80) {
     391           0 :             msgcomp = "RAW";
     392           0 :             decompress_tex = dxv_decompress_raw;
     393             :         } else {
     394           2 :             msgcomp = "LZF";
     395           2 :             decompress_tex = dxv_decompress_lzf;
     396             :         }
     397             : 
     398           2 :         if (old_type & 0x40) {
     399           1 :             msgtext = "DXT5";
     400             : 
     401           1 :             ctx->tex_funct = ctx->texdsp.dxt5_block;
     402           1 :             ctx->tex_step  = 16;
     403           1 :         } else if (old_type & 0x20 || version_major == 1) {
     404           1 :             msgtext = "DXT1";
     405             : 
     406           1 :             ctx->tex_funct = ctx->texdsp.dxt1_block;
     407           1 :             ctx->tex_step  = 8;
     408             :         } else {
     409           0 :             av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08"PRIX32")\n.", tag);
     410           0 :             return AVERROR_INVALIDDATA;
     411             :         }
     412           2 :         ctx->tex_rat = 1;
     413           2 :         break;
     414             :     }
     415             : 
     416             :     /* New header is 12 bytes long. */
     417           4 :     if (!old_type) {
     418           2 :         version_major = bytestream2_get_byte(gbc) - 1;
     419           2 :         version_minor = bytestream2_get_byte(gbc);
     420             : 
     421             :         /* Encoder copies texture data when compression is not advantageous. */
     422           2 :         if (bytestream2_get_byte(gbc)) {
     423           0 :             msgcomp = "RAW";
     424           0 :             ctx->tex_rat = 1;
     425           0 :             decompress_tex = dxv_decompress_raw;
     426             :         }
     427             : 
     428           2 :         bytestream2_skip(gbc, 1); // unknown
     429           2 :         size = bytestream2_get_le32(gbc);
     430             :     }
     431           4 :     av_log(avctx, AV_LOG_DEBUG,
     432             :            "%s compression with %s texture (version %d.%d)\n",
     433             :            msgcomp, msgtext, version_major, version_minor);
     434             : 
     435           4 :     if (size != bytestream2_get_bytes_left(gbc)) {
     436           0 :         av_log(avctx, AV_LOG_ERROR,
     437             :                "Incomplete or invalid file (header %d, left %u).\n",
     438             :                size, bytestream2_get_bytes_left(gbc));
     439           0 :         return AVERROR_INVALIDDATA;
     440             :     }
     441             : 
     442           4 :     ctx->tex_size = avctx->coded_width * avctx->coded_height * 4 / ctx->tex_rat;
     443           4 :     ret = av_reallocp(&ctx->tex_data, ctx->tex_size);
     444           4 :     if (ret < 0)
     445           0 :         return ret;
     446             : 
     447             :     /* Decompress texture out of the intermediate compression. */
     448           4 :     ret = decompress_tex(avctx);
     449           4 :     if (ret < 0)
     450           0 :         return ret;
     451             : 
     452           4 :     tframe.f = data;
     453           4 :     ret = ff_thread_get_buffer(avctx, &tframe, 0);
     454           4 :     if (ret < 0)
     455           0 :         return ret;
     456             : 
     457             :     /* Now decompress the texture with the standard functions. */
     458           8 :     avctx->execute2(avctx, decompress_texture_thread,
     459           4 :                     tframe.f, NULL, ctx->slice_count);
     460             : 
     461             :     /* Frame is ready to be output. */
     462           4 :     tframe.f->pict_type = AV_PICTURE_TYPE_I;
     463           4 :     tframe.f->key_frame = 1;
     464           4 :     *got_frame = 1;
     465             : 
     466           4 :     return avpkt->size;
     467             : }
     468             : 
     469           8 : static int dxv_init(AVCodecContext *avctx)
     470             : {
     471           8 :     DXVContext *ctx = avctx->priv_data;
     472           8 :     int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
     473             : 
     474           8 :     if (ret < 0) {
     475           0 :         av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
     476             :                avctx->width, avctx->height);
     477           0 :         return ret;
     478             :     }
     479             : 
     480             :     /* Codec requires 16x16 alignment. */
     481           8 :     avctx->coded_width  = FFALIGN(avctx->width,  16);
     482           8 :     avctx->coded_height = FFALIGN(avctx->height, 16);
     483             : 
     484           8 :     ff_texturedsp_init(&ctx->texdsp);
     485           8 :     avctx->pix_fmt = AV_PIX_FMT_RGBA;
     486             : 
     487           8 :     ctx->slice_count = av_clip(avctx->thread_count, 1,
     488           8 :                                avctx->coded_height / TEXTURE_BLOCK_H);
     489             : 
     490           8 :     return 0;
     491             : }
     492             : 
     493           8 : static int dxv_close(AVCodecContext *avctx)
     494             : {
     495           8 :     DXVContext *ctx = avctx->priv_data;
     496             : 
     497           8 :     av_freep(&ctx->tex_data);
     498             : 
     499           8 :     return 0;
     500             : }
     501             : 
     502             : AVCodec ff_dxv_decoder = {
     503             :     .name           = "dxv",
     504             :     .long_name      = NULL_IF_CONFIG_SMALL("Resolume DXV"),
     505             :     .type           = AVMEDIA_TYPE_VIDEO,
     506             :     .id             = AV_CODEC_ID_DXV,
     507             :     .init           = dxv_init,
     508             :     .decode         = dxv_decode,
     509             :     .close          = dxv_close,
     510             :     .priv_data_size = sizeof(DXVContext),
     511             :     .capabilities   = AV_CODEC_CAP_DR1 |
     512             :                       AV_CODEC_CAP_SLICE_THREADS |
     513             :                       AV_CODEC_CAP_FRAME_THREADS,
     514             :     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
     515             :                       FF_CODEC_CAP_INIT_CLEANUP,
     516             : };

Generated by: LCOV version 1.13