/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 | 0 | #define CDG_SCREEN_WIDTH 300u |
40 | 0 | #define CDG_SCREEN_HEIGHT 216u |
41 | | |
42 | | /* The border of the screen size */ |
43 | 0 | #define CDG_SCREEN_BORDER_WIDTH 6u |
44 | 0 | #define CDG_SCREEN_BORDER_HEIGHT 12u |
45 | | |
46 | | /* The display part */ |
47 | 0 | #define CDG_DISPLAY_WIDTH (CDG_SCREEN_WIDTH-2*CDG_SCREEN_BORDER_WIDTH) |
48 | 0 | #define CDG_DISPLAY_HEIGHT (CDG_SCREEN_HEIGHT-2*CDG_SCREEN_BORDER_HEIGHT) |
49 | | |
50 | 0 | #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 | 0 | #define CDG_PACKET_SIZE 24u |
64 | | |
65 | 0 | #define CDG_COLOR_R_SHIFT 0 |
66 | 0 | #define CDG_COLOR_G_SHIFT 8 |
67 | 0 | #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 | 104 | vlc_module_begin () |
84 | 52 | set_subcategory( SUBCAT_INPUT_VCODEC ) |
85 | 52 | set_description( N_("CDG video decoder") ) |
86 | 52 | set_capability( "video decoder", 1000 ) |
87 | 52 | set_callback( Open ) |
88 | 52 | add_shortcut( "cdg" ) |
89 | 52 | 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 | 0 | { |
144 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
145 | 0 | picture_t *p_pic = NULL; |
146 | |
|
147 | 0 | if( !p_block ) /* No Drain */ |
148 | 0 | return VLCDEC_SUCCESS; |
149 | | |
150 | 0 | 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 | 0 | while( p_block->i_buffer >= CDG_PACKET_SIZE ) |
158 | 0 | { |
159 | 0 | DecodePacket( p_sys, p_block->p_buffer, CDG_PACKET_SIZE ); |
160 | 0 | p_block->i_buffer -= CDG_PACKET_SIZE; |
161 | 0 | p_block->p_buffer += CDG_PACKET_SIZE; |
162 | 0 | } |
163 | | |
164 | | /* Only display 25 frame per second (there is 75 packets per second) */ |
165 | 0 | if( (p_sys->i_packet%3) == 1 && p_block->i_pts == p_block->i_dts ) |
166 | 0 | { |
167 | | /* Get a new picture */ |
168 | 0 | if( decoder_UpdateVideoFormat( p_dec ) ) |
169 | 0 | goto exit; |
170 | 0 | p_pic = decoder_NewPicture( p_dec ); |
171 | 0 | if( !p_pic ) |
172 | 0 | goto exit; |
173 | | |
174 | 0 | Render( p_sys, p_pic ); |
175 | 0 | p_pic->date = p_block->i_pts != VLC_TICK_INVALID ? p_block->i_pts : p_block->i_dts; |
176 | 0 | } |
177 | | |
178 | 0 | exit: |
179 | 0 | block_Release( p_block ); |
180 | 0 | if( p_pic != NULL ) |
181 | 0 | decoder_QueueVideo( p_dec, p_pic ); |
182 | 0 | return VLCDEC_SUCCESS; |
183 | 0 | } |
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 | 0 | { |
190 | 0 | int x, y; |
191 | 0 | for( y = sy; y < sy+dy; y++ ) |
192 | 0 | for( x = sx; x < sx+dx; x++ ) |
193 | 0 | p_cdg->p_screen[y*CDG_SCREEN_PITCH+x] = c; |
194 | 0 | } |
195 | | static int DecodeMemoryPreset( decoder_sys_t *p_cdg, const uint8_t *p_data ) |
196 | 0 | { |
197 | 0 | 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 | 0 | ScreenFill( p_cdg, 0, 0, CDG_SCREEN_WIDTH, CDG_SCREEN_HEIGHT, i_color ); |
203 | 0 | return 0; |
204 | 0 | } |
205 | | static int DecodeBorderPreset( decoder_sys_t *p_cdg, const uint8_t *p_data ) |
206 | 0 | { |
207 | 0 | const int i_color = p_data[0]&0x0f; |
208 | | |
209 | | /* */ |
210 | 0 | ScreenFill( p_cdg, 0, 0, |
211 | 0 | CDG_SCREEN_WIDTH, CDG_SCREEN_BORDER_HEIGHT, i_color ); |
212 | 0 | ScreenFill( p_cdg, 0, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, |
213 | 0 | CDG_SCREEN_WIDTH, CDG_SCREEN_BORDER_HEIGHT, i_color ); |
214 | 0 | ScreenFill( p_cdg, 0, CDG_SCREEN_BORDER_HEIGHT, |
215 | 0 | CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, i_color ); |
216 | 0 | ScreenFill( p_cdg, CDG_SCREEN_WIDTH-CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_BORDER_HEIGHT, |
217 | 0 | CDG_SCREEN_BORDER_WIDTH, CDG_SCREEN_HEIGHT-CDG_SCREEN_BORDER_HEIGHT, i_color ); |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | | static int DecodeLoadColorTable( decoder_sys_t *p_cdg, const uint8_t *p_data, int i_base ) |
222 | 0 | { |
223 | 0 | int n; |
224 | 0 | for( n = 0; n < 8; n++ ) |
225 | 0 | { |
226 | 0 | const int c = ((p_data[2*n+0] << 8) | p_data[2*n+1]); |
227 | 0 | const int r = (c >> 10)&0x0f; |
228 | 0 | const int g = ((c >> 6)&0x0c) | ((c >> 4)&0x03); |
229 | 0 | const int b = c &0x0f; |
230 | |
|
231 | 0 | p_cdg->color[i_base+n][0] = r << 4; |
232 | 0 | p_cdg->color[i_base+n][1] = g << 4; |
233 | 0 | p_cdg->color[i_base+n][2] = b << 4; |
234 | 0 | } |
235 | 0 | return 0; |
236 | 0 | } |
237 | | static int DecodeTileBlock( decoder_sys_t *p_cdg, const uint8_t *p_data, int doXor ) |
238 | 0 | { |
239 | 0 | int p_color[2]; |
240 | 0 | int sx, sy; |
241 | 0 | int x, y; |
242 | |
|
243 | 0 | p_color[0] = p_data[0] & 0x0f; |
244 | 0 | p_color[1] = p_data[1] & 0x0f; |
245 | |
|
246 | 0 | sy = (p_data[2] & 0x1f)*12; |
247 | 0 | sx = (p_data[3] & 0x3f)*6; |
248 | |
|
249 | 0 | for( y = 0; y < 12; y++ ) |
250 | 0 | { |
251 | 0 | for( x = 0; x < 6; x++ ) |
252 | 0 | { |
253 | 0 | const int idx = ( p_data[4+y] >> (5-x) ) & 0x01; |
254 | |
|
255 | 0 | unsigned index = (sy+y)*CDG_SCREEN_PITCH+(sx+x); |
256 | 0 | if( index >= CDG_SCREEN_PITCH*CDG_SCREEN_HEIGHT ) |
257 | 0 | return 0; |
258 | | |
259 | 0 | uint8_t *p = &p_cdg->p_screen[index]; |
260 | |
|
261 | 0 | if( doXor ) |
262 | 0 | *p ^= p_color[idx]; |
263 | 0 | else |
264 | 0 | *p = p_color[idx]; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | return 0; |
268 | 0 | } |
269 | | |
270 | | static int DecodeScroll( decoder_sys_t *p_cdg, const uint8_t *p_data, int b_copy ) |
271 | 0 | { |
272 | 0 | uint8_t copy[CDG_SCREEN_PITCH*CDG_SCREEN_HEIGHT]; |
273 | |
|
274 | 0 | uint8_t color = p_data[0]&0x0f; |
275 | 0 | int i_shifth; |
276 | 0 | int i_shiftv; |
277 | | |
278 | | /* */ |
279 | 0 | p_cdg->i_offseth = p_data[1]&0x7; |
280 | 0 | if( p_cdg->i_offseth >= CDG_SCREEN_BORDER_WIDTH ) |
281 | 0 | p_cdg->i_offseth = CDG_SCREEN_BORDER_WIDTH-1; |
282 | |
|
283 | 0 | p_cdg->i_offsetv = p_data[2]&0xf; |
284 | 0 | if( p_cdg->i_offsetv >= CDG_SCREEN_BORDER_HEIGHT ) |
285 | 0 | p_cdg->i_offsetv = CDG_SCREEN_BORDER_HEIGHT-1; |
286 | | |
287 | | /* */ |
288 | 0 | switch( (p_data[1] >> 4)&0x3 ) |
289 | 0 | { |
290 | 0 | case 0x01: i_shifth = 6; break; |
291 | 0 | case 0x02: i_shifth = -6; break; |
292 | 0 | default: |
293 | 0 | i_shifth = 0; |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | switch( (p_data[2] >> 4)&0x3 ) |
297 | 0 | { |
298 | 0 | case 0x01: i_shiftv = 12; break; |
299 | 0 | case 0x02: i_shiftv =-12; break; |
300 | 0 | default: |
301 | 0 | i_shiftv = 0; |
302 | 0 | break; |
303 | 0 | } |
304 | | |
305 | 0 | if( i_shifth == 0 && i_shiftv == 0 ) |
306 | 0 | return 0; |
307 | | |
308 | | /* Make a copy of the screen */ |
309 | 0 | memcpy( copy, p_cdg->screen, sizeof(p_cdg->screen) ); |
310 | | |
311 | | /* Fill the uncovered part XXX way too much */ |
312 | 0 | ScreenFill( p_cdg, 0, 0, CDG_SCREEN_WIDTH, CDG_SCREEN_HEIGHT, color ); |
313 | | |
314 | | /* Copy back */ |
315 | 0 | for( unsigned y = 0; y < CDG_SCREEN_HEIGHT; y++ ) |
316 | 0 | { |
317 | 0 | int dy = i_shiftv + y; |
318 | 0 | for( unsigned x = 0; x < CDG_SCREEN_WIDTH; x++ ) |
319 | 0 | { |
320 | 0 | int dx = i_shifth + x; |
321 | |
|
322 | 0 | if( b_copy ) |
323 | 0 | { |
324 | 0 | dy = (dy + CDG_SCREEN_HEIGHT) % CDG_SCREEN_HEIGHT; |
325 | 0 | dx = (dx + CDG_SCREEN_WIDTH ) % CDG_SCREEN_WIDTH; |
326 | 0 | } |
327 | 0 | else |
328 | 0 | { |
329 | 0 | if( dy < 0 || (unsigned)dy >= CDG_SCREEN_HEIGHT || |
330 | 0 | dx < 0 || (unsigned)dx >= CDG_SCREEN_WIDTH ) |
331 | 0 | continue; |
332 | 0 | } |
333 | 0 | p_cdg->screen[dy*CDG_SCREEN_PITCH+dx] = copy[y*CDG_SCREEN_PITCH+x]; |
334 | 0 | } |
335 | 0 | } |
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 | 0 | return 0; |
339 | 0 | } |
340 | | |
341 | | static int DecodePacket( decoder_sys_t *p_cdg, uint8_t *p_buffer, int i_buffer ) |
342 | 0 | { |
343 | 0 | const int i_cmd = p_buffer[0] & 0x3f; |
344 | 0 | const int i_instruction = p_buffer[1] & 0x3f; |
345 | 0 | const uint8_t *p_data = &p_buffer[4]; |
346 | |
|
347 | 0 | if( i_buffer != CDG_PACKET_SIZE ) |
348 | 0 | return -1; |
349 | | |
350 | 0 | p_cdg->i_packet++; |
351 | | |
352 | | /* Handle CDG command only */ |
353 | 0 | if( i_cmd != 0x09 ) |
354 | 0 | return 0; |
355 | | |
356 | 0 | switch( i_instruction ) |
357 | 0 | { |
358 | 0 | case 1: |
359 | 0 | DecodeMemoryPreset( p_cdg, p_data ); |
360 | 0 | break; |
361 | 0 | case 2: |
362 | 0 | DecodeBorderPreset( p_cdg, p_data ); |
363 | 0 | break; |
364 | 0 | case 6: |
365 | 0 | DecodeTileBlock( p_cdg, p_data, 0 ); |
366 | 0 | break; |
367 | 0 | case 20: |
368 | 0 | DecodeScroll( p_cdg, p_data, 0 ); |
369 | 0 | break; |
370 | 0 | case 24: |
371 | 0 | DecodeScroll( p_cdg, p_data, 1 ); |
372 | 0 | break; |
373 | 0 | 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 | 0 | break; |
378 | 0 | case 30: |
379 | 0 | DecodeLoadColorTable( p_cdg, p_data, 0 ); |
380 | 0 | break; |
381 | 0 | case 31: |
382 | 0 | DecodeLoadColorTable( p_cdg, p_data, 8 ); |
383 | 0 | break; |
384 | 0 | case 38: |
385 | 0 | DecodeTileBlock( p_cdg, p_data, 1 ); |
386 | 0 | break; |
387 | 0 | default: |
388 | 0 | break; |
389 | 0 | } |
390 | 0 | return 0; |
391 | 0 | } |
392 | | |
393 | | static void RenderPixel( picture_t *p_picture, int x, int y, uint32_t c ) |
394 | 0 | { |
395 | 0 | const int i_plane = 0; |
396 | 0 | const int i_pitch = p_picture->p[i_plane].i_pitch; |
397 | 0 | uint32_t *s = (uint32_t*)&p_picture->p[i_plane].p_pixels[y*i_pitch + x*sizeof(uint32_t)]; |
398 | 0 | *s = c; |
399 | 0 | } |
400 | | static uint32_t RenderRGB( int r, int g, int b ) |
401 | 0 | { |
402 | 0 | return ( r << CDG_COLOR_R_SHIFT ) | ( g << CDG_COLOR_G_SHIFT ) | ( b << CDG_COLOR_B_SHIFT ); |
403 | 0 | } |
404 | | |
405 | | static int Render( decoder_sys_t *p_cdg, picture_t *p_picture ) |
406 | 0 | { |
407 | 0 | for( unsigned y = 0; y < CDG_DISPLAY_HEIGHT; y++ ) |
408 | 0 | { |
409 | 0 | for( unsigned x = 0; x < CDG_DISPLAY_WIDTH; x++ ) |
410 | 0 | { |
411 | 0 | const int sx = x + p_cdg->i_offseth + CDG_SCREEN_BORDER_WIDTH; |
412 | 0 | const int sy = y + p_cdg->i_offsetv + CDG_SCREEN_BORDER_HEIGHT; |
413 | 0 | uint8_t cidx = p_cdg->p_screen[sy*CDG_SCREEN_PITCH +sx]; |
414 | 0 | uint8_t r = p_cdg->color[cidx][0]; |
415 | 0 | uint8_t g = p_cdg->color[cidx][1]; |
416 | 0 | uint8_t b = p_cdg->color[cidx][2]; |
417 | |
|
418 | 0 | RenderPixel( p_picture, x, y, RenderRGB( r, g, b ) ); |
419 | 0 | } |
420 | 0 | } |
421 | 0 | return 0; |
422 | 0 | } |
423 | | |