parse.c 22.4 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * parse.c: SPU parser
 *****************************************************************************
 * Copyright (C) 2000-2001 VideoLAN
5
 * $Id: parse.c,v 1.17 2004/01/27 22:51:39 hartman Exp $
6 7
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
Laurent Aimar's avatar
Laurent Aimar committed
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Laurent Aimar's avatar
Laurent Aimar committed
14
 *
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>

#include "spudec.h"

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
37 38
static int  ParseControlSeq  ( decoder_t *, subpicture_t * );
static int  ParseRLE         ( decoder_t *, subpicture_t * );
39 40 41 42 43 44

static void DestroySPU       ( subpicture_t * );

static void UpdateSPU        ( subpicture_t *, vlc_object_t * );
static int  CropCallback     ( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
45 46 47 48 49

/*****************************************************************************
 * AddNibble: read a nibble from a source packet and add it to our integer.
 *****************************************************************************/
static inline unsigned int AddNibble( unsigned int i_code,
50
                                      uint8_t *p_src, int *pi_index )
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
{
    if( *pi_index & 0x1 )
    {
        return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
    }
    else
    {
        return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
    }
}

/*****************************************************************************
 * ParsePacket: parse an SPU packet and send it to the video output
 *****************************************************************************
 * This function parses the SPU packet and, if valid, sends it to the
 * video output.
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
68
void E_(ParsePacket)( decoder_t *p_dec)
69
{
Laurent Aimar's avatar
Laurent Aimar committed
70
    decoder_sys_t *p_sys = p_dec->p_sys;
71

Laurent Aimar's avatar
Laurent Aimar committed
72
    subpicture_t  *p_spu;
73 74

    /* Allocate the subpicture internal data. */
Laurent Aimar's avatar
Laurent Aimar committed
75
    p_spu = vout_CreateSubPicture( p_sys->p_vout, MEMORY_SUBPICTURE );
76 77 78 79 80
    if( p_spu == NULL )
    {
        return;
    }

81 82 83 84
    /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
     * expand the RLE stuff so that we won't need to read nibbles later
     * on. This will speed things up a lot. Plus, we'll only need to do
     * this stupid interlacing stuff once. */
Laurent Aimar's avatar
Laurent Aimar committed
85
    p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) + 4*p_sys->i_rle_size );
86 87

    /* Fill the p_spu structure */
Laurent Aimar's avatar
Laurent Aimar committed
88
    vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
89

90
    p_spu->pf_render = E_(RenderSPU);
91 92
    p_spu->pf_destroy = DestroySPU;
    p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
93 94 95 96 97 98
    p_spu->p_sys->b_palette = VLC_FALSE;

    p_spu->p_sys->pi_alpha[0] = 0x00;
    p_spu->p_sys->pi_alpha[1] = 0x0f;
    p_spu->p_sys->pi_alpha[2] = 0x0f;
    p_spu->p_sys->pi_alpha[3] = 0x0f;
99

100 101
    p_spu->p_sys->b_crop = VLC_FALSE;

102
    /* Get display time now. If we do it later, we may miss the PTS. */
Laurent Aimar's avatar
Laurent Aimar committed
103
    p_spu->p_sys->i_pts = p_sys->i_pts;
104

105
    /* Attach to our input thread */
Laurent Aimar's avatar
Laurent Aimar committed
106
    p_spu->p_sys->p_input = vlc_object_find( p_dec,
107 108 109 110 111 112 113 114 115 116 117
                                             VLC_OBJECT_INPUT, FIND_PARENT );
    if( p_spu->p_sys->p_input )
    {
        vlc_value_t val;

        if( !var_Get( p_spu->p_sys->p_input, "highlight-mutex", &val ) )
        {
            vlc_mutex_t *p_mutex = val.p_address;

            vlc_mutex_lock( p_mutex );
            UpdateSPU( p_spu, VLC_OBJECT(p_spu->p_sys->p_input) );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
118

119 120 121 122 123 124
            var_AddCallback( p_spu->p_sys->p_input,
                             "highlight", CropCallback, p_spu );
            vlc_mutex_unlock( p_mutex );
        }
    }

125
    /* Getting the control part */
Laurent Aimar's avatar
Laurent Aimar committed
126
    if( ParseControlSeq( p_dec, p_spu ) )
127 128
    {
        /* There was a parse error, delete the subpicture */
Laurent Aimar's avatar
Laurent Aimar committed
129
        vout_DestroySubPicture( p_sys->p_vout, p_spu );
130 131 132
        return;
    }

Laurent Aimar's avatar
Laurent Aimar committed
133 134
     /* We try to display it */
    if( ParseRLE( p_dec, p_spu ) )
135 136
    {
        /* There was a parse error, delete the subpicture */
Laurent Aimar's avatar
Laurent Aimar committed
137
        vout_DestroySubPicture( p_sys->p_vout, p_spu );
138 139 140
        return;
    }

Laurent Aimar's avatar
Laurent Aimar committed
141 142
    msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
             p_sys->i_spu_size,
143 144 145
             p_spu->p_sys->pi_offset[0], p_spu->p_sys->pi_offset[1] );

    /* SPU is finished - we can ask the video output to display it */
Laurent Aimar's avatar
Laurent Aimar committed
146
    vout_DisplaySubPicture( p_sys->p_vout, p_spu );
147

148
    /* TODO: do stuff! */
149 150 151
}

/*****************************************************************************
152
 * ParseControlSeq: parse all SPU control sequences
153 154 155 156 157
 *****************************************************************************
 * This is the most important part in SPU decoding. We get dates, palette
 * information, coordinates, and so on. For more information on the
 * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
158
static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
159
{
Laurent Aimar's avatar
Laurent Aimar committed
160 161
    decoder_sys_t *p_sys = p_dec->p_sys;

162
    /* Our current index in the SPU packet */
Laurent Aimar's avatar
Laurent Aimar committed
163
    unsigned int i_index = p_sys->i_rle_size + 4;
164 165

    /* The next start-of-control-sequence index and the previous one */
166
    unsigned int i_next_seq = 0, i_cur_seq = 0;
167

168
    /* Command and date */
169
    uint8_t i_command = SPU_CMD_END;
170
    mtime_t date = 0;
171

172
    unsigned int i, pi_alpha[4];
173 174 175

    /* Initialize the structure */
    p_spu->i_start = p_spu->i_stop = 0;
176
    p_spu->b_ephemer = VLC_FALSE;
177 178 179

    do
    {
Laurent Aimar's avatar
Laurent Aimar committed
180 181 182 183 184 185 186 187 188
        if( (int)i_index >= p_sys->i_spu_size + 1 )
        {
            /* sanity
             * XXX only on test by loop as p_sys->buffer is bigger than needed
             * to avoid checking at each access
             */
            break;
        }

189 190 191
        /* If we just read a command sequence, read the next one;
         * otherwise, go on with the commands of the current sequence. */
        if( i_command == SPU_CMD_END )
192
        {
193
            /* Get the control sequence date */
Laurent Aimar's avatar
Laurent Aimar committed
194 195
            date = (mtime_t)GetWBE( &p_sys->buffer[i_index] ) * 11000;
            /* FIXME How to access i_rate
196
                    * p_spudec->bit_stream.p_pes->i_rate / DEFAULT_RATE;
Laurent Aimar's avatar
Laurent Aimar committed
197 198
            */

199 200
            /* Next offset */
            i_cur_seq = i_index;
Laurent Aimar's avatar
Laurent Aimar committed
201 202
            i_next_seq = GetWBE( &p_sys->buffer[i_index+2] );

203 204 205
            /* Skip what we just read */
            i_index += 4;
        }
Laurent Aimar's avatar
Laurent Aimar committed
206 207 208

        i_command = p_sys->buffer[i_index++];

209 210 211
        switch( i_command )
        {
        case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
212
            p_spu->i_start = p_spu->p_sys->i_pts + date;
213 214
            p_spu->b_ephemer = VLC_TRUE;
            break;
215

216 217
        /* Convert the dates in seconds to PTS values */
        case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
218
            p_spu->i_start = p_spu->p_sys->i_pts + date;
219 220 221
            break;

        case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
222
            p_spu->i_stop = p_spu->p_sys->i_pts + date;
223
            break;
224

225 226 227
        case SPU_CMD_SET_PALETTE:

            /* 03xxxx (palette) */
228
            if( p_dec->fmt_in.subs.spu.palette[0] == 0xBeeF )
229
            {
Laurent Aimar's avatar
Laurent Aimar committed
230
                unsigned int idx[4];
231 232

                p_spu->p_sys->b_palette = VLC_TRUE;
Laurent Aimar's avatar
Laurent Aimar committed
233 234 235 236 237 238

                idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f;
                idx[1] = (p_sys->buffer[i_index+0])&0x0f;
                idx[2] = (p_sys->buffer[i_index+1]>>4)&0x0f;
                idx[3] = (p_sys->buffer[i_index+1])&0x0f;

239 240
                for( i = 0; i < 4 ; i++ )
                {
241
                    uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
242 243 244 245 246 247 248 249 250 251

                    /* FIXME: this job should be done sooner */
                    p_spu->p_sys->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
                    p_spu->p_sys->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
                    p_spu->p_sys->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
                }
            }
            i_index += 2;

            break;
252

253
        case SPU_CMD_SET_ALPHACHANNEL: /* 04xxxx (alpha channel) */
Laurent Aimar's avatar
Laurent Aimar committed
254 255 256 257
            pi_alpha[3] = (p_sys->buffer[i_index+0]>>4)&0x0f;
            pi_alpha[2] = (p_sys->buffer[i_index+0])&0x0f;
            pi_alpha[1] = (p_sys->buffer[i_index+1]>>4)&0x0f;
            pi_alpha[0] = (p_sys->buffer[i_index+1])&0x0f;
258 259 260 261

            /* Ignore blank alpha palette. Sometimes spurious blank
             * alpha palettes are present - dunno why. */
            if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] )
262
            {
263 264 265 266 267 268 269
                p_spu->p_sys->pi_alpha[0] = pi_alpha[0];
                p_spu->p_sys->pi_alpha[1] = pi_alpha[1];
                p_spu->p_sys->pi_alpha[2] = pi_alpha[2];
                p_spu->p_sys->pi_alpha[3] = pi_alpha[3];
            }
            else
            {
Laurent Aimar's avatar
Laurent Aimar committed
270
                msg_Warn( p_dec, "ignoring blank alpha palette" );
271 272
            }

273 274
            i_index += 2;
            break;
275

276
        case SPU_CMD_SET_COORDINATES: /* 05xxxyyyxxxyyy (coordinates) */
Laurent Aimar's avatar
Laurent Aimar committed
277 278 279 280
            p_spu->i_x = (p_sys->buffer[i_index+0]<<4)|
                         ((p_sys->buffer[i_index+1]>>4)&0x0f);
            p_spu->i_width = (((p_sys->buffer[i_index+1]&0x0f)<<8)|
                              p_sys->buffer[i_index+2]) - p_spu->i_x + 1;
281

Laurent Aimar's avatar
Laurent Aimar committed
282 283 284 285
            p_spu->i_y = (p_sys->buffer[i_index+3]<<4)|
                         ((p_sys->buffer[i_index+4]>>4)&0x0f);
            p_spu->i_height = (((p_sys->buffer[i_index+4]&0x0f)<<8)|
                              p_sys->buffer[i_index+5]) - p_spu->i_y + 1;
286
            
287 288 289 290
            i_index += 6;
            break;

        case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */
Laurent Aimar's avatar
Laurent Aimar committed
291 292
            p_spu->p_sys->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4;
            p_spu->p_sys->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4;
293 294 295 296 297 298 299
            i_index += 4;
            break;

        case SPU_CMD_END: /* ff (end) */
            break;

        default: /* xx (unknown command) */
300
            msg_Warn( p_dec, "unknown command 0x%.2x", i_command );
301 302 303 304
            return VLC_EGENERIC;
        }

        /* We need to check for quit commands here */
Laurent Aimar's avatar
Laurent Aimar committed
305
        if( p_dec->b_die )
306 307 308 309 310
        {
            return VLC_EGENERIC;
        }

    } while( i_command != SPU_CMD_END || i_index == i_next_seq );
311 312 313 314

    /* Check that the next sequence index matches the current one */
    if( i_next_seq != i_cur_seq )
    {
Laurent Aimar's avatar
Laurent Aimar committed
315 316
        msg_Err( p_dec, "index mismatch (0x%.4x != 0x%.4x)",
                 i_next_seq, i_cur_seq );
317
        return VLC_EGENERIC;
318 319
    }

Laurent Aimar's avatar
Laurent Aimar committed
320
    if( (int)i_index > p_sys->i_spu_size )
321
    {
Laurent Aimar's avatar
Laurent Aimar committed
322 323
        msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)",
                 i_index, p_sys->i_spu_size );
324
        return VLC_EGENERIC;
325 326 327 328
    }

    if( !p_spu->i_start )
    {
Laurent Aimar's avatar
Laurent Aimar committed
329
        msg_Err( p_dec, "no `start display' command" );
330 331
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
332
    if( p_spu->i_stop <= p_spu->i_start && !p_spu->b_ephemer )
333 334
    {
        /* This subtitle will live for 5 seconds or until the next subtitle */
Laurent Aimar's avatar
Laurent Aimar committed
335 336
        p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000;
        /* FIXME how to access i_rate ?
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
337
                        * p_spudec->bit_stream.p_pes->i_rate / DEFAULT_RATE;
Laurent Aimar's avatar
Laurent Aimar committed
338
        */
339
        p_spu->b_ephemer = VLC_TRUE;
340 341 342
    }

    /* Get rid of padding bytes */
Laurent Aimar's avatar
Laurent Aimar committed
343
    if( p_sys->i_spu_size > (int)i_index + 1 )
344
    {
Laurent Aimar's avatar
Laurent Aimar committed
345 346
        /* Zero or one padding byte, are quite usual
         * More than one padding byte - this is very strange, but
347
         * we can deal with it */
Laurent Aimar's avatar
Laurent Aimar committed
348 349
        msg_Warn( p_dec, "%i padding bytes, we usually get 0 or 1 of them",
                  p_sys->i_spu_size - i_index );
350 351 352
    }

    /* Successfully parsed ! */
353
    return VLC_SUCCESS;
354 355 356 357 358 359 360 361 362
}

/*****************************************************************************
 * ParseRLE: parse the RLE part of the subtitle
 *****************************************************************************
 * This part parses the subtitle graphical data and stores it in a more
 * convenient structure for later decoding. For more information on the
 * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
363
static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
364
{
Laurent Aimar's avatar
Laurent Aimar committed
365 366 367
    decoder_sys_t *p_sys = p_dec->p_sys;
    uint8_t       *p_src = &p_sys->buffer[4];

368 369 370 371 372 373
    unsigned int i_code;

    unsigned int i_width = p_spu->i_width;
    unsigned int i_height = p_spu->i_height;
    unsigned int i_x, i_y;

374
    uint16_t *p_dest = (uint16_t *)p_spu->p_sys->p_data;
375 376 377 378 379 380

    /* The subtitles are interlaced, we need two offsets */
    unsigned int  i_id = 0;                   /* Start on the even SPU layer */
    unsigned int  pi_table[ 2 ];
    unsigned int *pi_offset;

381
#if 0 /* cropping */
382 383
    vlc_bool_t b_empty_top = VLC_TRUE,
               b_empty_bottom = VLC_FALSE;
384 385
    unsigned int i_skipped_top = 0,
                 i_skipped_bottom = 0;
386
#endif
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

    /* Colormap statistics */
    int i_border = -1;
    int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;

    pi_table[ 0 ] = p_spu->p_sys->pi_offset[ 0 ] << 1;
    pi_table[ 1 ] = p_spu->p_sys->pi_offset[ 1 ] << 1;

    for( i_y = 0 ; i_y < i_height ; i_y++ )
    {
        pi_offset = pi_table + i_id;

        for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
        {
            i_code = AddNibble( 0, p_src, pi_offset );

            if( i_code < 0x04 )
            {
                i_code = AddNibble( i_code, p_src, pi_offset );

                if( i_code < 0x10 )
                {
                    i_code = AddNibble( i_code, p_src, pi_offset );

                    if( i_code < 0x040 )
                    {
                        i_code = AddNibble( i_code, p_src, pi_offset );

                        if( i_code < 0x0100 )
                        {
                            /* If the 14 first bits are set to 0, then it's a
                             * new line. We emulate it. */
                            if( i_code < 0x0004 )
                            {
                                i_code |= ( i_width - i_x ) << 2;
                            }
                            else
                            {
                                /* We have a boo boo ! */
Laurent Aimar's avatar
Laurent Aimar committed
426
                                msg_Err( p_dec, "unknown RLE code "
427
                                         "0x%.4x", i_code );
428
                                return VLC_EGENERIC;
429 430 431 432 433 434 435 436
                            }
                        }
                    }
                }
            }

            if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
            {
Laurent Aimar's avatar
Laurent Aimar committed
437
                msg_Err( p_dec,
438 439
                         "out of bounds, %i at (%i,%i) is out of %ix%i",
                         i_code >> 2, i_x, i_y, i_width, i_height );
440
                return VLC_EGENERIC;
441 442 443 444 445 446 447 448 449
            }

            /* Try to find the border color */
            if( p_spu->p_sys->pi_alpha[ i_code & 0x3 ] != 0x00 )
            {
                i_border = i_code & 0x3;
                stats[i_border] += i_code >> 2;
            }

450
#if 0 /* cropping */
451 452 453 454 455 456 457 458 459 460 461 462 463 464
            if( (i_code >> 2) == i_width
                 && p_spu->p_sys->pi_alpha[ i_code & 0x3 ] == 0x00 )
            {
                if( b_empty_top )
                {
                    /* This is a blank top line, we skip it */
                    i_skipped_top++;
                }
                else
                {
                    /* We can't be sure the current lines will be skipped,
                     * so we store the code just in case. */
                    *p_dest++ = i_code;

465
                    b_empty_bottom = VLC_TRUE;
466 467 468 469 470 471 472 473 474
                    i_skipped_bottom++;
                }
            }
            else
            {
                /* We got a valid code, store it */
                *p_dest++ = i_code;

                /* Valid code means no blank line */
475 476
                b_empty_top = VLC_FALSE;
                b_empty_bottom = VLC_FALSE;
477 478
                i_skipped_bottom = 0;
            }
479 480 481
#else
            *p_dest++ = i_code;
#endif
482 483 484 485 486
        }

        /* Check that we didn't go too far */
        if( i_x > i_width )
        {
Laurent Aimar's avatar
Laurent Aimar committed
487 488
            msg_Err( p_dec, "i_x overflowed, %i > %i",
                     i_x, i_width );
489
            return VLC_EGENERIC;
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
        }

        /* Byte-align the stream */
        if( *pi_offset & 0x1 )
        {
            (*pi_offset)++;
        }

        /* Swap fields */
        i_id = ~i_id & 0x1;
    }

    /* We shouldn't get any padding bytes */
    if( i_y < i_height )
    {
Laurent Aimar's avatar
Laurent Aimar committed
505 506 507
        msg_Err( p_dec, "padding bytes found in RLE sequence" );
        msg_Err( p_dec, "send mail to <sam@zoy.org> if you "
                        "want to help debugging this" );
508 509 510 511 512 513 514 515

        /* Skip them just in case */
        while( i_y < i_height )
        {
            *p_dest++ = i_width << 2;
            i_y++;
        }

516
        return VLC_EGENERIC;
517 518
    }

Laurent Aimar's avatar
Laurent Aimar committed
519
    msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
520 521
             p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );

522
#if 0 /* cropping */
523 524 525 526 527 528 529 530 531
    /* Crop if necessary */
    if( i_skipped_top || i_skipped_bottom )
    {
        p_spu->i_y += i_skipped_top;
        p_spu->i_height -= i_skipped_top + i_skipped_bottom;

        msg_Dbg( p_spudec->p_fifo, "cropped to: %ix%i, position: %i,%i",
                 p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
    }
532
#endif
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585

    /* Handle color if no palette was found */
    if( !p_spu->p_sys->b_palette )
    {
        int i, i_inner = -1, i_shade = -1;

        /* Set the border color */
        p_spu->p_sys->pi_yuv[i_border][0] = 0x00;
        p_spu->p_sys->pi_yuv[i_border][1] = 0x80;
        p_spu->p_sys->pi_yuv[i_border][2] = 0x80;
        stats[i_border] = 0;

        /* Find the inner colors */
        for( i = 0 ; i < 4 && i_inner == -1 ; i++ )
        {
            if( stats[i] )
            {
                i_inner = i;
            }
        }

        for(       ; i < 4 && i_shade == -1 ; i++ )
        {
            if( stats[i] )
            {
                if( stats[i] > stats[i_inner] )
                {
                    i_shade = i_inner;
                    i_inner = i;
                }
                else
                {
                    i_shade = i;
                }
            }
        }

        /* Set the inner color */
        if( i_inner != -1 )
        {
            p_spu->p_sys->pi_yuv[i_inner][0] = 0xff;
            p_spu->p_sys->pi_yuv[i_inner][1] = 0x80;
            p_spu->p_sys->pi_yuv[i_inner][2] = 0x80;
        }

        /* Set the anti-aliasing color */
        if( i_shade != -1 )
        {
            p_spu->p_sys->pi_yuv[i_shade][0] = 0x80;
            p_spu->p_sys->pi_yuv[i_shade][1] = 0x80;
            p_spu->p_sys->pi_yuv[i_shade][2] = 0x80;
        }

Laurent Aimar's avatar
Laurent Aimar committed
586
        msg_Dbg( p_dec,
587 588 589 590
                 "using custom palette (border %i, inner %i, shade %i)",
                 i_border, i_inner, i_shade );
    }

591
    return VLC_SUCCESS;
592 593
}

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
/*****************************************************************************
 * DestroySPU: subpicture destructor
 *****************************************************************************/
static void DestroySPU( subpicture_t *p_spu )
{
    if( p_spu->p_sys->p_input )
    {
        /* Detach from our input thread */
        var_DelCallback( p_spu->p_sys->p_input, "highlight",
                         CropCallback, p_spu );
        vlc_object_release( p_spu->p_sys->p_input );
    }

    vlc_mutex_destroy( &p_spu->p_sys->lock );
    free( p_spu->p_sys );
}

/*****************************************************************************
 * UpdateSPU: update subpicture settings
 *****************************************************************************
 * This function is called from CropCallback and at initialization time, to
 * retrieve crop information from the input.
 *****************************************************************************/
static void UpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
{
    vlc_value_t val;

    if( var_Get( p_object, "highlight", &val ) )
    {
        return;
    }

    p_spu->p_sys->b_crop = val.b_bool;
    if( !p_spu->p_sys->b_crop )
    {
        return;
    }

    var_Get( p_object, "x-start", &val );
    p_spu->p_sys->i_x_start = val.i_int;
    var_Get( p_object, "y-start", &val );
    p_spu->p_sys->i_y_start = val.i_int;
    var_Get( p_object, "x-end", &val );
    p_spu->p_sys->i_x_end = val.i_int;
    var_Get( p_object, "y-end", &val );
    p_spu->p_sys->i_y_end = val.i_int;

#if 0
    if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
    {
        p_spu->p_sys->pi_color[0] = ((uint8_t *)val.p_address)[0];
        p_spu->p_sys->pi_color[1] = ((uint8_t *)val.p_address)[1];
        p_spu->p_sys->pi_color[2] = ((uint8_t *)val.p_address)[2];
        p_spu->p_sys->pi_color[3] = ((uint8_t *)val.p_address)[3];
    }
#endif

    if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
    {
        p_spu->p_sys->pi_alpha[0] = ((uint8_t *)val.p_address)[0];
        p_spu->p_sys->pi_alpha[1] = ((uint8_t *)val.p_address)[1];
        p_spu->p_sys->pi_alpha[2] = ((uint8_t *)val.p_address)[2];
        p_spu->p_sys->pi_alpha[3] = ((uint8_t *)val.p_address)[3];
    }
}

/*****************************************************************************
 * CropCallback: called when the highlight properties are changed
 *****************************************************************************
 * This callback is called from the input thread when we need cropping
 *****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    UpdateSPU( (subpicture_t *)p_data, p_object );

    return VLC_SUCCESS;
}