ffmpeg / libavcodec / rpza.c @ d36beb3f
History | View | Annotate | Download (8.58 KB)
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 ffmpeg documentation states that RGB555 pixels
|
34 |
* shall be stored in native CPU endianness.
|
35 |
*/
|
36 |
|
37 |
#include <stdio.h> |
38 |
#include <stdlib.h> |
39 |
#include <string.h> |
40 |
|
41 |
#include "libavutil/intreadwrite.h" |
42 |
#include "avcodec.h" |
43 |
|
44 |
typedef struct RpzaContext { |
45 |
|
46 |
AVCodecContext *avctx; |
47 |
AVFrame frame; |
48 |
|
49 |
const unsigned char *buf; |
50 |
int size;
|
51 |
|
52 |
} RpzaContext; |
53 |
|
54 |
#define ADVANCE_BLOCK() \
|
55 |
{ \ |
56 |
pixel_ptr += 4; \
|
57 |
if (pixel_ptr >= width) \
|
58 |
{ \ |
59 |
pixel_ptr = 0; \
|
60 |
row_ptr += stride * 4; \
|
61 |
} \ |
62 |
total_blocks--; \ |
63 |
if (total_blocks < 0) \ |
64 |
{ \ |
65 |
av_log(s->avctx, AV_LOG_ERROR, "warning: block counter just went negative (this should not happen)\n"); \
|
66 |
return; \
|
67 |
} \ |
68 |
} |
69 |
|
70 |
static void rpza_decode_stream(RpzaContext *s) |
71 |
{ |
72 |
int width = s->avctx->width;
|
73 |
int stride = s->frame.linesize[0] / 2; |
74 |
int row_inc = stride - 4; |
75 |
int stream_ptr = 0; |
76 |
int chunk_size;
|
77 |
unsigned char opcode; |
78 |
int n_blocks;
|
79 |
unsigned short colorA = 0, colorB; |
80 |
unsigned short color4[4]; |
81 |
unsigned char index, idx; |
82 |
unsigned short ta, tb; |
83 |
unsigned short *pixels = (unsigned short *)s->frame.data[0]; |
84 |
|
85 |
int row_ptr = 0; |
86 |
int pixel_ptr = 0; |
87 |
int block_ptr;
|
88 |
int pixel_x, pixel_y;
|
89 |
int total_blocks;
|
90 |
|
91 |
/* First byte is always 0xe1. Warn if it's different */
|
92 |
if (s->buf[stream_ptr] != 0xe1) |
93 |
av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n",
|
94 |
s->buf[stream_ptr]); |
95 |
|
96 |
/* Get chunk size, ingnoring first byte */
|
97 |
chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF;
|
98 |
stream_ptr += 4;
|
99 |
|
100 |
/* If length mismatch use size from MOV file and try to decode anyway */
|
101 |
if (chunk_size != s->size)
|
102 |
av_log(s->avctx, AV_LOG_ERROR, "MOV chunk size != encoded chunk size; using MOV chunk size\n");
|
103 |
|
104 |
chunk_size = s->size; |
105 |
|
106 |
/* Number of 4x4 blocks in frame. */
|
107 |
total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); |
108 |
|
109 |
/* Process chunk data */
|
110 |
while (stream_ptr < chunk_size) {
|
111 |
opcode = s->buf[stream_ptr++]; /* Get opcode */
|
112 |
|
113 |
n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */ |
114 |
|
115 |
/* If opcode MSbit is 0, we need more data to decide what to do */
|
116 |
if ((opcode & 0x80) == 0) { |
117 |
colorA = (opcode << 8) | (s->buf[stream_ptr++]);
|
118 |
opcode = 0;
|
119 |
if ((s->buf[stream_ptr] & 0x80) != 0) { |
120 |
/* Must behave as opcode 110xxxxx, using colorA computed
|
121 |
* above. Use fake opcode 0x20 to enter switch block at
|
122 |
* the right place */
|
123 |
opcode = 0x20;
|
124 |
n_blocks = 1;
|
125 |
} |
126 |
} |
127 |
|
128 |
switch (opcode & 0xe0) { |
129 |
|
130 |
/* Skip blocks */
|
131 |
case 0x80: |
132 |
while (n_blocks--) {
|
133 |
ADVANCE_BLOCK(); |
134 |
} |
135 |
break;
|
136 |
|
137 |
/* Fill blocks with one color */
|
138 |
case 0xa0: |
139 |
colorA = AV_RB16 (&s->buf[stream_ptr]); |
140 |
stream_ptr += 2;
|
141 |
while (n_blocks--) {
|
142 |
block_ptr = row_ptr + pixel_ptr; |
143 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
144 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
145 |
pixels[block_ptr] = colorA; |
146 |
block_ptr++; |
147 |
} |
148 |
block_ptr += row_inc; |
149 |
} |
150 |
ADVANCE_BLOCK(); |
151 |
} |
152 |
break;
|
153 |
|
154 |
/* Fill blocks with 4 colors */
|
155 |
case 0xc0: |
156 |
colorA = AV_RB16 (&s->buf[stream_ptr]); |
157 |
stream_ptr += 2;
|
158 |
case 0x20: |
159 |
colorB = AV_RB16 (&s->buf[stream_ptr]); |
160 |
stream_ptr += 2;
|
161 |
|
162 |
/* sort out the colors */
|
163 |
color4[0] = colorB;
|
164 |
color4[1] = 0; |
165 |
color4[2] = 0; |
166 |
color4[3] = colorA;
|
167 |
|
168 |
/* red components */
|
169 |
ta = (colorA >> 10) & 0x1F; |
170 |
tb = (colorB >> 10) & 0x1F; |
171 |
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; |
172 |
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; |
173 |
|
174 |
/* green components */
|
175 |
ta = (colorA >> 5) & 0x1F; |
176 |
tb = (colorB >> 5) & 0x1F; |
177 |
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; |
178 |
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; |
179 |
|
180 |
/* blue components */
|
181 |
ta = colorA & 0x1F;
|
182 |
tb = colorB & 0x1F;
|
183 |
color4[1] |= ((11 * ta + 21 * tb) >> 5); |
184 |
color4[2] |= ((21 * ta + 11 * tb) >> 5); |
185 |
|
186 |
while (n_blocks--) {
|
187 |
block_ptr = row_ptr + pixel_ptr; |
188 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
189 |
index = s->buf[stream_ptr++]; |
190 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
191 |
idx = (index >> (2 * (3 - pixel_x))) & 0x03; |
192 |
pixels[block_ptr] = color4[idx]; |
193 |
block_ptr++; |
194 |
} |
195 |
block_ptr += row_inc; |
196 |
} |
197 |
ADVANCE_BLOCK(); |
198 |
} |
199 |
break;
|
200 |
|
201 |
/* Fill block with 16 colors */
|
202 |
case 0x00: |
203 |
block_ptr = row_ptr + pixel_ptr; |
204 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
205 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
206 |
/* We already have color of upper left pixel */
|
207 |
if ((pixel_y != 0) || (pixel_x !=0)) { |
208 |
colorA = AV_RB16 (&s->buf[stream_ptr]); |
209 |
stream_ptr += 2;
|
210 |
} |
211 |
pixels[block_ptr] = colorA; |
212 |
block_ptr++; |
213 |
} |
214 |
block_ptr += row_inc; |
215 |
} |
216 |
ADVANCE_BLOCK(); |
217 |
break;
|
218 |
|
219 |
/* Unknown opcode */
|
220 |
default:
|
221 |
av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk."
|
222 |
" Skip remaining %d bytes of chunk data.\n", opcode,
|
223 |
chunk_size - stream_ptr); |
224 |
return;
|
225 |
} /* Opcode switch */
|
226 |
} |
227 |
} |
228 |
|
229 |
static av_cold int rpza_decode_init(AVCodecContext *avctx) |
230 |
{ |
231 |
RpzaContext *s = avctx->priv_data; |
232 |
|
233 |
s->avctx = avctx; |
234 |
avctx->pix_fmt = PIX_FMT_RGB555; |
235 |
|
236 |
s->frame.data[0] = NULL; |
237 |
|
238 |
return 0; |
239 |
} |
240 |
|
241 |
static int rpza_decode_frame(AVCodecContext *avctx, |
242 |
void *data, int *data_size, |
243 |
AVPacket *avpkt) |
244 |
{ |
245 |
const uint8_t *buf = avpkt->data;
|
246 |
int buf_size = avpkt->size;
|
247 |
RpzaContext *s = avctx->priv_data; |
248 |
|
249 |
s->buf = buf; |
250 |
s->size = buf_size; |
251 |
|
252 |
s->frame.reference = 1;
|
253 |
s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; |
254 |
if (avctx->reget_buffer(avctx, &s->frame)) {
|
255 |
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
|
256 |
return -1; |
257 |
} |
258 |
|
259 |
rpza_decode_stream(s); |
260 |
|
261 |
*data_size = sizeof(AVFrame);
|
262 |
*(AVFrame*)data = s->frame; |
263 |
|
264 |
/* always report that the buffer was completely consumed */
|
265 |
return buf_size;
|
266 |
} |
267 |
|
268 |
static av_cold int rpza_decode_end(AVCodecContext *avctx) |
269 |
{ |
270 |
RpzaContext *s = avctx->priv_data; |
271 |
|
272 |
if (s->frame.data[0]) |
273 |
avctx->release_buffer(avctx, &s->frame); |
274 |
|
275 |
return 0; |
276 |
} |
277 |
|
278 |
AVCodec ff_rpza_decoder = { |
279 |
"rpza",
|
280 |
AVMEDIA_TYPE_VIDEO, |
281 |
CODEC_ID_RPZA, |
282 |
sizeof(RpzaContext),
|
283 |
rpza_decode_init, |
284 |
NULL,
|
285 |
rpza_decode_end, |
286 |
rpza_decode_frame, |
287 |
CODEC_CAP_DR1, |
288 |
.long_name = NULL_IF_CONFIG_SMALL("QuickTime video (RPZA)"),
|
289 |
}; |