/src/ffmpeg/libavcodec/rl2.c
Line | Count | Source |
1 | | /* |
2 | | * RL2 Video Decoder |
3 | | * Copyright (C) 2008 Sascha Sommer (saschasommer@freenet.de) |
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 | | * RL2 Video Decoder |
25 | | * @author Sascha Sommer (saschasommer@freenet.de) |
26 | | * @see http://wiki.multimedia.cx/index.php?title=RL2 |
27 | | */ |
28 | | |
29 | | #include <string.h> |
30 | | |
31 | | #include "libavutil/internal.h" |
32 | | #include "libavutil/intreadwrite.h" |
33 | | #include "libavutil/mem.h" |
34 | | #include "avcodec.h" |
35 | | #include "codec_internal.h" |
36 | | #include "decode.h" |
37 | | |
38 | | |
39 | 701 | #define EXTRADATA1_SIZE (6 + 256 * 3) ///< video base, clr count, palette |
40 | | |
41 | | typedef struct Rl2Context { |
42 | | AVCodecContext *avctx; |
43 | | |
44 | | uint16_t video_base; ///< initial drawing offset |
45 | | uint32_t clr_count; ///< number of used colors (currently unused) |
46 | | uint8_t *back_frame; ///< background frame |
47 | | uint32_t palette[AVPALETTE_COUNT]; |
48 | | } Rl2Context; |
49 | | |
50 | | /** |
51 | | * Run Length Decode a single 320x200 frame |
52 | | * @param s rl2 context |
53 | | * @param in input buffer |
54 | | * @param size input buffer size |
55 | | * @param out output buffer |
56 | | * @param stride stride of the output buffer |
57 | | * @param video_base offset of the rle data inside the frame |
58 | | */ |
59 | | static void rl2_rle_decode(Rl2Context *s, const uint8_t *in, int size, |
60 | | uint8_t *out, ptrdiff_t stride, int video_base) |
61 | 161k | { |
62 | 161k | int base_x = video_base % s->avctx->width; |
63 | 161k | int base_y = video_base / s->avctx->width; |
64 | 161k | ptrdiff_t stride_adj = stride - s->avctx->width; |
65 | 161k | const uint8_t *back_frame = s->back_frame; |
66 | 161k | const uint8_t *in_end = in + size; |
67 | 161k | const uint8_t *out_end = out + stride * s->avctx->height - stride_adj; |
68 | 161k | uint8_t *line_end; |
69 | | |
70 | | /** copy start of the background frame */ |
71 | 161k | if (s->back_frame) { |
72 | 10.1M | for (int i = 0; i <= base_y; i++) { |
73 | 10.0M | memcpy(out, back_frame, s->avctx->width); |
74 | 10.0M | out += stride; |
75 | 10.0M | back_frame += s->avctx->width; |
76 | 10.0M | } |
77 | 155k | back_frame += base_x - s->avctx->width; |
78 | 155k | } else { |
79 | 5.18k | out += stride * (base_y + 1); |
80 | 5.18k | } |
81 | 161k | line_end = out - stride_adj; |
82 | 161k | out += base_x - stride; |
83 | | |
84 | | /** decode the variable part of the frame */ |
85 | 446k | while (in < in_end) { |
86 | 321k | uint8_t val = *in++; |
87 | 321k | int len = 1; |
88 | 321k | if (val >= 0x80) { |
89 | 71.8k | if (in >= in_end) |
90 | 29.9k | break; |
91 | 41.9k | len = *in++; |
92 | 41.9k | if (!len) |
93 | 4.33k | break; |
94 | 37.5k | val &= 0x7F; |
95 | 37.5k | } |
96 | | |
97 | 286k | if (back_frame) { |
98 | 240k | if (!val) { |
99 | 111k | do { |
100 | 111k | size_t copy = FFMIN(line_end - out, len); |
101 | 111k | memcpy(out, back_frame, copy); |
102 | 111k | out += copy; |
103 | 111k | back_frame += copy; |
104 | 111k | len -= copy; |
105 | 111k | if (out == line_end) { |
106 | 911 | if (out == out_end) |
107 | 254 | return; |
108 | 657 | out += stride_adj; |
109 | 657 | line_end += stride; |
110 | 657 | } |
111 | 111k | } while (len > 0); |
112 | 110k | continue; |
113 | 110k | } |
114 | 129k | back_frame += len; |
115 | 129k | val |= 0x80; |
116 | 129k | } |
117 | | |
118 | 5.91M | while (len--) { |
119 | 5.74M | *out++ = val; |
120 | 5.74M | if (out == line_end) { |
121 | 12.4k | if (out == out_end) |
122 | 1.53k | return; |
123 | 10.9k | out += stride_adj; |
124 | 10.9k | line_end += stride; |
125 | 10.9k | } |
126 | 5.74M | } |
127 | 176k | } |
128 | | |
129 | | /** copy the rest from the background frame */ |
130 | 159k | if (s->back_frame) { |
131 | 21.3M | while (1) { |
132 | 21.3M | memcpy(out, back_frame, line_end - out); |
133 | 21.3M | if (line_end == out_end) |
134 | 155k | break; |
135 | 21.1M | back_frame += line_end - out; |
136 | 21.1M | out = line_end + stride_adj; |
137 | 21.1M | line_end += stride; |
138 | 21.1M | } |
139 | 155k | } |
140 | 159k | } |
141 | | |
142 | | |
143 | | /** |
144 | | * Initialize the decoder |
145 | | * @param avctx decoder context |
146 | | * @return 0 success, -1 on error |
147 | | */ |
148 | | static av_cold int rl2_decode_init(AVCodecContext *avctx) |
149 | 464 | { |
150 | 464 | Rl2Context *s = avctx->priv_data; |
151 | 464 | int back_size; |
152 | 464 | int i; |
153 | 464 | int ret; |
154 | | |
155 | 464 | s->avctx = avctx; |
156 | 464 | avctx->pix_fmt = AV_PIX_FMT_PAL8; |
157 | | |
158 | 464 | ret = ff_set_dimensions(avctx, 320, 200); |
159 | 464 | if (ret < 0) |
160 | 0 | return ret; |
161 | | |
162 | | /** parse extra data */ |
163 | 464 | if (!avctx->extradata || avctx->extradata_size < EXTRADATA1_SIZE) { |
164 | 203 | av_log(avctx, AV_LOG_ERROR, "invalid extradata size\n"); |
165 | 203 | return AVERROR(EINVAL); |
166 | 203 | } |
167 | | |
168 | | /** get frame_offset */ |
169 | 261 | s->video_base = AV_RL16(&avctx->extradata[0]); |
170 | 261 | s->clr_count = AV_RL32(&avctx->extradata[2]); |
171 | | |
172 | 261 | if (s->video_base >= avctx->width * avctx->height) { |
173 | 3 | av_log(avctx, AV_LOG_ERROR, "invalid video_base\n"); |
174 | 3 | return AVERROR_INVALIDDATA; |
175 | 3 | } |
176 | | |
177 | | /** initialize palette */ |
178 | 66.3k | for (i = 0; i < AVPALETTE_COUNT; i++) |
179 | 66.0k | s->palette[i] = 0xFFU << 24 | AV_RB24(&avctx->extradata[6 + i * 3]); |
180 | | |
181 | | /** decode background frame if present */ |
182 | 258 | back_size = avctx->extradata_size - EXTRADATA1_SIZE; |
183 | | |
184 | 258 | if (back_size > 0) { |
185 | | /* The 254 are padding to ensure that pointer arithmetic stays within |
186 | | * the buffer. */ |
187 | 138 | uint8_t *back_frame = av_mallocz(avctx->width * avctx->height + 254); |
188 | 138 | if (!back_frame) |
189 | 0 | return AVERROR(ENOMEM); |
190 | 138 | rl2_rle_decode(s, avctx->extradata + EXTRADATA1_SIZE, back_size, |
191 | 138 | back_frame, avctx->width, 0); |
192 | 138 | s->back_frame = back_frame; |
193 | 138 | } |
194 | 258 | return 0; |
195 | 258 | } |
196 | | |
197 | | |
198 | | static int rl2_decode_frame(AVCodecContext *avctx, AVFrame *frame, |
199 | | int *got_frame, AVPacket *avpkt) |
200 | 161k | { |
201 | 161k | const uint8_t *buf = avpkt->data; |
202 | 161k | int ret, buf_size = avpkt->size; |
203 | 161k | Rl2Context *s = avctx->priv_data; |
204 | | |
205 | 161k | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
206 | 0 | return ret; |
207 | | |
208 | | /** run length decode */ |
209 | 161k | rl2_rle_decode(s, buf, buf_size, frame->data[0], frame->linesize[0], |
210 | 161k | s->video_base); |
211 | | |
212 | | /** make the palette available on the way out */ |
213 | 161k | memcpy(frame->data[1], s->palette, AVPALETTE_SIZE); |
214 | | |
215 | 161k | *got_frame = 1; |
216 | | |
217 | | /** report that the buffer was completely consumed */ |
218 | 161k | return buf_size; |
219 | 161k | } |
220 | | |
221 | | |
222 | | /** |
223 | | * Uninit decoder |
224 | | * @param avctx decoder context |
225 | | * @return 0 success, -1 on error |
226 | | */ |
227 | | static av_cold int rl2_decode_end(AVCodecContext *avctx) |
228 | 258 | { |
229 | 258 | Rl2Context *s = avctx->priv_data; |
230 | | |
231 | 258 | av_freep(&s->back_frame); |
232 | | |
233 | 258 | return 0; |
234 | 258 | } |
235 | | |
236 | | |
237 | | const FFCodec ff_rl2_decoder = { |
238 | | .p.name = "rl2", |
239 | | CODEC_LONG_NAME("RL2 video"), |
240 | | .p.type = AVMEDIA_TYPE_VIDEO, |
241 | | .p.id = AV_CODEC_ID_RL2, |
242 | | .priv_data_size = sizeof(Rl2Context), |
243 | | .init = rl2_decode_init, |
244 | | .close = rl2_decode_end, |
245 | | FF_CODEC_DECODE_CB(rl2_decode_frame), |
246 | | .p.capabilities = AV_CODEC_CAP_DR1, |
247 | | }; |