FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/rpza.c
Date: 2022-11-28 23:49:43
Exec Total Coverage
Lines: 93 121 76.9%
Branches: 39 68 57.4%

Line Branch Exec Source
1 /*
2 * Quicktime Video (RPZA) Video Decoder
3 * Copyright (C) 2003 The FFmpeg project
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 /**
23 * @file
24 * QT RPZA Video Decoder by Roberto Togni
25 * For more information about the RPZA format, visit:
26 * http://www.pcisys.net/~melanson/codecs/
27 *
28 * The RPZA decoder outputs RGB555 colorspace data.
29 *
30 * Note that this decoder reads big endian RGB555 pixel values from the
31 * bytestream, arranges them in the host's endian order, and outputs
32 * them to the final rendered map in the same host endian order. This is
33 * intended behavior as the libavcodec documentation states that RGB555
34 * pixels shall be stored in native CPU endianness.
35 */
36
37 #include <stdint.h>
38
39 #include "libavutil/internal.h"
40 #include "avcodec.h"
41 #include "bytestream.h"
42 #include "codec_internal.h"
43 #include "decode.h"
44
45 typedef struct RpzaContext {
46
47 AVCodecContext *avctx;
48 AVFrame *frame;
49
50 GetByteContext gb;
51 } RpzaContext;
52
53 #define CHECK_BLOCK() \
54 if (total_blocks < 1) { \
55 av_log(s->avctx, AV_LOG_ERROR, \
56 "Block counter just went negative (this should not happen)\n"); \
57 return AVERROR_INVALIDDATA; \
58 } \
59
60 #define ADVANCE_BLOCK() \
61 { \
62 pixel_ptr += 4; \
63 if (pixel_ptr >= width) \
64 { \
65 pixel_ptr = 0; \
66 row_ptr += stride * 4; \
67 } \
68 total_blocks--; \
69 }
70
71 31 static int rpza_decode_stream(RpzaContext *s)
72 {
73 31 int width = s->avctx->width;
74 int stride, row_inc, ret;
75 int chunk_size;
76 31 uint16_t colorA = 0, colorB;
77 uint16_t color4[4];
78 uint16_t ta, tb;
79 uint16_t *pixels;
80
81 31 int row_ptr = 0;
82 31 int pixel_ptr = 0;
83 int block_ptr;
84 int pixel_x, pixel_y;
85 int total_blocks;
86
87 /* First byte is always 0xe1. Warn if it's different */
88
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if (bytestream2_peek_byte(&s->gb) != 0xe1)
89 av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n",
90 bytestream2_peek_byte(&s->gb));
91
92 /* Get chunk size, ignoring first byte */
93 31 chunk_size = bytestream2_get_be32(&s->gb) & 0x00FFFFFF;
94
95 /* If length mismatch use size from MOV file and try to decode anyway */
96
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if (chunk_size != bytestream2_get_bytes_left(&s->gb) + 4)
97 av_log(s->avctx, AV_LOG_WARNING,
98 "MOV chunk size %d != encoded chunk size %d\n",
99 chunk_size,
100 bytestream2_get_bytes_left(&s->gb) + 4
101 );
102
103 /* Number of 4x4 blocks in frame. */
104 31 total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
105
106
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if (total_blocks / 32 > bytestream2_get_bytes_left(&s->gb))
107 return AVERROR_INVALIDDATA;
108
109
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if ((ret = ff_reget_buffer(s->avctx, s->frame, 0)) < 0)
110 return ret;
111 31 pixels = (uint16_t *)s->frame->data[0];
112 31 stride = s->frame->linesize[0] / 2;
113 31 row_inc = stride - 4;
114
115 /* Process chunk data */
116
2/2
✓ Branch 1 taken 60173 times.
✓ Branch 2 taken 31 times.
60204 while (bytestream2_get_bytes_left(&s->gb)) {
117 60173 uint8_t opcode = bytestream2_get_byte(&s->gb); /* Get opcode */
118
119 60173 int n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */
120
121 /* If opcode MSbit is 0, we need more data to decide what to do */
122
2/2
✓ Branch 0 taken 48161 times.
✓ Branch 1 taken 12012 times.
60173 if ((opcode & 0x80) == 0) {
123 48161 colorA = (opcode << 8) | bytestream2_get_byte(&s->gb);
124 48161 opcode = 0;
125
1/2
✓ Branch 1 taken 48161 times.
✗ Branch 2 not taken.
48161 if ((bytestream2_peek_byte(&s->gb) & 0x80) != 0) {
126 /* Must behave as opcode 110xxxxx, using colorA computed
127 * above. Use fake opcode 0x20 to enter switch block at
128 * the right place */
129 48161 opcode = 0x20;
130 48161 n_blocks = 1;
131 }
132 }
133
134 60173 n_blocks = FFMIN(n_blocks, total_blocks);
135
136
3/6
✓ Branch 0 taken 10393 times.
✓ Branch 1 taken 1619 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48161 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
60173 switch (opcode & 0xe0) {
137
138 /* Skip blocks */
139 10393 case 0x80:
140
2/2
✓ Branch 0 taken 98305 times.
✓ Branch 1 taken 10393 times.
108698 while (n_blocks--) {
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98305 times.
98305 CHECK_BLOCK();
142
2/2
✓ Branch 0 taken 1632 times.
✓ Branch 1 taken 96673 times.
98305 ADVANCE_BLOCK();
143 }
144 10393 break;
145
146 /* Fill blocks with one color */
147 1619 case 0xa0:
148 1619 colorA = bytestream2_get_be16(&s->gb);
149
2/2
✓ Branch 0 taken 2334 times.
✓ Branch 1 taken 1619 times.
3953 while (n_blocks--) {
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2334 times.
2334 CHECK_BLOCK();
151 2334 block_ptr = row_ptr + pixel_ptr;
152
2/2
✓ Branch 0 taken 9336 times.
✓ Branch 1 taken 2334 times.
11670 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
153
2/2
✓ Branch 0 taken 37344 times.
✓ Branch 1 taken 9336 times.
46680 for (pixel_x = 0; pixel_x < 4; pixel_x++){
154 37344 pixels[block_ptr] = colorA;
155 37344 block_ptr++;
156 }
157 9336 block_ptr += row_inc;
158 }
159
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 2249 times.
2334 ADVANCE_BLOCK();
160 }
161 1619 break;
162
163 /* Fill blocks with 4 colors */
164 case 0xc0:
165 colorA = bytestream2_get_be16(&s->gb);
166 48161 case 0x20:
167 48161 colorB = bytestream2_get_be16(&s->gb);
168
169 /* sort out the colors */
170 48161 color4[0] = colorB;
171 48161 color4[1] = 0;
172 48161 color4[2] = 0;
173 48161 color4[3] = colorA;
174
175 /* red components */
176 48161 ta = (colorA >> 10) & 0x1F;
177 48161 tb = (colorB >> 10) & 0x1F;
178 48161 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
179 48161 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
180
181 /* green components */
182 48161 ta = (colorA >> 5) & 0x1F;
183 48161 tb = (colorB >> 5) & 0x1F;
184 48161 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
185 48161 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
186
187 /* blue components */
188 48161 ta = colorA & 0x1F;
189 48161 tb = colorB & 0x1F;
190 48161 color4[1] |= ((11 * ta + 21 * tb) >> 5);
191 48161 color4[2] |= ((21 * ta + 11 * tb) >> 5);
192
193
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 48161 times.
48161 if (bytestream2_get_bytes_left(&s->gb) < n_blocks * 4)
194 return AVERROR_INVALIDDATA;
195
2/2
✓ Branch 0 taken 48161 times.
✓ Branch 1 taken 48161 times.
96322 while (n_blocks--) {
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48161 times.
48161 CHECK_BLOCK();
197 48161 block_ptr = row_ptr + pixel_ptr;
198
2/2
✓ Branch 0 taken 192644 times.
✓ Branch 1 taken 48161 times.
240805 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
199 192644 uint8_t index = bytestream2_get_byteu(&s->gb);
200
2/2
✓ Branch 0 taken 770576 times.
✓ Branch 1 taken 192644 times.
963220 for (pixel_x = 0; pixel_x < 4; pixel_x++){
201 770576 uint8_t idx = (index >> (2 * (3 - pixel_x))) & 0x03;
202 770576 pixels[block_ptr] = color4[idx];
203 770576 block_ptr++;
204 }
205 192644 block_ptr += row_inc;
206 }
207
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 48018 times.
48161 ADVANCE_BLOCK();
208 }
209 48161 break;
210
211 /* Fill block with 16 colors */
212 case 0x00:
213 if (bytestream2_get_bytes_left(&s->gb) < 30)
214 return AVERROR_INVALIDDATA;
215 CHECK_BLOCK();
216 block_ptr = row_ptr + pixel_ptr;
217 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
218 for (pixel_x = 0; pixel_x < 4; pixel_x++){
219 /* We already have color of upper left pixel */
220 if ((pixel_y != 0) || (pixel_x != 0))
221 colorA = bytestream2_get_be16u(&s->gb);
222 pixels[block_ptr] = colorA;
223 block_ptr++;
224 }
225 block_ptr += row_inc;
226 }
227 ADVANCE_BLOCK();
228 break;
229
230 /* Unknown opcode */
231 default:
232 av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk."
233 " Skip remaining %d bytes of chunk data.\n", opcode,
234 bytestream2_get_bytes_left(&s->gb));
235 return AVERROR_INVALIDDATA;
236 } /* Opcode switch */
237 }
238
239 31 return 0;
240 }
241
242 2 static av_cold int rpza_decode_init(AVCodecContext *avctx)
243 {
244 2 RpzaContext *s = avctx->priv_data;
245
246 2 s->avctx = avctx;
247 2 avctx->pix_fmt = AV_PIX_FMT_RGB555;
248
249 2 s->frame = av_frame_alloc();
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!s->frame)
251 return AVERROR(ENOMEM);
252
253 2 return 0;
254 }
255
256 31 static int rpza_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
257 int *got_frame, AVPacket *avpkt)
258 {
259 31 RpzaContext *s = avctx->priv_data;
260 int ret;
261
262 31 bytestream2_init(&s->gb, avpkt->data, avpkt->size);
263
264 31 ret = rpza_decode_stream(s);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (ret < 0)
266 return ret;
267
268
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 if ((ret = av_frame_ref(rframe, s->frame)) < 0)
269 return ret;
270
271 31 *got_frame = 1;
272
273 /* always report that the buffer was completely consumed */
274 31 return avpkt->size;
275 }
276
277 2 static av_cold int rpza_decode_end(AVCodecContext *avctx)
278 {
279 2 RpzaContext *s = avctx->priv_data;
280
281 2 av_frame_free(&s->frame);
282
283 2 return 0;
284 }
285
286 const FFCodec ff_rpza_decoder = {
287 .p.name = "rpza",
288 CODEC_LONG_NAME("QuickTime video (RPZA)"),
289 .p.type = AVMEDIA_TYPE_VIDEO,
290 .p.id = AV_CODEC_ID_RPZA,
291 .priv_data_size = sizeof(RpzaContext),
292 .init = rpza_decode_init,
293 .close = rpza_decode_end,
294 FF_CODEC_DECODE_CB(rpza_decode_frame),
295 .p.capabilities = AV_CODEC_CAP_DR1,
296 };
297