ffmpeg / libavcodec / rpza.c @ 5509bffa
History | View | Annotate | Download (8.59 KB)
1 |
/*
|
---|---|
2 |
* Quicktime Video (RPZA) Video Decoder
|
3 |
* Copyright (C) 2003 the ffmpeg project
|
4 |
*
|
5 |
* This library is free software; you can redistribute it and/or
|
6 |
* modify it under the terms of the GNU Lesser General Public
|
7 |
* License as published by the Free Software Foundation; either
|
8 |
* version 2 of the License, or (at your option) any later version.
|
9 |
*
|
10 |
* This library is distributed in the hope that it will be useful,
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 |
* Lesser General Public License for more details.
|
14 |
*
|
15 |
* You should have received a copy of the GNU Lesser General Public
|
16 |
* License along with this library; if not, write to the Free Software
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18 |
*
|
19 |
*/
|
20 |
|
21 |
/**
|
22 |
* @file rpza.c
|
23 |
* QT RPZA Video Decoder by Roberto Togni <rtogni@bresciaonline.it>
|
24 |
* For more information about the RPZA format, visit:
|
25 |
* http://www.pcisys.net/~melanson/codecs/
|
26 |
*
|
27 |
* The RPZA decoder outputs RGB555 colorspace data.
|
28 |
*
|
29 |
* Note that this decoder reads big endian RGB555 pixel values from the
|
30 |
* bytestream, arranges them in the host's endian order, and outputs
|
31 |
* them to the final rendered map in the same host endian order. This is
|
32 |
* intended behavior as the ffmpeg documentation states that RGB555 pixels
|
33 |
* shall be stored in native CPU endianness.
|
34 |
*/
|
35 |
|
36 |
#include <stdio.h> |
37 |
#include <stdlib.h> |
38 |
#include <string.h> |
39 |
#include <unistd.h> |
40 |
|
41 |
#include "common.h" |
42 |
#include "avcodec.h" |
43 |
#include "dsputil.h" |
44 |
|
45 |
typedef struct RpzaContext { |
46 |
|
47 |
AVCodecContext *avctx; |
48 |
DSPContext dsp; |
49 |
AVFrame frame; |
50 |
|
51 |
unsigned char *buf; |
52 |
int size;
|
53 |
|
54 |
} RpzaContext; |
55 |
|
56 |
#define ADVANCE_BLOCK() \
|
57 |
{ \ |
58 |
pixel_ptr += 4; \
|
59 |
if (pixel_ptr >= width) \
|
60 |
{ \ |
61 |
pixel_ptr = 0; \
|
62 |
row_ptr += stride * 4; \
|
63 |
} \ |
64 |
total_blocks--; \ |
65 |
if (total_blocks < 0) \ |
66 |
{ \ |
67 |
av_log(s->avctx, AV_LOG_ERROR, "warning: block counter just went negative (this should not happen)\n"); \
|
68 |
return; \
|
69 |
} \ |
70 |
} |
71 |
|
72 |
static void rpza_decode_stream(RpzaContext *s) |
73 |
{ |
74 |
int width = s->avctx->width;
|
75 |
int stride = s->frame.linesize[0] / 2; |
76 |
int row_inc = stride - 4; |
77 |
int stream_ptr = 0; |
78 |
int chunk_size;
|
79 |
unsigned char opcode; |
80 |
int n_blocks;
|
81 |
unsigned short colorA = 0, colorB; |
82 |
unsigned short color4[4]; |
83 |
unsigned char index, idx; |
84 |
unsigned short ta, tb; |
85 |
unsigned short *pixels = (unsigned short *)s->frame.data[0]; |
86 |
|
87 |
int row_ptr = 0; |
88 |
int pixel_ptr = 0; |
89 |
int block_ptr;
|
90 |
int pixel_x, pixel_y;
|
91 |
int total_blocks;
|
92 |
|
93 |
/* First byte is always 0xe1. Warn if it's different */
|
94 |
if (s->buf[stream_ptr] != 0xe1) |
95 |
av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n",
|
96 |
s->buf[stream_ptr]); |
97 |
|
98 |
/* Get chunk size, ingnoring first byte */
|
99 |
chunk_size = BE_32(&s->buf[stream_ptr]) & 0x00FFFFFF;
|
100 |
stream_ptr += 4;
|
101 |
|
102 |
/* If length mismatch use size from MOV file and try to decode anyway */
|
103 |
if (chunk_size != s->size)
|
104 |
av_log(s->avctx, AV_LOG_ERROR, "MOV chunk size != encoded chunk size; using MOV chunk size\n");
|
105 |
|
106 |
chunk_size = s->size; |
107 |
|
108 |
/* Number of 4x4 blocks in frame. */
|
109 |
total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); |
110 |
|
111 |
/* Process chunk data */
|
112 |
while (stream_ptr < chunk_size) {
|
113 |
opcode = s->buf[stream_ptr++]; /* Get opcode */
|
114 |
|
115 |
n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */ |
116 |
|
117 |
/* If opcode MSbit is 0, we need more data to decide what to do */
|
118 |
if ((opcode & 0x80) == 0) { |
119 |
colorA = (opcode << 8) | (s->buf[stream_ptr++]);
|
120 |
opcode = 0;
|
121 |
if ((s->buf[stream_ptr] & 0x80) != 0) { |
122 |
/* Must behave as opcode 110xxxxx, using colorA computed
|
123 |
* above. Use fake opcode 0x20 to enter switch block at
|
124 |
* the right place */
|
125 |
opcode = 0x20;
|
126 |
n_blocks = 1;
|
127 |
} |
128 |
} |
129 |
|
130 |
switch (opcode & 0xe0) { |
131 |
|
132 |
/* Skip blocks */
|
133 |
case 0x80: |
134 |
while (n_blocks--) {
|
135 |
ADVANCE_BLOCK(); |
136 |
} |
137 |
break;
|
138 |
|
139 |
/* Fill blocks with one color */
|
140 |
case 0xa0: |
141 |
colorA = BE_16 (&s->buf[stream_ptr]); |
142 |
stream_ptr += 2;
|
143 |
while (n_blocks--) {
|
144 |
block_ptr = row_ptr + pixel_ptr; |
145 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
146 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
147 |
pixels[block_ptr] = colorA; |
148 |
block_ptr++; |
149 |
} |
150 |
block_ptr += row_inc; |
151 |
} |
152 |
ADVANCE_BLOCK(); |
153 |
} |
154 |
break;
|
155 |
|
156 |
/* Fill blocks with 4 colors */
|
157 |
case 0xc0: |
158 |
colorA = BE_16 (&s->buf[stream_ptr]); |
159 |
stream_ptr += 2;
|
160 |
case 0x20: |
161 |
colorB = BE_16 (&s->buf[stream_ptr]); |
162 |
stream_ptr += 2;
|
163 |
|
164 |
/* sort out the colors */
|
165 |
color4[0] = colorB;
|
166 |
color4[1] = 0; |
167 |
color4[2] = 0; |
168 |
color4[3] = colorA;
|
169 |
|
170 |
/* red components */
|
171 |
ta = (colorA >> 10) & 0x1F; |
172 |
tb = (colorB >> 10) & 0x1F; |
173 |
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; |
174 |
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; |
175 |
|
176 |
/* green components */
|
177 |
ta = (colorA >> 5) & 0x1F; |
178 |
tb = (colorB >> 5) & 0x1F; |
179 |
color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; |
180 |
color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; |
181 |
|
182 |
/* blue components */
|
183 |
ta = colorA & 0x1F;
|
184 |
tb = colorB & 0x1F;
|
185 |
color4[1] |= ((11 * ta + 21 * tb) >> 5); |
186 |
color4[2] |= ((21 * ta + 11 * tb) >> 5); |
187 |
|
188 |
while (n_blocks--) {
|
189 |
block_ptr = row_ptr + pixel_ptr; |
190 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
191 |
index = s->buf[stream_ptr++]; |
192 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
193 |
idx = (index >> (2 * (3 - pixel_x))) & 0x03; |
194 |
pixels[block_ptr] = color4[idx]; |
195 |
block_ptr++; |
196 |
} |
197 |
block_ptr += row_inc; |
198 |
} |
199 |
ADVANCE_BLOCK(); |
200 |
} |
201 |
break;
|
202 |
|
203 |
/* Fill block with 16 colors */
|
204 |
case 0x00: |
205 |
block_ptr = row_ptr + pixel_ptr; |
206 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
207 |
for (pixel_x = 0; pixel_x < 4; pixel_x++){ |
208 |
/* We already have color of upper left pixel */
|
209 |
if ((pixel_y != 0) || (pixel_x !=0)) { |
210 |
colorA = BE_16 (&s->buf[stream_ptr]); |
211 |
stream_ptr += 2;
|
212 |
} |
213 |
pixels[block_ptr] = colorA; |
214 |
block_ptr++; |
215 |
} |
216 |
block_ptr += row_inc; |
217 |
} |
218 |
ADVANCE_BLOCK(); |
219 |
break;
|
220 |
|
221 |
/* Unknown opcode */
|
222 |
default:
|
223 |
av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk."
|
224 |
" Skip remaining %d bytes of chunk data.\n", opcode,
|
225 |
chunk_size - stream_ptr); |
226 |
return;
|
227 |
} /* Opcode switch */
|
228 |
} |
229 |
} |
230 |
|
231 |
static int rpza_decode_init(AVCodecContext *avctx) |
232 |
{ |
233 |
RpzaContext *s = (RpzaContext *)avctx->priv_data; |
234 |
|
235 |
s->avctx = avctx; |
236 |
avctx->pix_fmt = PIX_FMT_RGB555; |
237 |
avctx->has_b_frames = 0;
|
238 |
dsputil_init(&s->dsp, avctx); |
239 |
|
240 |
s->frame.data[0] = NULL; |
241 |
|
242 |
return 0; |
243 |
} |
244 |
|
245 |
static int rpza_decode_frame(AVCodecContext *avctx, |
246 |
void *data, int *data_size, |
247 |
uint8_t *buf, int buf_size)
|
248 |
{ |
249 |
RpzaContext *s = (RpzaContext *)avctx->priv_data; |
250 |
|
251 |
s->buf = buf; |
252 |
s->size = buf_size; |
253 |
|
254 |
s->frame.reference = 1;
|
255 |
s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; |
256 |
if (avctx->reget_buffer(avctx, &s->frame)) {
|
257 |
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
|
258 |
return -1; |
259 |
} |
260 |
|
261 |
rpza_decode_stream(s); |
262 |
|
263 |
*data_size = sizeof(AVFrame);
|
264 |
*(AVFrame*)data = s->frame; |
265 |
|
266 |
/* always report that the buffer was completely consumed */
|
267 |
return buf_size;
|
268 |
} |
269 |
|
270 |
static int rpza_decode_end(AVCodecContext *avctx) |
271 |
{ |
272 |
RpzaContext *s = (RpzaContext *)avctx->priv_data; |
273 |
|
274 |
if (s->frame.data[0]) |
275 |
avctx->release_buffer(avctx, &s->frame); |
276 |
|
277 |
return 0; |
278 |
} |
279 |
|
280 |
AVCodec rpza_decoder = { |
281 |
"rpza",
|
282 |
CODEC_TYPE_VIDEO, |
283 |
CODEC_ID_RPZA, |
284 |
sizeof(RpzaContext),
|
285 |
rpza_decode_init, |
286 |
NULL,
|
287 |
rpza_decode_end, |
288 |
rpza_decode_frame, |
289 |
CODEC_CAP_DR1, |
290 |
}; |