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