/src/vlc/modules/codec/cdg.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * cdg.c: CDG decoder module |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2007 Laurent Aimar |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir # via.ecp.fr> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | /***************************************************************************** |
24 | | * Preamble |
25 | | *****************************************************************************/ |
26 | | #ifdef HAVE_CONFIG_H |
27 | | # include "config.h" |
28 | | #endif |
29 | | |
30 | | #include <vlc_common.h> |
31 | | #include <vlc_plugin.h> |
32 | | #include <vlc_codec.h> |
33 | | |
34 | | /***************************************************************************** |
35 | | * decoder_sys_t : decoder descriptor |
36 | | *****************************************************************************/ |
37 | | |
38 | | /* The screen size */ |
39 | 4.55G | #define CDG_SCREEN_WIDTH 300u |
40 | 501M | #define CDG_SCREEN_HEIGHT 216u |
41 | | |
42 | | /* The border of the screen size */ |
43 | 2.93G | #define CDG_SCREEN_BORDER_WIDTH 6u |
44 | 1.47G | #define CDG_SCREEN_BORDER_HEIGHT 12u |
45 | | |
46 | | /* The display part */ |
47 | 1.47G | #define CDG_DISPLAY_WIDTH (CDG_SCREEN_WIDTH-2*CDG_SCREEN_BORDER_WIDTH) |
48 | 5.11M | #define CDG_DISPLAY_HEIGHT (CDG_SCREEN_HEIGHT-2*CDG_SCREEN_BORDER_HEIGHT) |
49 | | |
50 | 2.44G | #define CDG_SCREEN_PITCH CDG_SCREEN_WIDTH |
51 | | |
52 | | typedef struct |
53 | | { |
54 | | uint8_t color[16][3]; |
55 | | unsigned i_offseth; |
56 | | unsigned i_offsetv; |
57 | | uint8_t screen[CDG_SCREEN_PITCH*CDG_SCREEN_HEIGHT]; |
58 | | uint8_t *p_screen; |
59 | | |
60 | | int i_packet; |
61 | | } decoder_sys_t; |
62 | | |
63 | 3.34M | #define CDG_PACKET_SIZE 24u |
64 | | |
65 | 1.46G | #define CDG_COLOR_R_SHIFT 0 |
66 | 1.46G | #define CDG_COLOR_G_SHIFT 8 |
67 | 1.46G | #define CDG_COLOR_B_SHIFT 16 |
68 | | |
69 | | /***************************************************************************** |
70 | | * Local prototypes |
71 | | *****************************************************************************/ |
72 | | static int Open ( vlc_object_t * ); |
73 | | |
74 | | static int Decode( decoder_t *, block_t * ); |
75 | | |
76 | | static int DecodePacket( decoder_sys_t *p_cdg, uint8_t *p_buffer, int i_buffer ); |
77 | | static void Flush( decoder_t * ); |
78 | | static int Render( decoder_sys_t *p_cdg, picture_t *p_picture ); |
79 | | |
80 | | /***************************************************************************** |
81 | | * Module descriptor |
82 | | *****************************************************************************/ |
83 | 166 | vlc_module_begin () |
84 | 83 | set_subcategory( SUBCAT_INPUT_VCODEC ) |
85 | 83 | set_description( N_("CDG video decoder") ) |
86 | 83 | set_capability( "video decoder", 1000 ) |
87 | 83 | set_callback( Open ) |
88 | 83 | add_shortcut( "cdg" ) |
89 | 83 | vlc_module_end () |
90 | | |
91 | | /***************************************************************************** |
92 | | * Open: probe the decoder and return score |
93 | | *****************************************************************************/ |
94 | | static int Open( vlc_object_t *p_this ) |
95 | | { |
96 | | decoder_t *p_dec = (decoder_t*)p_this; |
97 | | decoder_sys_t *p_sys; |
98 | | |
99 | | if( p_dec->fmt_in->i_codec != VLC_CODEC_CDG ) |
100 | | return VLC_EGENERIC; |
101 | | |
102 | | /* Allocate the memory needed to store the decoder's structure */ |
103 | | p_dec->p_sys = p_sys = vlc_obj_calloc( p_this, 1, sizeof(decoder_sys_t) ); |
104 | | if( !p_sys ) |
105 | | return VLC_ENOMEM; |
106 | | |
107 | | /* Init */ |
108 | | p_sys->p_screen = p_sys->screen; |
109 | | p_sys->i_packet = 0; |
110 | | |
111 | | /* Set output properties |
112 | | * TODO maybe it would be better to use RV16 or RV24 ? */ |
113 | | p_dec->fmt_out.i_codec = |
114 | | p_dec->fmt_out.video.i_chroma = VLC_CODEC_XBGR; |
115 | | p_dec->fmt_out.video.i_width = CDG_DISPLAY_WIDTH; |
116 | | p_dec->fmt_out.video.i_height = CDG_DISPLAY_HEIGHT; |
117 | | p_dec->fmt_out.video.i_sar_num = 1; |
118 | | p_dec->fmt_out.video.i_sar_den = 1; |
119 | | |
120 | | /* Set callbacks */ |
121 | | p_dec->pf_decode = Decode; |
122 | | p_dec->pf_flush = Flush; |
123 | | |
124 | | return VLC_SUCCESS; |
125 | | } |
126 | | |
127 | | /***************************************************************************** |
128 | | * Flush: |
129 | | *****************************************************************************/ |
130 | | static void Flush( decoder_t *p_dec ) |
131 | 0 | { |
132 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
133 | |
|
134 | 0 | p_sys->i_packet = 0; |
135 | 0 | } |
136 | | |
137 | | /**************************************************************************** |
138 | | * Decode: the whole thing |
139 | | **************************************************************************** |
140 | | * This function must be fed with a complete compressed frame. |
141 | | ****************************************************************************/ |
142 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
143 | 319k | { |
144 | 319k | decoder_sys_t *p_sys = p_dec->p_sys; |
145 | 319k | picture_t *p_pic = NULL; |
146 | | |
147 | 319k | if( !p_block ) /* No Drain */ |
148 | 159k | return VLCDEC_SUCCESS; |
149 | | |
150 | 159k | if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) |
151 | 0 | { |
152 | 0 | Flush( p_dec ); |
153 | 0 | goto exit; |
154 | 0 | } |
155 | | |
156 | | /* Decode packet */ |
157 | 796k | while( p_block->i_buffer >= CDG_PACKET_SIZE ) |
158 | 637k | { |
159 | 637k | DecodePacket( p_sys, p_block->p_buffer, CDG_PACKET_SIZE ); |
160 | 637k | p_block->i_buffer -= CDG_PACKET_SIZE; |
161 | 637k | p_block->p_buffer += CDG_PACKET_SIZE; |
162 | 637k | } |
163 | | |
164 | | /* Only display 25 frame per second (there is 75 packets per second) */ |
165 | 159k | if( (p_sys->i_packet%3) == 1 && p_block->i_pts == p_block->i_dts ) |
166 | 26.5k | { |
167 | | /* Get a new picture */ |
168 | 26.5k | if( decoder_UpdateVideoFormat( p_dec ) ) |
169 | 0 | goto exit; |
170 | 26.5k | p_pic = decoder_NewPicture( p_dec ); |
171 | 26.5k | if( !p_pic ) |
172 | 0 | goto exit; |
173 | | |
174 | 26.5k | Render( p_sys, p_pic ); |
175 | 26.5k | p_pic->date = p_block->i_pts != VLC_TICK_INVALID ? p_block->i_pts : p_block->i_dts; |
176 | 26.5k | } |
177 | | |
178 | 159k | exit: |
179 | 159k | block_Release( p_block ); |
180 | 159k | if( p_pic != NULL ) |
181 | 26.5k | decoder_QueueVideo( p_dec, p_pic ); |
182 | 159k | return VLCDEC_SUCCESS; |
183 | 159k | } |
184 | | |
185 | | /***************************************************************************** |
186 | | * Decoder |
187 | | *****************************************************************************/ |
188 | | static void ScreenFill( decoder_sys_t *p_cdg, int sx, int sy, int dx, int dy, int c ) |
189 | 20.9k | { |
190 | 20.9k | int x, y; |
191 | 3.04M | for( y = sy; y < sy+dy; y++ ) |
192 | 492M | for( x = sx; x < sx+dx; x++ ) |
193 | 489M | p_cdg->p_screen[y*CDG_SCREEN_PITCH+x] = c; |
194 | 20.9k | } |
195 | | static int DecodeMemoryPreset( decoder_sys_t *p_cdg, const uint8_t *p_data ) |
196 | 3.18k | { |
197 | 3.18k | const int i_color = p_data[0]&0x0f; |
198 | | #if 0 |
199 | | const int i_repeat= p_data[1]&0x0f; |
200 | | #endif |
201 | | /* if i_repeat > 0 we could ignore it if we have a reliable stream */ |
202 | 3.18k | ScreenFill( p_cdg, 0, 0, CDG_SCREEN_WIDTH, CDG_SCREEN_HEIGHT, i_color ); |
203 | 3.18k | return 0; |
204 | 3.18k | } |
205 | | static int DecodeBorderPreset( decoder_sys_t *p_cdg, const uint8_t *p_data ) |
206 | 3.48k | { |
207 | 3.48k | const int i_color = p_data[0]&0x0f; |
208 | | |
209 | | /* */ |
210 | 3.48k | ScreenFill( p_cdg, 0, 0, |
211 | 3.48k | CDG_SCREEN_WIDTH, CDG_SCREEN_BORDER_HEIGHT, i_color ); |
212 | 3.48k | ScreenFill( p_cdg, 0, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, |
213 | 3.48k | CDG_SCREEN_WIDTH, CDG_SCREEN_BORDER_HEIGHT, i_color ); |
214 | 3.48k | ScreenFill( p_cdg, 0, CDG_SCREEN_BORDER_HEIGHT, |
215 | 3.48k | CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, i_color ); |
216 | 3.48k | ScreenFill( p_cdg, CDG_SCREEN_WIDTH-CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_BORDER_HEIGHT, |
217 | 3.48k | CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, i_color ); |
218 | 3.48k | return 0; |
219 | 3.48k | } |
220 | | |
221 | | static int DecodeLoadColorTable( decoder_sys_t *p_cdg, const uint8_t *p_data, int i_base ) |
222 | 3.96k | { |
223 | 3.96k | int n; |
224 | 35.6k | for( n = 0; n < 8; n++ ) |
225 | 31.7k | { |
226 | 31.7k | const int c = ((p_data[2*n+0] << 8) | p_data[2*n+1]); |
227 | 31.7k | const int r = (c >> 10)&0x0f; |
228 | 31.7k | const int g = ((c >> 6)&0x0c) | ((c >> 4)&0x03); |
229 | 31.7k | const int b = c &0x0f; |
230 | | |
231 | 31.7k | p_cdg->color[i_base+n][0] = r << 4; |
232 | 31.7k | p_cdg->color[i_base+n][1] = g << 4; |
233 | 31.7k | p_cdg->color[i_base+n][2] = b << 4; |
234 | 31.7k | } |
235 | 3.96k | return 0; |
236 | 3.96k | } |
237 | | static int DecodeTileBlock( decoder_sys_t *p_cdg, const uint8_t *p_data, int doXor ) |
238 | 5.36k | { |
239 | 5.36k | int p_color[2]; |
240 | 5.36k | int sx, sy; |
241 | 5.36k | int x, y; |
242 | | |
243 | 5.36k | p_color[0] = p_data[0] & 0x0f; |
244 | 5.36k | p_color[1] = p_data[1] & 0x0f; |
245 | | |
246 | 5.36k | sy = (p_data[2] & 0x1f)*12; |
247 | 5.36k | sx = (p_data[3] & 0x3f)*6; |
248 | | |
249 | 59.6k | for( y = 0; y < 12; y++ ) |
250 | 55.1k | { |
251 | 380k | for( x = 0; x < 6; x++ ) |
252 | 326k | { |
253 | 326k | const int idx = ( p_data[4+y] >> (5-x) ) & 0x01; |
254 | | |
255 | 326k | unsigned index = (sy+y)*CDG_SCREEN_PITCH+(sx+x); |
256 | 326k | if( index >= CDG_SCREEN_PITCH*CDG_SCREEN_HEIGHT ) |
257 | 938 | return 0; |
258 | | |
259 | 325k | uint8_t *p = &p_cdg->p_screen[index]; |
260 | | |
261 | 325k | if( doXor ) |
262 | 161k | *p ^= p_color[idx]; |
263 | 164k | else |
264 | 164k | *p = p_color[idx]; |
265 | 325k | } |
266 | 55.1k | } |
267 | 4.42k | return 0; |
268 | 5.36k | } |
269 | | |
270 | | static int DecodeScroll( decoder_sys_t *p_cdg, const uint8_t *p_data, int b_copy ) |
271 | 5.67k | { |
272 | 5.67k | uint8_t copy[CDG_SCREEN_PITCH*CDG_SCREEN_HEIGHT]; |
273 | | |
274 | 5.67k | uint8_t color = p_data[0]&0x0f; |
275 | 5.67k | int i_shifth; |
276 | 5.67k | int i_shiftv; |
277 | | |
278 | | /* */ |
279 | 5.67k | p_cdg->i_offseth = p_data[1]&0x7; |
280 | 5.67k | if( p_cdg->i_offseth >= CDG_SCREEN_BORDER_WIDTH ) |
281 | 2.57k | p_cdg->i_offseth = CDG_SCREEN_BORDER_WIDTH-1; |
282 | | |
283 | 5.67k | p_cdg->i_offsetv = p_data[2]&0xf; |
284 | 5.67k | if( p_cdg->i_offsetv >= CDG_SCREEN_BORDER_HEIGHT ) |
285 | 715 | p_cdg->i_offsetv = CDG_SCREEN_BORDER_HEIGHT-1; |
286 | | |
287 | | /* */ |
288 | 5.67k | switch( (p_data[1] >> 4)&0x3 ) |
289 | 5.67k | { |
290 | 2.52k | case 0x01: i_shifth = 6; break; |
291 | 796 | case 0x02: i_shifth = -6; break; |
292 | 2.35k | default: |
293 | 2.35k | i_shifth = 0; |
294 | 2.35k | break; |
295 | 5.67k | } |
296 | 5.67k | switch( (p_data[2] >> 4)&0x3 ) |
297 | 5.67k | { |
298 | 480 | case 0x01: i_shiftv = 12; break; |
299 | 1.30k | case 0x02: i_shiftv =-12; break; |
300 | 3.88k | default: |
301 | 3.88k | i_shiftv = 0; |
302 | 3.88k | break; |
303 | 5.67k | } |
304 | | |
305 | 5.67k | if( i_shifth == 0 && i_shiftv == 0 ) |
306 | 1.82k | return 0; |
307 | | |
308 | | /* Make a copy of the screen */ |
309 | 3.84k | memcpy( copy, p_cdg->screen, sizeof(p_cdg->screen) ); |
310 | | |
311 | | /* Fill the uncovered part XXX way too much */ |
312 | 3.84k | ScreenFill( p_cdg, 0, 0, CDG_SCREEN_WIDTH, CDG_SCREEN_HEIGHT, color ); |
313 | | |
314 | | /* Copy back */ |
315 | 834k | for( unsigned y = 0; y < CDG_SCREEN_HEIGHT; y++ ) |
316 | 830k | { |
317 | 830k | int dy = i_shiftv + y; |
318 | 250M | for( unsigned x = 0; x < CDG_SCREEN_WIDTH; x++ ) |
319 | 249M | { |
320 | 249M | int dx = i_shifth + x; |
321 | | |
322 | 249M | if( b_copy ) |
323 | 149M | { |
324 | 149M | dy = (dy + CDG_SCREEN_HEIGHT) % CDG_SCREEN_HEIGHT; |
325 | 149M | dx = (dx + CDG_SCREEN_WIDTH ) % CDG_SCREEN_WIDTH; |
326 | 149M | } |
327 | 99.9M | else |
328 | 99.9M | { |
329 | 99.9M | if( dy < 0 || (unsigned)dy >= CDG_SCREEN_HEIGHT || |
330 | 94.9M | dx < 0 || (unsigned)dx >= CDG_SCREEN_WIDTH ) |
331 | 6.72M | continue; |
332 | 99.9M | } |
333 | 242M | p_cdg->screen[dy*CDG_SCREEN_PITCH+dx] = copy[y*CDG_SCREEN_PITCH+x]; |
334 | 242M | } |
335 | 830k | } |
336 | | /* */ |
337 | | //CdgDebug( CDG_LOG_WARNING, "DecodeScroll: color=%d ch=%d oh=%d cv=%d ov=%d\n copy=%d\n", color, i_shifth, p_cdg->i_offseth, i_shiftv, p_cdg->i_offsetv, b_copy ); |
338 | 3.84k | return 0; |
339 | 5.67k | } |
340 | | |
341 | | static int DecodePacket( decoder_sys_t *p_cdg, uint8_t *p_buffer, int i_buffer ) |
342 | 637k | { |
343 | 637k | const int i_cmd = p_buffer[0] & 0x3f; |
344 | 637k | const int i_instruction = p_buffer[1] & 0x3f; |
345 | 637k | const uint8_t *p_data = &p_buffer[4]; |
346 | | |
347 | 637k | if( i_buffer != CDG_PACKET_SIZE ) |
348 | 0 | return -1; |
349 | | |
350 | 637k | p_cdg->i_packet++; |
351 | | |
352 | | /* Handle CDG command only */ |
353 | 637k | if( i_cmd != 0x09 ) |
354 | 592k | return 0; |
355 | | |
356 | 44.5k | switch( i_instruction ) |
357 | 44.5k | { |
358 | 3.18k | case 1: |
359 | 3.18k | DecodeMemoryPreset( p_cdg, p_data ); |
360 | 3.18k | break; |
361 | 3.48k | case 2: |
362 | 3.48k | DecodeBorderPreset( p_cdg, p_data ); |
363 | 3.48k | break; |
364 | 2.94k | case 6: |
365 | 2.94k | DecodeTileBlock( p_cdg, p_data, 0 ); |
366 | 2.94k | break; |
367 | 2.61k | case 20: |
368 | 2.61k | DecodeScroll( p_cdg, p_data, 0 ); |
369 | 2.61k | break; |
370 | 3.05k | case 24: |
371 | 3.05k | DecodeScroll( p_cdg, p_data, 1 ); |
372 | 3.05k | break; |
373 | 80 | case 28: |
374 | | /* FIXME what to do about that one ? */ |
375 | | //CdgDebug( CDG_LOG_WARNING, "DecodeDefineTransparentColor not implemented\n" ); |
376 | | //DecodeDefineTransparentColor( p_cdg, p_data ); |
377 | 80 | break; |
378 | 1.91k | case 30: |
379 | 1.91k | DecodeLoadColorTable( p_cdg, p_data, 0 ); |
380 | 1.91k | break; |
381 | 2.04k | case 31: |
382 | 2.04k | DecodeLoadColorTable( p_cdg, p_data, 8 ); |
383 | 2.04k | break; |
384 | 2.42k | case 38: |
385 | 2.42k | DecodeTileBlock( p_cdg, p_data, 1 ); |
386 | 2.42k | break; |
387 | 22.8k | default: |
388 | 22.8k | break; |
389 | 44.5k | } |
390 | 44.5k | return 0; |
391 | 44.5k | } |
392 | | |
393 | | static void RenderPixel( picture_t *p_picture, int x, int y, uint32_t c ) |
394 | 1.46G | { |
395 | 1.46G | const int i_plane = 0; |
396 | 1.46G | const int i_pitch = p_picture->p[i_plane].i_pitch; |
397 | 1.46G | uint32_t *s = (uint32_t*)&p_picture->p[i_plane].p_pixels[y*i_pitch + x*sizeof(uint32_t)]; |
398 | 1.46G | *s = c; |
399 | 1.46G | } |
400 | | static uint32_t RenderRGB( int r, int g, int b ) |
401 | 1.46G | { |
402 | 1.46G | return ( r << CDG_COLOR_R_SHIFT ) | ( g << CDG_COLOR_G_SHIFT ) | ( b << CDG_COLOR_B_SHIFT ); |
403 | 1.46G | } |
404 | | |
405 | | static int Render( decoder_sys_t *p_cdg, picture_t *p_picture ) |
406 | 26.5k | { |
407 | 5.11M | for( unsigned y = 0; y < CDG_DISPLAY_HEIGHT; y++ ) |
408 | 5.09M | { |
409 | 1.47G | for( unsigned x = 0; x < CDG_DISPLAY_WIDTH; x++ ) |
410 | 1.46G | { |
411 | 1.46G | const int sx = x + p_cdg->i_offseth + CDG_SCREEN_BORDER_WIDTH; |
412 | 1.46G | const int sy = y + p_cdg->i_offsetv + CDG_SCREEN_BORDER_HEIGHT; |
413 | 1.46G | uint8_t cidx = p_cdg->p_screen[sy*CDG_SCREEN_PITCH +sx]; |
414 | 1.46G | uint8_t r = p_cdg->color[cidx][0]; |
415 | 1.46G | uint8_t g = p_cdg->color[cidx][1]; |
416 | 1.46G | uint8_t b = p_cdg->color[cidx][2]; |
417 | | |
418 | 1.46G | RenderPixel( p_picture, x, y, RenderRGB( r, g, b ) ); |
419 | 1.46G | } |
420 | 5.09M | } |
421 | 26.5k | return 0; |
422 | 26.5k | } |
423 | | |