00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <vlc/vlc.h>
00030 #include <vlc/vout.h>
00031 #include <vlc/decoder.h>
00032
00033 #include "spudec.h"
00034
00035
00036
00037
00038 static int ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *);
00039 static int ParseRLE ( decoder_t *, subpicture_t *, subpicture_data_t *);
00040 static void Render ( decoder_t *, subpicture_t *, subpicture_data_t *);
00041
00042
00043
00044
00045 static inline unsigned int AddNibble( unsigned int i_code,
00046 uint8_t *p_src, unsigned int *pi_index )
00047 {
00048 if( *pi_index & 0x1 )
00049 {
00050 return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
00051 }
00052 else
00053 {
00054 return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
00055 }
00056 }
00057
00058
00059
00060
00061
00062
00063
00064 subpicture_t * E_(ParsePacket)( decoder_t *p_dec )
00065 {
00066 decoder_sys_t *p_sys = p_dec->p_sys;
00067 subpicture_data_t *p_spu_data;
00068 subpicture_t *p_spu;
00069
00070
00071 p_spu = p_dec->pf_spu_buffer_new( p_dec );
00072 if( !p_spu ) return NULL;
00073
00074
00075
00076
00077
00078 p_spu_data = malloc( sizeof(subpicture_data_t) + 4 * p_sys->i_rle_size );
00079 p_spu_data->p_data = (uint8_t *)p_spu_data + sizeof(subpicture_data_t);
00080 p_spu_data->b_palette = VLC_FALSE;
00081 p_spu_data->b_auto_crop = VLC_FALSE;
00082 p_spu_data->i_y_top_offset = 0;
00083 p_spu_data->i_y_bottom_offset = 0;
00084
00085 p_spu_data->pi_alpha[0] = 0x00;
00086 p_spu_data->pi_alpha[1] = 0x0f;
00087 p_spu_data->pi_alpha[2] = 0x0f;
00088 p_spu_data->pi_alpha[3] = 0x0f;
00089
00090
00091 p_spu_data->i_pts = p_sys->i_pts;
00092
00093 p_spu->i_original_picture_width =
00094 p_dec->fmt_in.subs.spu.i_original_frame_width;
00095 p_spu->i_original_picture_height =
00096 p_dec->fmt_in.subs.spu.i_original_frame_height;
00097
00098
00099 if( ParseControlSeq( p_dec, p_spu, p_spu_data ) )
00100 {
00101
00102 p_dec->pf_spu_buffer_del( p_dec, p_spu );
00103 return NULL;
00104 }
00105
00106
00107 if( ParseRLE( p_dec, p_spu, p_spu_data ) )
00108 {
00109
00110 p_dec->pf_spu_buffer_del( p_dec, p_spu );
00111 return NULL;
00112 }
00113
00114 msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
00115 p_sys->i_spu_size,
00116 p_spu_data->pi_offset[0], p_spu_data->pi_offset[1] );
00117
00118 Render( p_dec, p_spu, p_spu_data );
00119 free( p_spu_data );
00120
00121 return p_spu;
00122 }
00123
00124
00125
00126
00127
00128
00129
00130
00131 static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
00132 subpicture_data_t *p_spu_data )
00133 {
00134 decoder_sys_t *p_sys = p_dec->p_sys;
00135
00136
00137 unsigned int i_index = p_sys->i_rle_size + 4;
00138
00139
00140 unsigned int i_next_seq = 0, i_cur_seq = 0;
00141
00142
00143 uint8_t i_command = SPU_CMD_END;
00144 mtime_t date = 0;
00145
00146 unsigned int i, pi_alpha[4];
00147
00148
00149 p_spu->i_start = p_spu->i_stop = 0;
00150 p_spu->b_ephemer = VLC_FALSE;
00151
00152 do
00153 {
00154 if( (int)i_index >= p_sys->i_spu_size + 1 )
00155 {
00156
00157
00158
00159
00160 break;
00161 }
00162
00163
00164
00165 if( i_command == SPU_CMD_END )
00166 {
00167
00168 date = (mtime_t)GetWBE( &p_sys->buffer[i_index] ) * 11000;
00169
00170
00171
00172
00173
00174 i_cur_seq = i_index;
00175 i_next_seq = GetWBE( &p_sys->buffer[i_index+2] );
00176
00177
00178 i_index += 4;
00179 }
00180
00181 i_command = p_sys->buffer[i_index++];
00182
00183 switch( i_command )
00184 {
00185 case SPU_CMD_FORCE_DISPLAY:
00186 p_spu->i_start = p_spu_data->i_pts + date;
00187 p_spu->b_ephemer = VLC_TRUE;
00188 break;
00189
00190
00191 case SPU_CMD_START_DISPLAY:
00192 p_spu->i_start = p_spu_data->i_pts + date;
00193 break;
00194
00195 case SPU_CMD_STOP_DISPLAY:
00196 p_spu->i_stop = p_spu_data->i_pts + date;
00197 break;
00198
00199 case SPU_CMD_SET_PALETTE:
00200
00201
00202 if( p_dec->fmt_in.subs.spu.palette[0] == 0xBeeF )
00203 {
00204 unsigned int idx[4];
00205
00206 p_spu_data->b_palette = VLC_TRUE;
00207
00208 idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f;
00209 idx[1] = (p_sys->buffer[i_index+0])&0x0f;
00210 idx[2] = (p_sys->buffer[i_index+1]>>4)&0x0f;
00211 idx[3] = (p_sys->buffer[i_index+1])&0x0f;
00212
00213 for( i = 0; i < 4 ; i++ )
00214 {
00215 uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
00216
00217
00218 p_spu_data->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
00219 p_spu_data->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
00220 p_spu_data->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
00221 }
00222 }
00223 i_index += 2;
00224
00225 break;
00226
00227 case SPU_CMD_SET_ALPHACHANNEL:
00228 pi_alpha[3] = (p_sys->buffer[i_index+0]>>4)&0x0f;
00229 pi_alpha[2] = (p_sys->buffer[i_index+0])&0x0f;
00230 pi_alpha[1] = (p_sys->buffer[i_index+1]>>4)&0x0f;
00231 pi_alpha[0] = (p_sys->buffer[i_index+1])&0x0f;
00232
00233
00234
00235 if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] )
00236 {
00237 p_spu_data->pi_alpha[0] = pi_alpha[0];
00238 p_spu_data->pi_alpha[1] = pi_alpha[1];
00239 p_spu_data->pi_alpha[2] = pi_alpha[2];
00240 p_spu_data->pi_alpha[3] = pi_alpha[3];
00241 }
00242 else
00243 {
00244 msg_Warn( p_dec, "ignoring blank alpha palette" );
00245 }
00246
00247 i_index += 2;
00248 break;
00249
00250 case SPU_CMD_SET_COORDINATES:
00251 p_spu->i_x = (p_sys->buffer[i_index+0]<<4)|
00252 ((p_sys->buffer[i_index+1]>>4)&0x0f);
00253 p_spu->i_width = (((p_sys->buffer[i_index+1]&0x0f)<<8)|
00254 p_sys->buffer[i_index+2]) - p_spu->i_x + 1;
00255
00256 p_spu->i_y = (p_sys->buffer[i_index+3]<<4)|
00257 ((p_sys->buffer[i_index+4]>>4)&0x0f);
00258 p_spu->i_height = (((p_sys->buffer[i_index+4]&0x0f)<<8)|
00259 p_sys->buffer[i_index+5]) - p_spu->i_y + 1;
00260
00261
00262 if( p_spu->i_height > 250 )
00263 p_spu_data->b_auto_crop = VLC_TRUE;
00264
00265 i_index += 6;
00266 break;
00267
00268 case SPU_CMD_SET_OFFSETS:
00269 p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4;
00270 p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4;
00271 i_index += 4;
00272 break;
00273
00274 case SPU_CMD_END:
00275 break;
00276
00277 default:
00278 msg_Warn( p_dec, "unknown command 0x%.2x", i_command );
00279 return VLC_EGENERIC;
00280 }
00281
00282
00283 if( p_dec->b_die )
00284 {
00285 return VLC_EGENERIC;
00286 }
00287
00288 } while( i_command != SPU_CMD_END || i_index == i_next_seq );
00289
00290
00291 if( i_next_seq != i_cur_seq )
00292 {
00293 msg_Err( p_dec, "index mismatch (0x%.4x != 0x%.4x)",
00294 i_next_seq, i_cur_seq );
00295 return VLC_EGENERIC;
00296 }
00297
00298 if( (int)i_index > p_sys->i_spu_size )
00299 {
00300 msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)",
00301 i_index, p_sys->i_spu_size );
00302 return VLC_EGENERIC;
00303 }
00304
00305 if( !p_spu->i_start )
00306 {
00307 msg_Err( p_dec, "no `start display' command" );
00308 }
00309
00310 if( p_spu->i_stop <= p_spu->i_start && !p_spu->b_ephemer )
00311 {
00312
00313 p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000;
00314 p_spu->b_ephemer = VLC_TRUE;
00315 }
00316
00317
00318 if( p_sys->i_spu_size > (int)i_index + 1 )
00319 {
00320
00321
00322
00323 msg_Warn( p_dec, "%i padding bytes, we usually get 0 or 1 of them",
00324 p_sys->i_spu_size - i_index );
00325 }
00326
00327
00328 return VLC_SUCCESS;
00329 }
00330
00331
00332
00333
00334
00335
00336
00337
00338 static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu,
00339 subpicture_data_t *p_spu_data )
00340 {
00341 decoder_sys_t *p_sys = p_dec->p_sys;
00342 uint8_t *p_src = &p_sys->buffer[4];
00343
00344 unsigned int i_code;
00345
00346 unsigned int i_width = p_spu->i_width;
00347 unsigned int i_height = p_spu->i_height;
00348 unsigned int i_x, i_y;
00349
00350 uint16_t *p_dest = (uint16_t *)p_spu_data->p_data;
00351
00352
00353 unsigned int i_id = 0;
00354 unsigned int pi_table[ 2 ];
00355 unsigned int *pi_offset;
00356
00357
00358 vlc_bool_t b_empty_top = VLC_TRUE;
00359 unsigned int i_skipped_top = 0, i_skipped_bottom = 0;
00360 unsigned int i_transparent_code = 0;
00361
00362
00363 int i_border = -1;
00364 int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
00365
00366 pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1;
00367 pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1;
00368
00369 for( i_y = 0 ; i_y < i_height ; i_y++ )
00370 {
00371 pi_offset = pi_table + i_id;
00372
00373 for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
00374 {
00375 i_code = AddNibble( 0, p_src, pi_offset );
00376
00377 if( i_code < 0x04 )
00378 {
00379 i_code = AddNibble( i_code, p_src, pi_offset );
00380
00381 if( i_code < 0x10 )
00382 {
00383 i_code = AddNibble( i_code, p_src, pi_offset );
00384
00385 if( i_code < 0x040 )
00386 {
00387 i_code = AddNibble( i_code, p_src, pi_offset );
00388
00389 if( i_code < 0x0100 )
00390 {
00391
00392
00393 if( i_code < 0x0004 )
00394 {
00395 i_code |= ( i_width - i_x ) << 2;
00396 }
00397 else
00398 {
00399
00400 msg_Err( p_dec, "unknown RLE code "
00401 "0x%.4x", i_code );
00402 return VLC_EGENERIC;
00403 }
00404 }
00405 }
00406 }
00407 }
00408
00409 if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
00410 {
00411 msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i",
00412 i_code >> 2, i_x, i_y, i_width, i_height );
00413 return VLC_EGENERIC;
00414 }
00415
00416
00417 if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 )
00418 {
00419 i_border = i_code & 0x3;
00420 stats[i_border] += i_code >> 2;
00421 }
00422
00423
00424 if( p_spu_data->b_auto_crop )
00425 {
00426 if( !i_y )
00427 {
00428
00429
00430
00431 if( (i_code >> 2) == i_width &&
00432 p_spu_data->pi_alpha[ i_code & 0x3 ] == 0x00 )
00433 {
00434 i_transparent_code = i_code;
00435 }
00436 else
00437 {
00438 p_spu_data->b_auto_crop = VLC_FALSE;
00439 }
00440 }
00441
00442 if( i_code == i_transparent_code )
00443 {
00444 if( b_empty_top )
00445 {
00446
00447 i_skipped_top++;
00448 }
00449 else
00450 {
00451
00452
00453 *p_dest++ = i_code;
00454 i_skipped_bottom++;
00455 }
00456 }
00457 else
00458 {
00459
00460 *p_dest++ = i_code;
00461
00462
00463 b_empty_top = VLC_FALSE;
00464 i_skipped_bottom = 0;
00465 }
00466 }
00467 else
00468 {
00469 *p_dest++ = i_code;
00470 }
00471 }
00472
00473
00474 if( i_x > i_width )
00475 {
00476 msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width );
00477 return VLC_EGENERIC;
00478 }
00479
00480
00481 if( *pi_offset & 0x1 )
00482 {
00483 (*pi_offset)++;
00484 }
00485
00486
00487 i_id = ~i_id & 0x1;
00488 }
00489
00490
00491 if( i_y < i_height )
00492 {
00493 msg_Err( p_dec, "padding bytes found in RLE sequence" );
00494 msg_Err( p_dec, "send mail to <[email protected]> if you "
00495 "want to help debugging this" );
00496
00497
00498 while( i_y < i_height )
00499 {
00500 *p_dest++ = i_width << 2;
00501 i_y++;
00502 }
00503
00504 return VLC_EGENERIC;
00505 }
00506
00507 msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
00508 p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
00509
00510
00511 if( i_skipped_top || i_skipped_bottom )
00512 {
00513 int i_y = p_spu->i_y + i_skipped_top;
00514 int i_height = p_spu->i_height - (i_skipped_top + i_skipped_bottom);
00515
00516 p_spu_data->i_y_top_offset = i_skipped_top;
00517 p_spu_data->i_y_bottom_offset = i_skipped_bottom;
00518 msg_Dbg( p_dec, "cropped to: %ix%i, position: %i,%i",
00519 p_spu->i_width, i_height, p_spu->i_x, i_y );
00520 }
00521
00522
00523 if( !p_spu_data->b_palette )
00524 {
00525 int i, i_inner = -1, i_shade = -1;
00526
00527
00528 p_spu_data->pi_yuv[i_border][0] = 0x00;
00529 p_spu_data->pi_yuv[i_border][1] = 0x80;
00530 p_spu_data->pi_yuv[i_border][2] = 0x80;
00531 stats[i_border] = 0;
00532
00533
00534 for( i = 0 ; i < 4 && i_inner == -1 ; i++ )
00535 {
00536 if( stats[i] )
00537 {
00538 i_inner = i;
00539 }
00540 }
00541
00542 for( ; i < 4 && i_shade == -1 ; i++ )
00543 {
00544 if( stats[i] )
00545 {
00546 if( stats[i] > stats[i_inner] )
00547 {
00548 i_shade = i_inner;
00549 i_inner = i;
00550 }
00551 else
00552 {
00553 i_shade = i;
00554 }
00555 }
00556 }
00557
00558
00559 if( i_inner != -1 )
00560 {
00561 p_spu_data->pi_yuv[i_inner][0] = 0xff;
00562 p_spu_data->pi_yuv[i_inner][1] = 0x80;
00563 p_spu_data->pi_yuv[i_inner][2] = 0x80;
00564 }
00565
00566
00567 if( i_shade != -1 )
00568 {
00569 p_spu_data->pi_yuv[i_shade][0] = 0x80;
00570 p_spu_data->pi_yuv[i_shade][1] = 0x80;
00571 p_spu_data->pi_yuv[i_shade][2] = 0x80;
00572 }
00573
00574 msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)",
00575 i_border, i_inner, i_shade );
00576 }
00577
00578 return VLC_SUCCESS;
00579 }
00580
00581 static void Render( decoder_t *p_dec, subpicture_t *p_spu,
00582 subpicture_data_t *p_spu_data )
00583 {
00584 uint8_t *p_p;
00585 int i_x, i_y, i_len, i_color, i_pitch;
00586 uint16_t *p_source = (uint16_t *)p_spu_data->p_data;
00587 video_format_t fmt;
00588
00589
00590 memset( &fmt, 0, sizeof(video_format_t) );
00591 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
00592 fmt.i_aspect = 0;
00593 fmt.i_width = fmt.i_visible_width = p_spu->i_width;
00594 fmt.i_height = fmt.i_visible_height = p_spu->i_height -
00595 p_spu_data->i_y_top_offset - p_spu_data->i_y_bottom_offset;
00596 fmt.i_x_offset = fmt.i_y_offset = 0;
00597 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
00598 if( !p_spu->p_region )
00599 {
00600 msg_Err( p_dec, "cannot allocate SPU region" );
00601 return;
00602 }
00603
00604 p_spu->p_region->i_x = 0;
00605 p_spu->p_region->i_y = p_spu_data->i_y_top_offset;
00606 p_p = p_spu->p_region->picture.p->p_pixels;
00607 i_pitch = p_spu->p_region->picture.p->i_pitch;
00608
00609
00610 fmt.p_palette->i_entries = 4;
00611 for( i_x = 0; i_x < fmt.p_palette->i_entries; i_x++ )
00612 {
00613 fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
00614 fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
00615 fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
00616 fmt.p_palette->palette[i_x][3] =
00617 p_spu_data->pi_alpha[i_x] == 0xf ? 0xff :
00618 p_spu_data->pi_alpha[i_x] << 4;
00619 }
00620
00621
00622 for( i_y = 0; i_y < (int)fmt.i_height * i_pitch; i_y += i_pitch )
00623 {
00624
00625 for( i_x = 0 ; i_x < (int)fmt.i_width; i_x += i_len )
00626 {
00627
00628 i_color = *p_source & 0x3;
00629 i_len = *p_source++ >> 2;
00630 memset( p_p + i_x + i_y, i_color, i_len );
00631 }
00632 }
00633 }