/src/ffmpeg/libavcodec/vbndec.c
Line | Count | Source |
1 | | /* |
2 | | * Vizrt Binary Image decoder |
3 | | * |
4 | | * This file is part of FFmpeg. |
5 | | * |
6 | | * FFmpeg is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * FFmpeg is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with FFmpeg; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | /** |
22 | | * @file |
23 | | * Vizrt Binary Image decoder |
24 | | */ |
25 | | |
26 | | #include "avcodec.h" |
27 | | #include "bytestream.h" |
28 | | #include "codec_internal.h" |
29 | | #include "decode.h" |
30 | | #include "texturedsp.h" |
31 | | #include "vbn.h" |
32 | | #include "libavutil/imgutils.h" |
33 | | #include "libavutil/mem.h" |
34 | | |
35 | | typedef struct VBNContext { |
36 | | TextureDSPContext texdsp; |
37 | | TextureDSPThreadContext dec; |
38 | | } VBNContext; |
39 | | |
40 | | static av_cold int vbn_init(AVCodecContext *avctx) |
41 | 1.48k | { |
42 | 1.48k | VBNContext *ctx = avctx->priv_data; |
43 | 1.48k | ff_texturedsp_init(&ctx->texdsp); |
44 | 1.48k | return 0; |
45 | 1.48k | } |
46 | | |
47 | | static int decompress(AVCodecContext *avctx, GetByteContext *gb, |
48 | | int compression, uint8_t **outbuf) |
49 | 9.57k | { |
50 | 9.57k | if (compression == VBN_COMPRESSION_NONE) // outbuf is left NULL because gb->buf can be used directly |
51 | 8.73k | return bytestream2_get_bytes_left(gb); |
52 | | |
53 | 840 | av_log(avctx, AV_LOG_ERROR, "Unsupported VBN compression: 0x%08x\n", compression); |
54 | 840 | return AVERROR_PATCHWELCOME; |
55 | 9.57k | } |
56 | | |
57 | | static int vbn_decode_frame(AVCodecContext *avctx, |
58 | | AVFrame *frame, int *got_frame, |
59 | | AVPacket *avpkt) |
60 | 155k | { |
61 | 155k | VBNContext *ctx = avctx->priv_data; |
62 | 155k | GetByteContext gb0, *const gb = &gb0; |
63 | 155k | uint8_t *image_buf = NULL; |
64 | 155k | int image_len; |
65 | 155k | int width, height, components, format, compression, pix_fmt, linesize, data_size; |
66 | 155k | int ret; |
67 | | |
68 | 155k | bytestream2_init(gb, avpkt->data, avpkt->size); |
69 | | |
70 | 155k | if (bytestream2_get_bytes_left(gb) < VBN_HEADER_SIZE) { |
71 | 139k | av_log(avctx, AV_LOG_ERROR, "VBN header truncated\n"); |
72 | 139k | return AVERROR_INVALIDDATA; |
73 | 139k | } |
74 | | |
75 | 15.7k | if (bytestream2_get_le32u(gb) != VBN_MAGIC || |
76 | 14.8k | bytestream2_get_le32u(gb) != VBN_MAJOR || |
77 | 14.5k | bytestream2_get_le32u(gb) != VBN_MINOR) { |
78 | 1.53k | av_log(avctx, AV_LOG_ERROR, "Invalid VBN header\n"); |
79 | 1.53k | return AVERROR_INVALIDDATA; |
80 | 1.53k | } |
81 | | |
82 | 14.2k | width = bytestream2_get_le32u(gb); |
83 | 14.2k | height = bytestream2_get_le32u(gb); |
84 | 14.2k | components = bytestream2_get_le32u(gb); |
85 | 14.2k | format = bytestream2_get_le32u(gb); |
86 | 14.2k | pix_fmt = bytestream2_get_le32u(gb); |
87 | 14.2k | bytestream2_get_le32u(gb); // mipmaps |
88 | 14.2k | data_size = bytestream2_get_le32u(gb); |
89 | 14.2k | bytestream2_seek(gb, VBN_HEADER_SIZE, SEEK_SET); |
90 | | |
91 | 14.2k | compression = format & 0xffffff00; |
92 | 14.2k | format = format & 0xff; |
93 | | |
94 | 14.2k | if (data_size != bytestream2_get_bytes_left(gb)) { |
95 | 1.19k | av_log(avctx, AV_LOG_ERROR, "Truncated packet\n"); |
96 | 1.19k | return AVERROR_INVALIDDATA; |
97 | 1.19k | } |
98 | | |
99 | 13.0k | if (pix_fmt != VBN_PIX_RGBA && pix_fmt != VBN_PIX_RGB) { |
100 | 248 | av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format: 0x%08x\n", pix_fmt); |
101 | 248 | return AVERROR_PATCHWELCOME; |
102 | 248 | } |
103 | | |
104 | 12.7k | ret = ff_set_dimensions(avctx, width, height); |
105 | 12.7k | if (ret < 0) |
106 | 1.88k | return ret; |
107 | | |
108 | 10.9k | if (format == VBN_FORMAT_RAW) { |
109 | 8.38k | if (pix_fmt == VBN_PIX_RGB && components == 3) { |
110 | 7.59k | avctx->pix_fmt = AV_PIX_FMT_RGB24; |
111 | 7.59k | linesize = avctx->width * 3; |
112 | 7.59k | } else if (pix_fmt == VBN_PIX_RGBA && components == 4) { |
113 | 263 | avctx->pix_fmt = AV_PIX_FMT_RGBA; |
114 | 263 | linesize = avctx->width * 4; |
115 | 530 | } else { |
116 | 530 | av_log(avctx, AV_LOG_ERROR, "Unsupported number of components: %d\n", components); |
117 | 530 | return AVERROR_PATCHWELCOME; |
118 | 530 | } |
119 | 8.38k | } else if (format == VBN_FORMAT_DXT1 || format == VBN_FORMAT_DXT5) { |
120 | 2.30k | if (avctx->width % TEXTURE_BLOCK_W || avctx->height % TEXTURE_BLOCK_H) { |
121 | 595 | av_log(avctx, AV_LOG_ERROR, "DXTx compression only supports 4 pixel aligned resolutions\n"); |
122 | 595 | return AVERROR_INVALIDDATA; |
123 | 595 | } |
124 | | |
125 | 1.71k | avctx->pix_fmt = AV_PIX_FMT_RGBA; |
126 | 1.71k | if (format == VBN_FORMAT_DXT1) { |
127 | 1.33k | ctx->dec.tex_funct = ctx->texdsp.dxt1_block; |
128 | 1.33k | ctx->dec.tex_ratio = 8; |
129 | 1.33k | linesize = avctx->coded_width / 2; |
130 | 1.33k | } else { |
131 | 382 | ctx->dec.tex_funct = ctx->texdsp.dxt5_block; |
132 | 382 | ctx->dec.tex_ratio = 16; |
133 | 382 | linesize = avctx->coded_width; |
134 | 382 | } |
135 | 1.71k | } else { |
136 | 217 | av_log(avctx, AV_LOG_ERROR, "Unsupported VBN format: 0x%02x\n", format); |
137 | 217 | return AVERROR_PATCHWELCOME; |
138 | 217 | } |
139 | | |
140 | 9.57k | image_len = decompress(avctx, gb, compression, &image_buf); |
141 | 9.57k | if (image_len < 0) |
142 | 840 | return image_len; |
143 | | |
144 | 8.73k | if (image_len < linesize * avctx->coded_height) { |
145 | 391 | av_log(avctx, AV_LOG_ERROR, "Insufficient data\n"); |
146 | 391 | ret = AVERROR_INVALIDDATA; |
147 | 391 | goto out; |
148 | 391 | } |
149 | | |
150 | 8.34k | ret = ff_get_buffer(avctx, frame, 0); |
151 | 8.34k | if (ret < 0) |
152 | 0 | goto out; |
153 | | |
154 | 8.34k | if (format == VBN_FORMAT_RAW) { |
155 | 7.34k | uint8_t *flipped = frame->data[0] + frame->linesize[0] * (frame->height - 1); |
156 | 7.34k | av_image_copy_plane(flipped, -frame->linesize[0], image_buf ? image_buf : gb->buffer, linesize, linesize, frame->height); |
157 | 7.34k | } else { |
158 | 994 | ctx->dec.slice_count = av_clip(avctx->thread_count, 1, avctx->coded_height / TEXTURE_BLOCK_H); |
159 | 994 | ctx->dec.tex_data.in = image_buf ? image_buf : gb->buffer; |
160 | 994 | ctx->dec.raw_ratio = 16; |
161 | 994 | ctx->dec.frame_data.out = frame->data[0] + frame->linesize[0] * (frame->height - 1); |
162 | 994 | ctx->dec.stride = -frame->linesize[0]; |
163 | 994 | ctx->dec.width = avctx->coded_width; |
164 | 994 | ctx->dec.height = avctx->coded_height; |
165 | 994 | ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec); |
166 | 994 | } |
167 | | |
168 | 8.34k | *got_frame = 1; |
169 | 8.34k | ret = avpkt->size; |
170 | | |
171 | 8.73k | out: |
172 | 8.73k | av_freep(&image_buf); |
173 | 8.73k | return ret; |
174 | 8.34k | } |
175 | | |
176 | | const FFCodec ff_vbn_decoder = { |
177 | | .p.name = "vbn", |
178 | | CODEC_LONG_NAME("Vizrt Binary Image"), |
179 | | .p.type = AVMEDIA_TYPE_VIDEO, |
180 | | .p.id = AV_CODEC_ID_VBN, |
181 | | .init = vbn_init, |
182 | | FF_CODEC_DECODE_CB(vbn_decode_frame), |
183 | | .priv_data_size = sizeof(VBNContext), |
184 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, |
185 | | }; |