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 |