vout_subpictures.c 36.3 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 2000-2004 VideoLAN
5
 * $Id$
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8
 *
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
9
 *          Gildas Bazin <gbazin@videolan.org>
Sam Hocevar's avatar
 
Sam Hocevar committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * 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.
 *
 * 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 <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */

33
#include <vlc/vlc.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
34

35
#include "vlc_block.h"
36
#include "vlc_video.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
37
#include "video_output.h"
38
#include "vlc_spu.h"
39 40 41
#include "vlc_filter.h"
#include "osd.h"

42 43 44 45 46 47
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void UpdateSPU   ( spu_t *, vlc_object_t * );
static int  CropCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
48

49 50
static subpicture_t *sub_new_buffer( filter_t * );
static void sub_del_buffer( filter_t *, subpicture_t * );
51 52
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
53 54
static picture_t *spu_new_video_buffer( filter_t * );
static void spu_del_video_buffer( filter_t *, picture_t * );
55 56

/**
57
 * Creates the subpicture unit
58
 *
59
 * \param p_this the parent object which creates the subpicture unit
60
 */
61
spu_t *__spu_Create( vlc_object_t *p_this )
62 63
{
    int i_index;
64
    spu_t *p_spu = vlc_object_create( p_this, VLC_OBJECT_SPU );
65 66 67

    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
68 69
        p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
        p_spu->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
70 71
    }

72 73 74
    p_spu->p_blend = NULL;
    p_spu->p_text = NULL;
    p_spu->p_scale = NULL;
75
    p_spu->i_filter = 0;
76 77 78 79 80

    /* Register the default subpicture channel */
    p_spu->i_channel = 1;

    vlc_mutex_init( p_this, &p_spu->subpicture_lock );
81

82 83
    vlc_object_attach( p_spu, p_this );

84 85 86 87 88 89 90 91 92 93 94 95 96
    return p_spu;
}

/**
 * Initialise the subpicture unit
 *
 * \param p_spu the subpicture unit object
 */
int spu_Init( spu_t *p_spu )
{
    char *psz_filter, *psz_filter_orig;
    vlc_value_t val;

97
    /* If the user requested an SPU margin, we force the position. */
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    var_Create( p_spu, "spumargin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_spu, "spumargin", &val );
    p_spu->i_margin = val.i_int;

    var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_spu, "sub-filter", &val );
    psz_filter = psz_filter_orig = val.psz_string;
    if( psz_filter && *psz_filter )
    {
        p_spu->pp_filter[p_spu->i_filter] =
            vlc_object_create( p_spu, VLC_OBJECT_FILTER );
        vlc_object_attach( p_spu->pp_filter[p_spu->i_filter], p_spu );
        p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_new = sub_new_buffer;
        p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_del = sub_del_buffer;
        p_spu->pp_filter[p_spu->i_filter]->p_owner =
            (filter_owner_sys_t *)p_spu;
        p_spu->pp_filter[p_spu->i_filter]->p_module =
            module_Need( p_spu->pp_filter[p_spu->i_filter],
                         "subpicture filter", psz_filter, 0 );
        if( p_spu->pp_filter[p_spu->i_filter]->p_module ) p_spu->i_filter++;
        else
        {
            msg_Dbg( p_spu, "no subpicture filter found" );
            vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
            vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
        }
    }
    if( psz_filter_orig ) free( psz_filter_orig );
126

127
    return VLC_EGENERIC;
128 129 130
}

/**
131
 * Destroy the subpicture unit
132
 *
133
 * \param p_this the parent object which destroys the subpicture unit
134
 */
135
void spu_Destroy( spu_t *p_spu )
136 137 138
{
    int i_index;

139 140
    vlc_object_detach( p_spu );

141 142 143
    /* Destroy all remaining subpictures */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
144
        if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
145
        {
146
            spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
147 148 149
        }
    }

150
    if( p_spu->p_blend )
151
    {
152 153
        if( p_spu->p_blend->p_module )
            module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
154

155 156
        vlc_object_detach( p_spu->p_blend );
        vlc_object_destroy( p_spu->p_blend );
157 158
    }

159
    if( p_spu->p_text )
160
    {
161 162
        if( p_spu->p_text->p_module )
            module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
163

164 165
        vlc_object_detach( p_spu->p_text );
        vlc_object_destroy( p_spu->p_text );
166 167
    }

168 169 170 171 172 173 174 175 176
    if( p_spu->p_scale )
    {
        if( p_spu->p_scale->p_module )
            module_Unneed( p_spu->p_scale, p_spu->p_scale->p_module );

        vlc_object_detach( p_spu->p_scale );
        vlc_object_destroy( p_spu->p_scale );
    }

177 178 179 180 181 182 183 184
    while( p_spu->i_filter-- )
    {
        module_Unneed( p_spu->pp_filter[p_spu->i_filter],
                       p_spu->pp_filter[p_spu->i_filter]->p_module );
        vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
        vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
    }

185 186
    vlc_mutex_destroy( &p_spu->subpicture_lock );
    vlc_object_destroy( p_spu );
187 188 189 190 191
}

/**
 * Attach/Detach the SPU from any input
 *
192
 * \param p_this the object in which to destroy the subpicture unit
193 194
 * \param b_attach to select attach or detach
 */
195
void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, vlc_bool_t b_attach )
196 197 198
{
    vlc_object_t *p_input;

199
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
200 201 202
    if( !p_input ) return;

    if( b_attach )
203
    {
204 205
        UpdateSPU( p_spu, VLC_OBJECT(p_input) );
        var_AddCallback( p_input, "highlight", CropCallback, p_spu );
206 207 208 209 210
        vlc_object_release( p_input );
    }
    else
    {
        /* Delete callback */
211
        var_DelCallback( p_input, "highlight", CropCallback, p_spu );
212 213 214 215 216 217 218 219 220 221
        vlc_object_release( p_input );
    }
}

/**
 * Create a subpicture region
 *
 * \param p_this vlc_object_t
 * \param p_fmt the format that this subpicture region should have
 */
222 223 224 225
static void RegionPictureRelease( picture_t *p_pic )
{
    if( p_pic->p_data_orig ) free( p_pic->p_data_orig );
}
226 227 228 229 230 231
subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
                                         video_format_t *p_fmt )
{
    subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
    memset( p_region, 0, sizeof(subpicture_region_t) );
    p_region->p_next = 0;
232
    p_region->p_cache = 0;
233
    p_region->fmt = *p_fmt;
234 235 236 237 238 239 240 241 242 243
    p_region->psz_text = 0;

    if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
        p_fmt->p_palette = p_region->fmt.p_palette =
            malloc( sizeof(video_palette_t) );
    else p_fmt->p_palette = p_region->fmt.p_palette = NULL;

    p_region->picture.p_data_orig = 0;

    if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
244 245 246 247 248 249 250

    vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
                          p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );

    if( !p_region->picture.i_planes )
    {
        free( p_region );
251
        free( p_fmt->p_palette );
252 253 254
        return NULL;
    }

255 256
    p_region->picture.pf_release = RegionPictureRelease;

257 258 259 260 261 262 263 264 265 266 267 268
    return p_region;
}

/**
 * Destroy a subpicture region
 *
 * \param p_this vlc_object_t
 * \param p_region the subpicture region to destroy
 */
void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
{
    if( !p_region ) return;
269 270
    if( p_region->picture.pf_release )
        p_region->picture.pf_release( &p_region->picture );
271
    if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
272
    if( p_region->psz_text ) free( p_region->psz_text );
273
    if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
274 275
    free( p_region );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
276

277
/**
278
 * Display a subpicture
279
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
280 281
 * Remove the reservation flag of a subpicture, which will cause it to be
 * ready for display.
282
 * \param p_spu the subpicture unit object
283 284
 * \param p_subpic the subpicture to display
 */
285
void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
286 287 288 289
{
    /* Check if status is valid */
    if( p_subpic->i_status != RESERVED_SUBPICTURE )
    {
290 291
        msg_Err( p_spu, "subpicture %p has invalid status #%d",
                 p_subpic, p_subpic->i_status );
Sam Hocevar's avatar
 
Sam Hocevar committed
292 293
    }

294 295
    /* Remove reservation flag */
    p_subpic->i_status = READY_SUBPICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
296

297
    if( p_subpic->i_channel == DEFAULT_CHAN )
Sam Hocevar's avatar
 
Sam Hocevar committed
298
    {
299 300 301
        p_subpic->i_channel = 0xFFFF;
        spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
        p_subpic->i_channel = DEFAULT_CHAN;
Sam Hocevar's avatar
 
Sam Hocevar committed
302
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
303 304
}

305
/**
306
 * Allocate a subpicture in the spu heap.
307
 *
308
 * This function create a reserved subpicture in the spu heap.
Sam Hocevar's avatar
 
Sam Hocevar committed
309 310 311
 * A null pointer is returned if the function fails. This method provides an
 * already allocated zone of memory in the spu data fields. It needs locking
 * since several pictures can be created by several producers threads.
312
 * \param p_spu the subpicture unit in which to create the subpicture
313 314
 * \return NULL on error, a reserved subpicture otherwise
 */
315
subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
Sam Hocevar's avatar
 
Sam Hocevar committed
316 317
{
    int                 i_subpic;                        /* subpicture index */
318
    subpicture_t *      p_subpic = NULL;            /* first free subpicture */
Sam Hocevar's avatar
 
Sam Hocevar committed
319

320
    /* Get lock */
321
    vlc_mutex_lock( &p_spu->subpicture_lock );
322

Sam Hocevar's avatar
 
Sam Hocevar committed
323 324 325
    /*
     * Look for an empty place
     */
yoann's avatar
yoann committed
326
    p_subpic = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
327 328
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
329
        if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
330 331
        {
            /* Subpicture is empty and ready for allocation */
332 333
            p_subpic = &p_spu->p_subpicture[i_subpic];
            p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
334
            break;
Sam Hocevar's avatar
 
Sam Hocevar committed
335 336 337
        }
    }

338 339
    /* If no free subpicture could be found */
    if( p_subpic == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
340
    {
341 342
        msg_Err( p_spu, "subpicture heap is full" );
        vlc_mutex_unlock( &p_spu->subpicture_lock );
343
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
344
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
345

346
    /* Copy subpicture information, set some default values */
347 348 349 350 351 352 353
    memset( p_subpic, 0, sizeof(subpicture_t) );
    p_subpic->i_type     = MEMORY_SUBPICTURE;
    p_subpic->i_status   = RESERVED_SUBPICTURE;
    p_subpic->b_absolute = VLC_TRUE;
    p_subpic->pf_render  = 0;
    p_subpic->pf_destroy = 0;
    p_subpic->p_sys      = 0;
354

355
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
356

357 358 359
    p_subpic->pf_create_region = __spu_CreateRegion;
    p_subpic->pf_destroy_region = __spu_DestroyRegion;

360
    return p_subpic;
Sam Hocevar's avatar
 
Sam Hocevar committed
361 362
}

363 364 365
/**
 * Remove a subpicture from the heap
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
366 367 368
 * This function frees a previously reserved subpicture.
 * It is meant to be used when the construction of a picture aborted.
 * This function does not need locking since reserved subpictures are ignored
369
 * by the spu.
370
 */
371
void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
372
{
gbazin's avatar
 
gbazin committed
373
    /* Get lock */
374
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
 
gbazin committed
375 376 377 378

    /* There can be race conditions so we need to check the status */
    if( p_subpic->i_status == FREE_SUBPICTURE )
    {
379
        vlc_mutex_unlock( &p_spu->subpicture_lock );
gbazin's avatar
 
gbazin committed
380 381 382
        return;
    }

383 384 385 386
    /* Check if status is valid */
    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
           && ( p_subpic->i_status != READY_SUBPICTURE ) )
    {
387
        msg_Err( p_spu, "subpicture %p has invalid status %d",
388 389 390
                         p_subpic, p_subpic->i_status );
    }

391 392 393 394
    while( p_subpic->p_region )
    {
        subpicture_region_t *p_region = p_subpic->p_region;
        p_subpic->p_region = p_region->p_next;
395
        spu_DestroyRegion( p_spu, p_region );
396 397
    }

398 399 400 401 402 403
    if( p_subpic->pf_destroy )
    {
        p_subpic->pf_destroy( p_subpic );
    }

    p_subpic->i_status = FREE_SUBPICTURE;
gbazin's avatar
 
gbazin committed
404

405
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
406 407 408
}

/*****************************************************************************
409
 * spu_RenderSubpictures: render a subpicture list
Sam Hocevar's avatar
 
Sam Hocevar committed
410
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
411
 * This function renders all sub picture units in the list.
Sam Hocevar's avatar
 
Sam Hocevar committed
412
 *****************************************************************************/
413 414 415 416
void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
                            picture_t *p_pic_dst, picture_t *p_pic_src,
                            subpicture_t *p_subpic,
                            int i_scale_width, int i_scale_height )
Sam Hocevar's avatar
 
Sam Hocevar committed
417
{
gbazin's avatar
 
gbazin committed
418
    /* Get lock */
419
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
 
gbazin committed
420 421 422

    /* Check i_status again to make sure spudec hasn't destroyed the subpic */
    while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
423
    {
424 425
        subpicture_region_t *p_region = p_subpic->p_region;

426
        /* Load the blending module */
427
        if( !p_spu->p_blend && p_region )
428
        {
429 430 431 432 433 434 435 436 437 438 439
            p_spu->p_blend = vlc_object_create( p_spu, sizeof(filter_t) );
            vlc_object_attach( p_spu->p_blend, p_spu );
            p_spu->p_blend->fmt_out.video.i_x_offset =
                p_spu->p_blend->fmt_out.video.i_y_offset = 0;
            p_spu->p_blend->fmt_out.video.i_aspect = p_fmt->i_aspect;
            p_spu->p_blend->fmt_out.video.i_chroma = p_fmt->i_chroma;

            p_spu->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');

            p_spu->p_blend->p_module =
                module_Need( p_spu->p_blend, "video blending", 0, 0 );
440
        }
441

442
        /* Load the text rendering module */
443
        if( !p_spu->p_text && p_region )
444
        {
445 446 447 448 449 450 451 452 453 454
            p_spu->p_text = vlc_object_create( p_spu, sizeof(filter_t) );
            vlc_object_attach( p_spu->p_text, p_spu );

            p_spu->p_text->fmt_out.video.i_width =
                p_spu->p_text->fmt_out.video.i_visible_width =
                    p_fmt->i_width;
            p_spu->p_text->fmt_out.video.i_height =
                p_spu->p_text->fmt_out.video.i_visible_height =
                    p_fmt->i_height;

455 456
            p_spu->p_text->pf_sub_buffer_new = spu_new_buffer;
            p_spu->p_text->pf_sub_buffer_del = spu_del_buffer;
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

            p_spu->p_text->p_module =
                module_Need( p_spu->p_text, "text renderer", 0, 0 );
        }

        /* Load the scaling module */
        if( !p_spu->p_scale && (i_scale_width != 1000 ||
            i_scale_height != 1000) )
        {
            p_spu->p_scale = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
            vlc_object_attach( p_spu->p_scale, p_spu );
            p_spu->p_scale->fmt_out.video.i_chroma =
                p_spu->p_scale->fmt_in.video.i_chroma =
                    VLC_FOURCC('Y','U','V','P');
            p_spu->p_scale->fmt_in.video.i_width =
                p_spu->p_scale->fmt_in.video.i_height = 32;
            p_spu->p_scale->fmt_out.video.i_width =
                p_spu->p_scale->fmt_out.video.i_height = 16;

            p_spu->p_scale->pf_vout_buffer_new = spu_new_video_buffer;
            p_spu->p_scale->pf_vout_buffer_del = spu_del_video_buffer;
            p_spu->p_scale->p_module =
                module_Need( p_spu->p_scale, "video filter2", 0, 0 );
480 481
        }

482 483
        if( p_subpic->pf_render )
        {
484 485 486 487 488 489 490
            /* HACK to remove when the ogt subpic decoder is gone */
            if( p_spu->p_parent &&
                p_spu->p_parent->i_object_type == VLC_OBJECT_VOUT )
            {
                vout_thread_t *p_vout = (vout_thread_t *)p_spu->p_parent;
                p_subpic->pf_render( p_vout, p_pic_dst, p_subpic );
            }
491
        }
492 493
        else while( p_region && p_spu->p_blend &&
                    p_spu->p_blend->pf_video_blend )
494 495 496 497
        {
            int i_x_offset = p_region->i_x + p_subpic->i_x;
            int i_y_offset = p_region->i_y + p_subpic->i_y;

498 499
            if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
            {
500 501
                if( p_spu->p_text && p_spu->p_text->p_module &&
                    p_spu->p_text->pf_render_string )
502 503 504
                {
                    /* TODO: do it in a less hacky way
                     * (modify text renderer API) */
505
                    subpicture_t *p_subpic_tmp;
506 507
                    subpicture_region_t tmp_region;
                    block_t *p_new_block =
508
                        block_New( p_spu, strlen(p_region->psz_text) + 1 );
509 510 511 512 513 514 515 516 517

                    if( p_new_block )
                    {
                        memcpy( p_new_block->p_buffer, p_region->psz_text,
                                p_new_block->i_buffer );
                        p_new_block->i_pts = p_new_block->i_dts =
                            p_subpic->i_start;
                        p_new_block->i_length =
                            p_subpic->i_start - p_subpic->i_stop;
518 519
                        p_subpic_tmp = p_spu->p_text->pf_render_string(
                            p_spu->p_text, p_new_block );
520

521
                        if( p_subpic_tmp )
522 523
                        {
                            tmp_region = *p_region;
524
                            *p_region = *p_subpic_tmp->p_region;
525
                            p_region->p_next = tmp_region.p_next;
526
                            *p_subpic_tmp->p_region = tmp_region;
527
                            p_spu->p_text->pf_sub_buffer_del( p_spu->p_text,
528
                                                              p_subpic_tmp );
529 530 531 532 533
                        }
                    }
                }
            }

534 535
            if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
            {
536
                i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
537
                    p_subpic->i_y;
538 539 540
            }
            else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
            {
541
                i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
542 543 544 545
            }

            if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
            {
546
                i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
547
                    p_subpic->i_x;
548 549 550
            }
            else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
            {
551
                i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
552 553 554 555 556 557 558
            }

            if( p_subpic->b_absolute )
            {
                i_x_offset = p_region->i_x + p_subpic->i_x;
                i_y_offset = p_region->i_y + p_subpic->i_y;

559 560 561 562 563 564 565 566 567 568
                if( p_spu->i_margin >= 0 )
                {
                    if( p_subpic->i_height + (unsigned int)p_spu->i_margin <=
                        p_fmt->i_height )
                    {
                        i_y_offset = p_fmt->i_height -
                            p_spu->i_margin - p_subpic->i_height;
                    }
                }
            }
569 570

            /* Force cropping if requested */
571
            if( p_spu->b_force_crop )
572
            {
573
                video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
574 575

                /* Find the intersection */
576
                if( p_spu->i_crop_x + p_spu->i_crop_width <= i_x_offset ||
577
                    i_x_offset + (int)p_fmt->i_visible_width <
578 579
                        p_spu->i_crop_x ||
                    p_spu->i_crop_y + p_spu->i_crop_height <= i_y_offset ||
580
                    i_y_offset + (int)p_fmt->i_visible_height <
581
                        p_spu->i_crop_y )
582 583 584 585 586 587 588
                {
                    /* No intersection */
                    p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
                }
                else
                {
                    int i_x, i_y, i_x_end, i_y_end;
589 590 591
                    i_x = __MAX( p_spu->i_crop_x, i_x_offset );
                    i_y = __MAX( p_spu->i_crop_y, i_y_offset );
                    i_x_end = __MIN( p_spu->i_crop_x + p_spu->i_crop_width,
592
                                   i_x_offset + (int)p_fmt->i_visible_width );
593
                    i_y_end = __MIN( p_spu->i_crop_y + p_spu->i_crop_height,
594 595 596 597 598 599 600 601 602 603
                                   i_y_offset + (int)p_fmt->i_visible_height );

                    p_fmt->i_x_offset = i_x - i_x_offset;
                    p_fmt->i_y_offset = i_y - i_y_offset;
                    p_fmt->i_visible_width = i_x_end - i_x;
                    p_fmt->i_visible_height = i_y_end - i_y;

                    i_x_offset = i_x;
                    i_y_offset = i_y;
                }
604 605 606
            }

            /* Force palette if requested */
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
            if( p_spu->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
                p_spu->p_blend->fmt_in.video.i_chroma )
            {
                p_spu->p_blend->fmt_in.video.p_palette->palette[0][3] =
                    p_spu->pi_alpha[0];
                p_spu->p_blend->fmt_in.video.p_palette->palette[1][3] =
                    p_spu->pi_alpha[1];
                p_spu->p_blend->fmt_in.video.p_palette->palette[2][3] =
                    p_spu->pi_alpha[2];
                p_spu->p_blend->fmt_in.video.p_palette->palette[3][3] =
                    p_spu->pi_alpha[3];
            }

            /* Scale SPU if necessary */
            if( p_region->p_cache )
622
            {
623 624 625 626 627 628 629 630 631
                if( i_scale_width * p_region->fmt.i_width / 1000 !=
                    p_region->p_cache->fmt.i_width ||
                    i_scale_height * p_region->fmt.i_height / 1000 !=
                    p_region->p_cache->fmt.i_height )
                {
                    p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
                                                 p_region->p_cache );
                    p_region->p_cache = 0;
                }
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 673 674 675 676 677 678 679 680 681 682 683 684
            if( (i_scale_width != 1000 || i_scale_height != 1000) &&
                p_spu->p_scale && !p_region->p_cache )
            {
                picture_t *p_pic;

                p_spu->p_scale->fmt_in.video = p_region->fmt;
                p_spu->p_scale->fmt_out.video = p_region->fmt;

                p_region->p_cache =
                    p_subpic->pf_create_region( VLC_OBJECT(p_spu),
                        &p_spu->p_scale->fmt_out.video );
                if( p_spu->p_scale->fmt_out.video.p_palette )
                    *p_spu->p_scale->fmt_out.video.p_palette =
                        *p_region->fmt.p_palette;
                p_region->p_cache->p_next = p_region->p_next;

                vout_CopyPicture( p_spu, &p_region->p_cache->picture,
                                  &p_region->picture );

                p_spu->p_scale->fmt_out.video.i_width =
                    p_region->fmt.i_width * i_scale_width / 1000;
                p_spu->p_scale->fmt_out.video.i_visible_width =
                    p_region->fmt.i_visible_width * i_scale_width / 1000;
                p_spu->p_scale->fmt_out.video.i_height =
                    p_region->fmt.i_height * i_scale_height / 1000;
                p_spu->p_scale->fmt_out.video.i_visible_height =
                    p_region->fmt.i_visible_height * i_scale_height / 1000;
                p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;

                p_pic = p_spu->p_scale->pf_video_filter(
                                 p_spu->p_scale, &p_region->p_cache->picture );
                if( p_pic )
                {
                    picture_t p_pic_tmp = p_region->p_cache->picture;
                    p_region->p_cache->picture = *p_pic;
                    *p_pic = p_pic_tmp;
                    free( p_pic );
                }
            }
            if( (i_scale_width != 1000 || i_scale_height != 1000) &&
                p_spu->p_scale && p_region->p_cache )
            {
                p_region = p_region->p_cache;
            }

            if( p_subpic->b_absolute )
            {
                i_x_offset = i_x_offset * i_scale_width / 1000;
                i_y_offset = i_y_offset * i_scale_height / 1000;
            }

            p_spu->p_blend->fmt_in.video = p_region->fmt;
685

686
            /* Update the output picture size */
687 688 689 690 691 692 693 694
            p_spu->p_blend->fmt_out.video.i_width =
                p_spu->p_blend->fmt_out.video.i_visible_width =
                    p_fmt->i_width;
            p_spu->p_blend->fmt_out.video.i_height =
                p_spu->p_blend->fmt_out.video.i_visible_height =
                    p_fmt->i_height;

           p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
695
                p_pic_src, &p_region->picture, i_x_offset, i_y_offset );
696 697 698 699

            p_region = p_region->p_next;
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
700 701
        p_subpic = p_subpic->p_next;
    }
gbazin's avatar
 
gbazin committed
702

703
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
704 705 706
}

/*****************************************************************************
707
 * spu_SortSubpictures: find the subpictures to display
Sam Hocevar's avatar
 
Sam Hocevar committed
708 709 710 711 712 713 714 715 716
 *****************************************************************************
 * This function parses all subpictures and decides which ones need to be
 * displayed. This operation does not need lock, since only READY_SUBPICTURE
 * are handled. If no picture has been selected, display_date will depend on
 * the subpicture.
 * We also check for ephemer DVD subpictures (subpictures that have
 * to be removed if a newer one is available), which makes it a lot
 * more difficult to guess if a subpicture has to be rendered or not.
 *****************************************************************************/
717
subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date )
Sam Hocevar's avatar
 
Sam Hocevar committed
718 719 720 721 722 723
{
    int i_index;
    subpicture_t *p_subpic     = NULL;
    subpicture_t *p_ephemer    = NULL;
    mtime_t       ephemer_date = 0;

724 725 726 727 728 729 730 731 732 733 734 735
    /* Run subpicture filters */
    for( i_index = 0; i_index < p_spu->i_filter; i_index++ )
    {
        subpicture_t *p_subpic_filter;
        p_subpic_filter = p_spu->pp_filter[i_index]->
            pf_sub_filter( p_spu->pp_filter[i_index], display_date );
        if( p_subpic_filter )
        {
            spu_DisplaySubpicture( p_spu, p_subpic_filter );
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
736 737 738 739
    /* We get an easily parsable chained list of subpictures which
     * ends with NULL since p_subpic was initialized to NULL. */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
740
        if( p_spu->p_subpicture[i_index].i_status == READY_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
741 742
        {
            /* If it is a DVD subpicture, check its date */
743
            if( p_spu->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
744
            {
745 746
                if( !p_spu->p_subpicture[i_index].b_ephemer
                     && display_date > p_spu->p_subpicture[i_index].i_stop )
Sam Hocevar's avatar
 
Sam Hocevar committed
747 748
                {
                    /* Too late, destroy the subpic */
749
                    spu_DestroySubpicture(p_spu,&p_spu->p_subpicture[i_index]);
Sam Hocevar's avatar
 
Sam Hocevar committed
750 751 752
                    continue;
                }

753
                if( display_date
754
                     && display_date < p_spu->p_subpicture[i_index].i_start )
Sam Hocevar's avatar
 
Sam Hocevar committed
755 756 757 758 759 760 761
                {
                    /* Too early, come back next monday */
                    continue;
                }

                /* If this is an ephemer subpic, see if it's the
                 * youngest we have */
762
                if( p_spu->p_subpicture[i_index].b_ephemer )
Sam Hocevar's avatar
 
Sam Hocevar committed
763 764 765
                {
                    if( p_ephemer == NULL )
                    {
766
                        p_ephemer = &p_spu->p_subpicture[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
767 768 769
                        continue;
                    }

770
                    if( p_spu->p_subpicture[i_index].i_start
Sam Hocevar's avatar
 
Sam Hocevar committed
771 772 773 774 775 776
                                                     < p_ephemer->i_start )
                    {
                        /* Link the previous ephemer subpicture and
                         * replace it with the current one */
                        p_ephemer->p_next = p_subpic;
                        p_subpic = p_ephemer;
777
                        p_ephemer = &p_spu->p_subpicture[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
778 779 780 781 782 783 784 785 786 787 788 789 790

                        /* If it's the 2nd youngest subpicture,
                         * register its date */
                        if( !ephemer_date
                              || ephemer_date > p_subpic->i_start )
                        {
                            ephemer_date = p_subpic->i_start;
                        }

                        continue;
                    }
                }

791 792
                p_spu->p_subpicture[i_index].p_next = p_subpic;
                p_subpic = &p_spu->p_subpicture[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
793

794 795
                /* If it's the 2nd youngest subpicture, register its date */
                if( !ephemer_date || ephemer_date > p_subpic->i_start )
Sam Hocevar's avatar
 
Sam Hocevar committed
796 797 798 799 800 801 802
                {
                    ephemer_date = p_subpic->i_start;
                }
            }
            /* If it's not a DVD subpicture, just register it */
            else
            {
803 804
                p_spu->p_subpicture[i_index].p_next = p_subpic;
                p_subpic = &p_spu->p_subpicture[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
805 806 807 808 809 810 811 812
            }
        }
    }

    /* If we found an ephemer subpicture, check if it has to be
     * displayed */
    if( p_ephemer != NULL )
    {
813
        if( p_ephemer->i_start <= ephemer_date )
Sam Hocevar's avatar
 
Sam Hocevar committed
814 815
        {
            /* Ephemer subpicture has lived too long */
816
            spu_DestroySubpicture( p_spu, p_ephemer );
Sam Hocevar's avatar
 
Sam Hocevar committed
817 818 819 820 821 822 823 824 825 826 827 828
        }
        else
        {
            /* Ephemer subpicture can still live a bit */
            p_ephemer->p_next = p_subpic;
            return p_ephemer;
        }
    }

    return p_subpic;
}

829
/*****************************************************************************
830
 * SpuClearChannel: clear an spu channel
831
 *****************************************************************************
832
 * This function destroys the subpictures which belong to the spu channel
833 834
 * corresponding to i_channel_id.
 *****************************************************************************/
835
static void SpuClearChannel( spu_t *p_spu, int i_channel )
836 837 838 839
{
    int                 i_subpic;                        /* subpicture index */
    subpicture_t *      p_subpic = NULL;            /* first free subpicture */

840
    vlc_mutex_lock( &p_spu->subpicture_lock );
841 842 843

    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
844
        p_subpic = &p_spu->p_subpicture[i_subpic];
845 846 847 848 849 850
        if( p_subpic->i_status == FREE_SUBPICTURE
            || ( p_subpic->i_status != RESERVED_SUBPICTURE
                 && p_subpic->i_status != READY_SUBPICTURE ) )
        {
            continue;
        }
851

852 853
        if( p_subpic->i_channel == i_channel )
        {
854 855 856 857
            while( p_subpic->p_region )
            {
                subpicture_region_t *p_region = p_subpic->p_region;
                p_subpic->p_region = p_region->p_next;
858
                spu_DestroyRegion( p_spu, p_region );
859 860
            }

861
            if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
862 863 864 865
            p_subpic->i_status = FREE_SUBPICTURE;
        }
    }

866
    vlc_mutex_unlock( &p_spu->subpicture_lock );
867
}
868

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
/*****************************************************************************
 * spu_ControlDefault: default methods for the subpicture unit control.
 *****************************************************************************/
int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
{
    int *pi, i;

    switch( i_query )
    {
    case SPU_CHANNEL_REGISTER:
        pi = (int *)va_arg( args, int * );
        p_spu->i_channel++;
        if( pi ) *pi = p_spu->i_channel;
        msg_Dbg( p_spu, "Registering subpicture channel, ID: %i",
                 p_spu->i_channel );
        break;

    case SPU_CHANNEL_CLEAR:
        i = (int)va_arg( args, int );
        SpuClearChannel( p_spu, i );
        break;

    default:
        msg_Dbg( p_spu, "control query not supported" );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Object variables callbacks
 *****************************************************************************/

903 904 905 906 907 908
/*****************************************************************************
 * UpdateSPU: update subpicture settings
 *****************************************************************************
 * This function is called from CropCallback and at initialization time, to
 * retrieve crop information from the input.
 *****************************************************************************/
909
static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
910 911 912
{
    vlc_value_t val;

913 914
    p_spu->b_force_alpha = VLC_FALSE;
    p_spu->b_force_crop = VLC_FALSE;
915 916 917

    if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;

918
    p_spu->b_force_crop = VLC_TRUE;
919
    var_Get( p_object, "x-start", &val );
920
    p_spu->i_crop_x = val.i_int;
921
    var_Get( p_object, "y-start", &val );
922
    p_spu->i_crop_y = val.i_int;
923
    var_Get( p_object, "x-end", &val );
924
    p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
925
    var_Get( p_object, "y-end", &val );
926
    p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
927 928 929 930 931 932 933

#if 0
    if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
    {
        int i;
        for( i = 0; i < 4; i++ )
        {
934
            p_spu->pi_color[i] = ((uint8_t *)val.p_address)[i];
935 936 937 938 939 940 941 942 943
        }
    }
#endif

    if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
    {
        int i;
        for( i = 0; i < 4; i++ )
        {
944 945 946
            p_spu->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
            p_spu->pi_alpha[i] = p_spu->pi_alpha[i] == 0xf ?
                0xff : p_spu->pi_alpha[i] << 4;
947
        }
948
        p_spu->b_force_alpha = VLC_TRUE;
949 950
    }

951 952 953
    msg_Dbg( p_object, "crop: %i,%i,%i,%i, alpha: %i",
             p_spu->i_crop_x, p_spu->i_crop_y,
             p_spu->i_crop_width, p_spu->i_crop_height, p_spu->b_force_alpha );
954 955 956 957 958 959 960 961 962 963
}

/*****************************************************************************
 * 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 )
{
964
    UpdateSPU( (spu_t *)p_data, p_object );
965 966 967 968 969 970
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Buffers allocation callbacks for the filters
 *****************************************************************************/
971 972 973 974 975 976 977 978 979 980 981 982
static subpicture_t *sub_new_buffer( filter_t *p_filter )
{
    spu_t *p_spu = (spu_t *)p_filter->p_owner;
    return spu_CreateSubpicture( p_spu );
}

static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
{
    spu_t *p_spu = (spu_t *)p_filter->p_owner;
    spu_DestroySubpicture( p_spu, p_subpic );
}

983 984
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
985 986 987 988 989 990
    subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
    memset( p_subpic, 0, sizeof(subpicture_t) );
    p_subpic->b_absolute = VLC_TRUE;

    p_subpic->pf_create_region = __spu_CreateRegion;
    p_subpic->pf_destroy_region = __spu_DestroyRegion;
991

992
    return p_subpic;
993 994
}

995
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
996
{
997