ffmpeg / libavcodec / kgv1dec.c @ d36beb3f
History | View | Annotate | Download (4.33 KB)
1 |
/*
|
---|---|
2 |
* Kega Game Video (KGV1) decoder
|
3 |
* Copyright (c) 2010 Daniel Verkamp
|
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 |
* Kega Game Video decoder
|
25 |
*/
|
26 |
|
27 |
#include "libavutil/intreadwrite.h" |
28 |
#include "libavcore/imgutils.h" |
29 |
#include "avcodec.h" |
30 |
|
31 |
typedef struct { |
32 |
AVCodecContext *avctx; |
33 |
AVFrame pic; |
34 |
uint16_t *prev, *cur; |
35 |
} KgvContext; |
36 |
|
37 |
static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) |
38 |
{ |
39 |
const uint8_t *buf = avpkt->data;
|
40 |
const uint8_t *buf_end = buf + avpkt->size;
|
41 |
KgvContext * const c = avctx->priv_data;
|
42 |
int offsets[7]; |
43 |
uint16_t *out, *prev; |
44 |
int outcnt = 0, maxcnt; |
45 |
int w, h, i;
|
46 |
|
47 |
if (avpkt->size < 2) |
48 |
return -1; |
49 |
|
50 |
w = (buf[0] + 1) * 8; |
51 |
h = (buf[1] + 1) * 8; |
52 |
buf += 2;
|
53 |
|
54 |
if (av_image_check_size(w, h, 0, avctx)) |
55 |
return -1; |
56 |
|
57 |
if (w != avctx->width || h != avctx->height)
|
58 |
avcodec_set_dimensions(avctx, w, h); |
59 |
|
60 |
maxcnt = w * h; |
61 |
|
62 |
out = av_realloc(c->cur, w * h * 2);
|
63 |
if (!out)
|
64 |
return -1; |
65 |
c->cur = out; |
66 |
|
67 |
prev = av_realloc(c->prev, w * h * 2);
|
68 |
if (!prev)
|
69 |
return -1; |
70 |
c->prev = prev; |
71 |
|
72 |
for (i = 0; i < 7; i++) |
73 |
offsets[i] = -1;
|
74 |
|
75 |
while (outcnt < maxcnt && buf_end - 2 > buf) { |
76 |
int code = AV_RL16(buf);
|
77 |
buf += 2;
|
78 |
|
79 |
if (!(code & 0x8000)) { |
80 |
out[outcnt++] = code; // rgb555 pixel coded directly
|
81 |
} else {
|
82 |
int count;
|
83 |
uint16_t *inp; |
84 |
|
85 |
if ((code & 0x6000) == 0x6000) { |
86 |
// copy from previous frame
|
87 |
int oidx = (code >> 10) & 7; |
88 |
int start;
|
89 |
|
90 |
count = (code & 0x3FF) + 3; |
91 |
|
92 |
if (offsets[oidx] < 0) { |
93 |
if (buf_end - 3 < buf) |
94 |
break;
|
95 |
offsets[oidx] = AV_RL24(buf); |
96 |
buf += 3;
|
97 |
} |
98 |
|
99 |
start = (outcnt + offsets[oidx]) % maxcnt; |
100 |
|
101 |
if (maxcnt - start < count)
|
102 |
break;
|
103 |
|
104 |
inp = prev + start; |
105 |
} else {
|
106 |
// copy from earlier in this frame
|
107 |
int offset = (code & 0x1FFF) + 1; |
108 |
|
109 |
if (!(code & 0x6000)) { |
110 |
count = 2;
|
111 |
} else if ((code & 0x6000) == 0x2000) { |
112 |
count = 3;
|
113 |
} else {
|
114 |
if (buf_end - 1 < buf) |
115 |
break;
|
116 |
count = 4 + *buf++;
|
117 |
} |
118 |
|
119 |
if (outcnt < offset)
|
120 |
break;
|
121 |
|
122 |
inp = out + outcnt - offset; |
123 |
} |
124 |
|
125 |
if (maxcnt - outcnt < count)
|
126 |
break;
|
127 |
|
128 |
for (i = 0; i < count; i++) |
129 |
out[outcnt++] = inp[i]; |
130 |
} |
131 |
} |
132 |
|
133 |
if (outcnt - maxcnt)
|
134 |
av_log(avctx, AV_LOG_DEBUG, "frame finished with %d diff\n", outcnt - maxcnt);
|
135 |
|
136 |
c->pic.data[0] = (uint8_t *)c->cur;
|
137 |
c->pic.linesize[0] = w * 2; |
138 |
|
139 |
*data_size = sizeof(AVFrame);
|
140 |
*(AVFrame*)data = c->pic; |
141 |
|
142 |
FFSWAP(uint16_t *, c->cur, c->prev); |
143 |
|
144 |
return avpkt->size;
|
145 |
} |
146 |
|
147 |
static av_cold int decode_init(AVCodecContext *avctx) |
148 |
{ |
149 |
KgvContext * const c = avctx->priv_data;
|
150 |
|
151 |
c->avctx = avctx; |
152 |
avctx->pix_fmt = PIX_FMT_RGB555; |
153 |
|
154 |
return 0; |
155 |
} |
156 |
|
157 |
static av_cold int decode_end(AVCodecContext *avctx) |
158 |
{ |
159 |
KgvContext * const c = avctx->priv_data;
|
160 |
|
161 |
av_freep(&c->cur); |
162 |
av_freep(&c->prev); |
163 |
|
164 |
return 0; |
165 |
} |
166 |
|
167 |
AVCodec ff_kgv1_decoder = { |
168 |
"kgv1",
|
169 |
AVMEDIA_TYPE_VIDEO, |
170 |
CODEC_ID_KGV1, |
171 |
sizeof(KgvContext),
|
172 |
decode_init, |
173 |
NULL,
|
174 |
decode_end, |
175 |
decode_frame, |
176 |
.max_lowres = 1,
|
177 |
.long_name = NULL_IF_CONFIG_SMALL("Kega Game Video"),
|
178 |
}; |