FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/rpza.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 106 121 87.6%
Functions: 4 4 100.0%
Branches: 53 68 77.9%

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 238 static int rpza_decode_stream(RpzaContext *s)
72 {
73 238 int width = s->avctx->width;
74 int stride, row_inc, ret;
75 int chunk_size;
76 238 uint16_t colorA = 0, colorB;
77 uint16_t color4[4];
78 uint16_t ta, tb;
79 uint16_t *pixels;
80
81 238 int row_ptr = 0;
82 238 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 238 times.
238 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 238 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 238 times.
238 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 238 total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
105
106
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
238 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 238 times.
238 if ((ret = ff_reget_buffer(s->avctx, s->frame, 0)) < 0)
110 return ret;
111 238 pixels = (uint16_t *)s->frame->data[0];
112 238 stride = s->frame->linesize[0] / 2;
113 238 row_inc = stride - 4;
114
115 /* Process chunk data */
116
2/2
✓ Branch 1 taken 1026089 times.
✓ Branch 2 taken 238 times.
1026327 while (bytestream2_get_bytes_left(&s->gb)) {
117 1026089 uint8_t opcode = bytestream2_get_byte(&s->gb); /* Get opcode */
118
119 1026089 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 910282 times.
✓ Branch 1 taken 115807 times.
1026089 if ((opcode & 0x80) == 0) {
123 910282 colorA = (opcode << 8) | bytestream2_get_byte(&s->gb);
124 910282 opcode = 0;
125
2/2
✓ Branch 1 taken 59181 times.
✓ Branch 2 taken 851101 times.
910282 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 59181 opcode = 0x20;
130 59181 n_blocks = 1;
131 }
132 }
133
134 1026089 n_blocks = FFMIN(n_blocks, total_blocks);
135
136
4/6
✓ Branch 0 taken 15198 times.
✓ Branch 1 taken 100609 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 59181 times.
✓ Branch 4 taken 851101 times.
✗ Branch 5 not taken.
1026089 switch (opcode & 0xe0) {
137
138 /* Skip blocks */
139 15198 case 0x80:
140
2/2
✓ Branch 0 taken 124418 times.
✓ Branch 1 taken 15198 times.
139616 while (n_blocks--) {
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124418 times.
124418 CHECK_BLOCK();
142
2/2
✓ Branch 0 taken 1978 times.
✓ Branch 1 taken 122440 times.
124418 ADVANCE_BLOCK();
143 }
144 15198 break;
145
146 /* Fill blocks with one color */
147 100609 case 0xa0:
148 100609 colorA = bytestream2_get_be16(&s->gb);
149
2/2
✓ Branch 0 taken 102150 times.
✓ Branch 1 taken 100609 times.
202759 while (n_blocks--) {
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102150 times.
102150 CHECK_BLOCK();
151 102150 block_ptr = row_ptr + pixel_ptr;
152
2/2
✓ Branch 0 taken 408600 times.
✓ Branch 1 taken 102150 times.
510750 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
153
2/2
✓ Branch 0 taken 1634400 times.
✓ Branch 1 taken 408600 times.
2043000 for (pixel_x = 0; pixel_x < 4; pixel_x++){
154 1634400 pixels[block_ptr] = colorA;
155 1634400 block_ptr++;
156 }
157 408600 block_ptr += row_inc;
158 }
159
2/2
✓ Branch 0 taken 1025 times.
✓ Branch 1 taken 101125 times.
102150 ADVANCE_BLOCK();
160 }
161 100609 break;
162
163 /* Fill blocks with 4 colors */
164 case 0xc0:
165 colorA = bytestream2_get_be16(&s->gb);
166 59181 case 0x20:
167 59181 colorB = bytestream2_get_be16(&s->gb);
168
169 /* sort out the colors */
170 59181 color4[0] = colorB;
171 59181 color4[1] = 0;
172 59181 color4[2] = 0;
173 59181 color4[3] = colorA;
174
175 /* red components */
176 59181 ta = (colorA >> 10) & 0x1F;
177 59181 tb = (colorB >> 10) & 0x1F;
178 59181 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
179 59181 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
180
181 /* green components */
182 59181 ta = (colorA >> 5) & 0x1F;
183 59181 tb = (colorB >> 5) & 0x1F;
184 59181 color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
185 59181 color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
186
187 /* blue components */
188 59181 ta = colorA & 0x1F;
189 59181 tb = colorB & 0x1F;
190 59181 color4[1] |= ((11 * ta + 21 * tb) >> 5);
191 59181 color4[2] |= ((21 * ta + 11 * tb) >> 5);
192
193
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 59181 times.
59181 if (bytestream2_get_bytes_left(&s->gb) < n_blocks * 4)
194 return AVERROR_INVALIDDATA;
195
2/2
✓ Branch 0 taken 59181 times.
✓ Branch 1 taken 59181 times.
118362 while (n_blocks--) {
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59181 times.
59181 CHECK_BLOCK();
197 59181 block_ptr = row_ptr + pixel_ptr;
198
2/2
✓ Branch 0 taken 236724 times.
✓ Branch 1 taken 59181 times.
295905 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
199 236724 uint8_t index = bytestream2_get_byteu(&s->gb);
200
2/2
✓ Branch 0 taken 946896 times.
✓ Branch 1 taken 236724 times.
1183620 for (pixel_x = 0; pixel_x < 4; pixel_x++){
201 946896 uint8_t idx = (index >> (2 * (3 - pixel_x))) & 0x03;
202 946896 pixels[block_ptr] = color4[idx];
203 946896 block_ptr++;
204 }
205 236724 block_ptr += row_inc;
206 }
207
2/2
✓ Branch 0 taken 219 times.
✓ Branch 1 taken 58962 times.
59181 ADVANCE_BLOCK();
208 }
209 59181 break;
210
211 /* Fill block with 16 colors */
212 851101 case 0x00:
213
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 851101 times.
851101 if (bytestream2_get_bytes_left(&s->gb) < 30)
214 return AVERROR_INVALIDDATA;
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 851101 times.
851101 CHECK_BLOCK();
216 851101 block_ptr = row_ptr + pixel_ptr;
217
2/2
✓ Branch 0 taken 3404404 times.
✓ Branch 1 taken 851101 times.
4255505 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
218
2/2
✓ Branch 0 taken 13617616 times.
✓ Branch 1 taken 3404404 times.
17022020 for (pixel_x = 0; pixel_x < 4; pixel_x++){
219 /* We already have color of upper left pixel */
220
4/4
✓ Branch 0 taken 3404404 times.
✓ Branch 1 taken 10213212 times.
✓ Branch 2 taken 2553303 times.
✓ Branch 3 taken 851101 times.
13617616 if ((pixel_y != 0) || (pixel_x != 0))
221 12766515 colorA = bytestream2_get_be16u(&s->gb);
222 13617616 pixels[block_ptr] = colorA;
223 13617616 block_ptr++;
224 }
225 3404404 block_ptr += row_inc;
226 }
227
2/2
✓ Branch 0 taken 10308 times.
✓ Branch 1 taken 840793 times.
851101 ADVANCE_BLOCK();
228 851101 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 238 return 0;
240 }
241
242 10 static av_cold int rpza_decode_init(AVCodecContext *avctx)
243 {
244 10 RpzaContext *s = avctx->priv_data;
245
246 10 s->avctx = avctx;
247 10 avctx->pix_fmt = AV_PIX_FMT_RGB555;
248
249 10 s->frame = av_frame_alloc();
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!s->frame)
251 return AVERROR(ENOMEM);
252
253 10 return 0;
254 }
255
256 238 static int rpza_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
257 int *got_frame, AVPacket *avpkt)
258 {
259 238 RpzaContext *s = avctx->priv_data;
260 int ret;
261
262 238 bytestream2_init(&s->gb, avpkt->data, avpkt->size);
263
264 238 ret = rpza_decode_stream(s);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 if (ret < 0)
266 return ret;
267
268
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
238 if ((ret = av_frame_ref(rframe, s->frame)) < 0)
269 return ret;
270
271 238 *got_frame = 1;
272
273 /* always report that the buffer was completely consumed */
274 238 return avpkt->size;
275 }
276
277 10 static av_cold int rpza_decode_end(AVCodecContext *avctx)
278 {
279 10 RpzaContext *s = avctx->priv_data;
280
281 10 av_frame_free(&s->frame);
282
283 10 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