vout_subpictures.c 50.5 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1
2
3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
4
 * Copyright (C) 2000-2007 the VideoLAN team
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
 *
 * 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
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Sam Hocevar's avatar
 
Sam Hocevar committed
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
zorglub's avatar
zorglub committed
34
35
36
37
#include <vlc_vout.h>
#include <vlc_block.h>
#include <vlc_filter.h>
#include <vlc_osd.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38
#include "../libvlc.h"
39

40
41
42
43
44
45
/*****************************************************************************
 * 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 * );
46

gbazin's avatar
gbazin committed
47
48
static int spu_vaControlDefault( spu_t *, int, va_list );

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
58
59
static int spu_ParseChain( spu_t * );
static int SubFilterCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );

dionoea's avatar
dionoea committed
60
61
static int sub_filter_allocation_init( filter_t *, void * );
static void sub_filter_allocation_clear( filter_t * );
gbazin's avatar
gbazin committed
62
63
64
65
66
67
struct filter_owner_sys_t
{
    spu_t *p_spu;
    int i_channel;
};

68
69
70
71
72
enum {
    SCALE_DEFAULT,
    SCALE_TEXT,
    SCALE_SIZE
};
dionoea's avatar
dionoea committed
73

74
/**
75
 * Creates the subpicture unit
76
 *
77
 * \param p_this the parent object which creates the subpicture unit
78
 */
79
spu_t *__spu_Create( vlc_object_t *p_this )
80
81
{
    int i_index;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
82
83
    spu_t *p_spu = vlc_custom_create( p_this, sizeof( spu_t ),
                                      VLC_OBJECT_GENERIC, "subpicture" );
84
85
86

    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
87
        p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
88
89
    }

90
91
92
    p_spu->p_blend = NULL;
    p_spu->p_text = NULL;
    p_spu->p_scale = NULL;
gbazin's avatar
gbazin committed
93
    p_spu->pf_control = spu_vaControlDefault;
94
95

    /* Register the default subpicture channel */
gbazin's avatar
gbazin committed
96
    p_spu->i_channel = 2;
97

98
    vlc_mutex_init( &p_spu->subpicture_lock );
99

100
101
    vlc_object_attach( p_spu, p_this );

dionoea's avatar
dionoea committed
102
103
104
105
    p_spu->p_chain = filter_chain_New( p_spu, "sub filter", false,
                                       sub_filter_allocation_init,
                                       sub_filter_allocation_clear,
                                       p_spu );
106
107
108
109
110
111
112
113
114
115
116
117
    return p_spu;
}

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

118
119
120
    /* If the user requested a sub margin, we force the position. */
    var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_spu, "sub-margin", &val );
121
122
123
    p_spu->i_margin = val.i_int;

    var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
124
125
126
127
    var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );

    spu_ParseChain( p_spu );

128
    return VLC_SUCCESS;
129
}
dionoea's avatar
   
dionoea committed
130

131
132
int spu_ParseChain( spu_t *p_spu )
{
dionoea's avatar
dionoea committed
133
134
    char *psz_parser = var_GetString( p_spu, "sub-filter" );
    if( filter_chain_AppendFromString( p_spu->p_chain, psz_parser ) < 0 )
135
    {
dionoea's avatar
dionoea committed
136
137
        free( psz_parser );
        return VLC_EGENERIC;
138
    }
139

dionoea's avatar
dionoea committed
140
    free( psz_parser );
141
    return VLC_SUCCESS;
142
143
144
}

/**
145
 * Destroy the subpicture unit
146
 *
147
 * \param p_this the parent object which destroys the subpicture unit
148
 */
149
void spu_Destroy( spu_t *p_spu )
150
151
152
153
154
155
{
    int i_index;

    /* Destroy all remaining subpictures */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
156
        if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
157
        {
158
            spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
159
160
161
        }
    }

162
    if( p_spu->p_blend )
163
    {
164
165
        if( p_spu->p_blend->p_module )
            module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
166

167
        vlc_object_detach( p_spu->p_blend );
168
        vlc_object_release( p_spu->p_blend );
169
170
    }

171
    if( p_spu->p_text )
172
    {
173
174
        if( p_spu->p_text->p_module )
            module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
175

176
        vlc_object_detach( p_spu->p_text );
177
        vlc_object_release( p_spu->p_text );
178
179
    }

180
181
182
183
184
185
    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 );
186
        vlc_object_release( p_spu->p_scale );
187
188
    }

189
    filter_chain_Delete( p_spu->p_chain );
190
191

    vlc_mutex_destroy( &p_spu->subpicture_lock );
192
    vlc_object_release( p_spu );
193
194
}

195
196
197
/**
 * Attach/Detach the SPU from any input
 *
198
 * \param p_this the object in which to destroy the subpicture unit
199
200
 * \param b_attach to select attach or detach
 */
201
void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
202
203
204
{
    vlc_object_t *p_input;

205
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
206
207
208
    if( !p_input ) return;

    if( b_attach )
209
    {
210
211
        UpdateSPU( p_spu, VLC_OBJECT(p_input) );
        var_AddCallback( p_input, "highlight", CropCallback, p_spu );
212
213
214
215
216
        vlc_object_release( p_input );
    }
    else
    {
        /* Delete callback */
217
        var_DelCallback( p_input, "highlight", CropCallback, p_spu );
218
219
220
221
222
223
224
225
226
227
        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
 */
228
229
static void RegionPictureRelease( picture_t *p_pic )
{
230
    free( p_pic->p_data_orig );
231
}
232
233
234
235
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) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
236
237
    if( !p_region ) return NULL;

238
    memset( p_region, 0, sizeof(subpicture_region_t) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
239
    p_region->i_alpha = 0xff;
240
241
    p_region->p_next = NULL;
    p_region->p_cache = NULL;
242
    p_region->fmt = *p_fmt;
243
    p_region->psz_text = NULL;
hartman's avatar
hartman committed
244
    p_region->p_style = NULL;
245
246
247
248
249
250

    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;

251
    p_region->picture.p_data_orig = NULL;
252
253

    if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
254
255
256
257
258
259
260

    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 );
261
        free( p_fmt->p_palette );
262
263
264
        return NULL;
    }

265
266
    p_region->picture.pf_release = RegionPictureRelease;

267
268
269
    return p_region;
}

270
271
272
273
274
275
276
277
278
279
280
281
/**
 * Make a subpicture region from an existing picture_t
 *
 * \param p_this vlc_object_t
 * \param p_fmt the format that this subpicture region should have
 * \param p_pic a pointer to the picture creating the region (not freed)
 */
subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
                                       video_format_t *p_fmt,
                                       picture_t *p_pic )
{
    subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
282
    (void)p_this;
283
    if( !p_region ) return NULL;
284
    memset( p_region, 0, sizeof(subpicture_region_t) );
285
    p_region->i_alpha = 0xff;
286
287
288
289
    p_region->p_next = 0;
    p_region->p_cache = 0;
    p_region->fmt = *p_fmt;
    p_region->psz_text = 0;
hartman's avatar
hartman committed
290
    p_region->p_style = NULL;
291
292
293
294
295
296
297
298
299
300
301
302

    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;

    memcpy( &p_region->picture, p_pic, sizeof(picture_t) );
    p_region->picture.pf_release = RegionPictureRelease;

    return p_region;
}

303
304
305
306
307
308
309
310
311
/**
 * 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;
312
313
    if( p_region->picture.pf_release )
        p_region->picture.pf_release( &p_region->picture );
314
    free( p_region->fmt.p_palette );
315
    if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
316

317
318
    free( p_region->psz_text );
    free( p_region->psz_html );
319
    //free( p_region->p_style ); FIXME --fenrir plugin does not allocate the memory for it. I think it might lead to segfault, video renderer can live longer than the decoder
320
321
    free( p_region );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
322

323
/**
324
 * Display a subpicture
325
 *
Sam Hocevar's avatar
   
Sam Hocevar committed
326
327
 * Remove the reservation flag of a subpicture, which will cause it to be
 * ready for display.
328
 * \param p_spu the subpicture unit object
329
330
 * \param p_subpic the subpicture to display
 */
331
void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
332
333
334
335
{
    /* Check if status is valid */
    if( p_subpic->i_status != RESERVED_SUBPICTURE )
    {
336
337
        msg_Err( p_spu, "subpicture %p has invalid status #%d",
                 p_subpic, p_subpic->i_status );
Sam Hocevar's avatar
 
Sam Hocevar committed
338
339
    }

340
341
    /* Remove reservation flag */
    p_subpic->i_status = READY_SUBPICTURE;
Sam Hocevar's avatar
   
Sam Hocevar committed
342

343
    if( p_subpic->i_channel == DEFAULT_CHAN )
Sam Hocevar's avatar
   
Sam Hocevar committed
344
    {
345
346
347
        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
348
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
349
350
}

351
/**
352
 * Allocate a subpicture in the spu heap.
353
 *
354
 * This function create a reserved subpicture in the spu heap.
Sam Hocevar's avatar
 
Sam Hocevar committed
355
356
357
 * 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.
358
 * \param p_spu the subpicture unit in which to create the subpicture
359
360
 * \return NULL on error, a reserved subpicture otherwise
 */
361
subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
Sam Hocevar's avatar
 
Sam Hocevar committed
362
363
{
    int                 i_subpic;                        /* subpicture index */
364
    subpicture_t *      p_subpic = NULL;            /* first free subpicture */
Sam Hocevar's avatar
 
Sam Hocevar committed
365

366
    /* Get lock */
367
    vlc_mutex_lock( &p_spu->subpicture_lock );
368

Sam Hocevar's avatar
 
Sam Hocevar committed
369
370
371
    /*
     * Look for an empty place
     */
yoann's avatar
yoann committed
372
    p_subpic = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
373
374
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
375
        if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
376
377
        {
            /* Subpicture is empty and ready for allocation */
378
379
            p_subpic = &p_spu->p_subpicture[i_subpic];
            p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
380
            break;
Sam Hocevar's avatar
 
Sam Hocevar committed
381
382
383
        }
    }

384
385
    /* If no free subpicture could be found */
    if( p_subpic == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
386
    {
387
388
        msg_Err( p_spu, "subpicture heap is full" );
        vlc_mutex_unlock( &p_spu->subpicture_lock );
389
        return NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
390
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
391

392
    /* Copy subpicture information, set some default values */
393
394
    memset( p_subpic, 0, sizeof(subpicture_t) );
    p_subpic->i_status   = RESERVED_SUBPICTURE;
395
396
397
    p_subpic->b_absolute = true;
    p_subpic->b_pausable = false;
    p_subpic->b_fade     = false;
398
    p_subpic->i_alpha    = 0xFF;
399
400
401
402
    p_subpic->p_region   = NULL;
    p_subpic->pf_render  = NULL;
    p_subpic->pf_destroy = NULL;
    p_subpic->p_sys      = NULL;
403
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
404

405
    p_subpic->pf_create_region = __spu_CreateRegion;
406
    p_subpic->pf_make_region = __spu_MakeRegion;
407
    p_subpic->pf_destroy_region = __spu_DestroyRegion;
408

409
    return p_subpic;
Sam Hocevar's avatar
 
Sam Hocevar committed
410
411
}

412
413
414
/**
 * Remove a subpicture from the heap
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
415
416
417
 * 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
418
 * by the spu.
419
 */
420
void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
421
{
gbazin's avatar
   
gbazin committed
422
    /* Get lock */
423
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
   
gbazin committed
424
425
426
427

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

432
433
434
435
    /* Check if status is valid */
    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
           && ( p_subpic->i_status != READY_SUBPICTURE ) )
    {
436
        msg_Err( p_spu, "subpicture %p has invalid status %d",
437
438
439
                         p_subpic, p_subpic->i_status );
    }

440
441
442
443
    while( p_subpic->p_region )
    {
        subpicture_region_t *p_region = p_subpic->p_region;
        p_subpic->p_region = p_region->p_next;
444
        spu_DestroyRegion( p_spu, p_region );
445
446
    }

447
448
449
450
451
452
    if( p_subpic->pf_destroy )
    {
        p_subpic->pf_destroy( p_subpic );
    }

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

454
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
455
456
457
}

/*****************************************************************************
458
 * spu_RenderSubpictures: render a subpicture list
Sam Hocevar's avatar
 
Sam Hocevar committed
459
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
460
 * This function renders all sub picture units in the list.
Sam Hocevar's avatar
 
Sam Hocevar committed
461
 *****************************************************************************/
462
463
464
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,
465
                            int i_scale_width_orig, int i_scale_height_orig )
Sam Hocevar's avatar
 
Sam Hocevar committed
466
{
467
468
    int i_source_video_width;
    int i_source_video_height;
469
    subpicture_t *p_subpic_v = p_subpic;
470

gbazin's avatar
   
gbazin committed
471
    /* Get lock */
472
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
   
gbazin committed
473

474
475
476
477
478
479
480
481
482
483
    for( p_subpic_v = p_subpic;
            p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE;
            p_subpic_v = p_subpic_v->p_next )
    {
        if( p_subpic_v->pf_pre_render )
        {
            p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v, mdate() );
        }
    }

484
485
486
487
488
489
490
491
    if( i_scale_width_orig <= 0 )
        i_scale_width_orig = 1;
    if( i_scale_height_orig <= 0 )
        i_scale_height_orig = 1;

    i_source_video_width  = p_fmt->i_width  * 1000 / i_scale_width_orig;
    i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig;

gbazin's avatar
   
gbazin committed
492
    /* Check i_status again to make sure spudec hasn't destroyed the subpic */
493
    while( ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
494
    {
495
        subpicture_region_t *p_region;
496
497
498
499
500
        int pi_scale_width[ SCALE_SIZE ];
        int pi_scale_height[ SCALE_SIZE ];
        int pi_subpic_x[ SCALE_SIZE ];
        int k;

501
502
503
504
505
506
507
508
509
510
511
        /* If the source video and subtitles stream agree on the size of
         * the video then disregard all further references to the subtitle
         * stream.
         */
        if( ( i_source_video_height == p_subpic->i_original_picture_height ) &&
            ( i_source_video_width  == p_subpic->i_original_picture_width ) )
        {
            p_subpic->i_original_picture_height = 0;
            p_subpic->i_original_picture_width = 0;
        }

512
        for( k = 0; k < SCALE_SIZE ; k++ )
513
            pi_subpic_x[ k ] = p_subpic->i_x;
514

515
516
517
518
519
520
521
522
523
524
525
526
        if( p_subpic->pf_update_regions )
        {
            if ( p_subpic->p_region ) {
                spu_DestroyRegion( p_spu, p_subpic->p_region );
            }
            p_subpic->p_region = p_region = p_subpic->pf_update_regions( p_fmt, p_spu, p_subpic, mdate() );
        }
        else
        {
            p_region = p_subpic->p_region;
        }

527
        /* Load the blending module */
528
        if( !p_spu->p_blend && p_region )
529
        {
530
            p_spu->p_blend = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
531
532
533
534
535
536
            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;

dionoea's avatar
dionoea committed
537
538
            /* The blend module will be loaded when needed with the real
            * input format */
539
540
            memset( &p_spu->p_blend->fmt_in, 0, sizeof(p_spu->p_blend->fmt_in) );
            p_spu->p_blend->p_module = NULL;
541
        }
542

543
544
545
546
547
        /* Load the text rendering module; it is possible there is a
         * text region somewhere in the subpicture other than the first
         * element in the region list, so just load it anyway as we'll
         * probably want it sooner or later. */
        if( !p_spu->p_text && p_region )
548
        {
549
550
            char *psz_modulename = NULL;

551
            p_spu->p_text = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
552
553
554
555
            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 =
556
                p_fmt->i_width;
557
558
            p_spu->p_text->fmt_out.video.i_height =
                p_spu->p_text->fmt_out.video.i_visible_height =
559
                p_fmt->i_height;
560

561
562
            p_spu->p_text->pf_sub_buffer_new = spu_new_buffer;
            p_spu->p_text->pf_sub_buffer_del = spu_del_buffer;
563
564
565
566
567

            psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
            if( psz_modulename && *psz_modulename )
            {
                p_spu->p_text->p_module =
568
                    module_Need( p_spu->p_text, "text renderer",
569
                                 psz_modulename, true );
570
571
572
573
574
575
            }
            if( !p_spu->p_text->p_module )
            {
                p_spu->p_text->p_module =
                    module_Need( p_spu->p_text, "text renderer", 0, 0 );
            }
576
            free( psz_modulename );
577
        }
578

579
        if( p_spu->p_text )
580
        {
581
582
            subpicture_region_t *p_text_region = p_subpic->p_region;

dionoea's avatar
dionoea committed
583
584
585
586
587
588
589
            /* Only overwrite the size fields if the region is still in
             * pre-rendered TEXT format. We have to traverse the subregion
             * list because if more than one subregion is present, the text
             * region isn't guarentteed to be the first in the list, and
             * only text regions use this flag. All of this effort assists
             * with the rescaling of text that has been rendered at native
             * resolution, rather than video resolution.
590
591
             */
            while( p_text_region &&
592
                   ( p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
593
            {
594
                p_text_region = p_text_region->p_next;
595
            }
596

dionoea's avatar
dionoea committed
597
            if( p_text_region &&
598
                ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) )
599
            {
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
                if( (p_subpic->i_original_picture_height > 0) &&
                    (p_subpic->i_original_picture_width  > 0) )
                {
                    p_spu->p_text->fmt_out.video.i_width =
                        p_spu->p_text->fmt_out.video.i_visible_width =
                        p_subpic->i_original_picture_width;
                    p_spu->p_text->fmt_out.video.i_height =
                        p_spu->p_text->fmt_out.video.i_visible_height =
                        p_subpic->i_original_picture_height;
                }
                else
                {
                    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;
                }
619
            }
620
        }
621

622
623
        pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig;
        pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig;
624

625
626
627
628
629
630
631
        if( p_spu->p_text )
        {
            pi_scale_width[ SCALE_TEXT ]     = ( p_fmt->i_width * 1000 ) /
                                          p_spu->p_text->fmt_out.video.i_width;
            pi_scale_height[ SCALE_TEXT ]    = ( p_fmt->i_height * 1000 ) /
                                          p_spu->p_text->fmt_out.video.i_height;
        }
632
633
634
635
636
637
638
639
640
        /* If we have an explicit size plane to render to, then turn off
         * the fontsize rescaling.
         */
        if( (p_subpic->i_original_picture_height > 0) &&
            (p_subpic->i_original_picture_width  > 0) )
        {
            i_scale_width_orig  = 1000;
            i_scale_height_orig = 1000;
        }
641
642

        for( k = 0; k < SCALE_SIZE ; k++ )
643
        {
dionoea's avatar
dionoea committed
644
645
646
            /* Case of both width and height being specified has been dealt
             * with above by instead rendering to an output pane of the
             * explicit dimensions specified - we don't need to scale it.
647
             */
648
            if( (p_subpic->i_original_picture_height > 0) &&
Jean-Paul Saman's avatar
Jean-Paul Saman committed
649
                (p_subpic->i_original_picture_width <= 0) )
650
651
652
653
654
655
            {
                pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height /
                                 p_subpic->i_original_picture_height;
                pi_scale_width[ k ]  = pi_scale_width[ k ]  * i_source_video_height /
                                 p_subpic->i_original_picture_height;
            }
hartman's avatar
hartman committed
656
        }
657

658
659
        /* Set default subpicture aspect ratio */
        if( p_region && p_region->fmt.i_aspect &&
660
            ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
zorglub's avatar
zorglub committed
661
        {
662
663
664
665
            p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
            p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
        }
        if( p_region &&
666
            ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
667
668
669
670
        {
            p_region->fmt.i_sar_den = p_fmt->i_sar_den;
            p_region->fmt.i_sar_num = p_fmt->i_sar_num;
        }
671

672
        /* Take care of the aspect ratio */
dionoea's avatar
dionoea committed
673
        if( p_region &&
674
675
            ( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
              ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) ) )
676
        {
677
            for( k = 0; k < SCALE_SIZE ; k++ )
678
679
680
681
682
683
            {
                pi_scale_width[ k ] = pi_scale_width[ k ] *
                    (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
                    p_region->fmt.i_sar_den / p_fmt->i_sar_num;
                pi_subpic_x[ k ] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
            }
684
685
        }

686
        /* Load the scaling module */
687
        if( !p_spu->p_scale &&
688
689
690
691
           ((((pi_scale_width[ SCALE_TEXT ]    > 0)     || (pi_scale_height[ SCALE_TEXT ]    > 0)) &&
             ((pi_scale_width[ SCALE_TEXT ]    != 1000) || (pi_scale_height[ SCALE_TEXT ]    != 1000))) ||
            (((pi_scale_width[ SCALE_DEFAULT ] > 0)     || (pi_scale_height[ SCALE_DEFAULT ] > 0)) &&
             ((pi_scale_width[ SCALE_DEFAULT ] != 1000) || (pi_scale_height[ SCALE_DEFAULT ] != 1000)))) )
692
693
694
695
696
697
        {
            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');
698
699
            /* FIXME: We'll also be using it for YUVA and RGBA blending ... */

700
701
702
703
704
705
706
707
708
            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 );
709
710
        }

711
        while( p_region )
712
        {
713
            video_format_t orig_fmt = p_region->fmt;
714
            bool b_rerender_text = false;
715
            int i_fade_alpha = 255;
716
717
718
719
720
            int i_x_offset;
            int i_y_offset;
            int i_scale_idx   = SCALE_DEFAULT;
            int i_inv_scale_x = 1000;
            int i_inv_scale_y = 1000;
721

722
723
            if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
            {
724
                if( p_spu->p_text && p_spu->p_text->p_module )
725
                {
726
727
                    vlc_value_t  val;

dionoea's avatar
dionoea committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
                    /* Setup 3 variables which can be used to render
                     * time-dependent text (and effects). The first indicates
                     * the total amount of time the text will be on screen,
                     * the second the amount of time it has already been on
                     * screen (can be a negative value as text is layed out
                     * before it is rendered) and the third is a feedback
                     * variable from the renderer - if the renderer sets it
                     * then this particular text is time-dependent, eg. the
                     * visual progress bar inside the text in karaoke and the
                     * text needs to be rendered multiple times in order for
                     * the effect to work - we therefore need to return the
                     * region to its original state at the end of the loop,
                     * instead of leaving it in YUVA or YUVP.
                     * Any renderer which is unaware of how to render
                     * time-dependent text can happily ignore the variables
                     * and render the text the same as usual - it should at
                     * least show up on screen, but the effect won't change
                     * the text over time.
746
747
748
749
750
751
752
753
754
755
756
                     */

                    var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
                    val.i_time = p_subpic->i_stop - p_subpic->i_start;
                    var_Set( p_spu->p_text, "spu-duration", val );

                    var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
                    val.i_time = mdate() - p_subpic->i_start;
                    var_Set( p_spu->p_text, "spu-elapsed", val );

                    var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
757
                    var_SetBool( p_spu->p_text, "text-rerender", false );
758

759
760
761
762
                    var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER );
                    var_SetInteger( p_spu->p_text, "scale",
                              __MIN(i_scale_width_orig, i_scale_height_orig) );

763
764
765
766
767
768
769
770
                    if( p_spu->p_text->pf_render_html && p_region->psz_html )
                    {
                        p_spu->p_text->pf_render_html( p_spu->p_text,
                                                       p_region, p_region );
                    }
                    else if( p_spu->p_text->pf_render_text )
                    {
                        p_spu->p_text->pf_render_text( p_spu->p_text,
dionoea's avatar
dionoea committed
771
                                                       p_region, p_region );
772
                    }
773
774
775
776
777
                    b_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );

                    var_Destroy( p_spu->p_text, "spu-duration" );
                    var_Destroy( p_spu->p_text, "spu-elapsed" );
                    var_Destroy( p_spu->p_text, "text-rerender" );
778
                    var_Destroy( p_spu->p_text, "scale" );
779
                }
780
                p_region->i_align |= SUBPICTURE_RENDERED;
781
            }
782

783
784
785
786
787
788
789
790
791
            if( p_region->i_align & SUBPICTURE_RENDERED )
            {
                i_scale_idx   = SCALE_TEXT;
                i_inv_scale_x = i_scale_width_orig;
                i_inv_scale_y = i_scale_height_orig;
            }

            i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000;
            i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000;
792

793
            /* Force palette if requested */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
794
            if( p_spu->b_force_palette &&
795
                ( VLC_FOURCC('Y','U','V','P') == p_region->fmt.i_chroma ) )
796
            {
797
798
                memcpy( p_region->fmt.p_palette->palette,
                        p_spu->palette, 16 );
799
800
801
            }

            /* Scale SPU if necessary */
dionoea's avatar
dionoea committed
802
            if( p_region->p_cache &&
803
                ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
804
            {
805
                if( pi_scale_width[ i_scale_idx ] * p_region->fmt.i_width / 1000 !=
806
                    p_region->p_cache->fmt.i_width ||
807
                    pi_scale_height[ i_scale_idx ] * p_region->fmt.i_height / 1000 !=
808
809
810
811
812
813
                    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;
                }
814
            }
815

dionoea's avatar
dionoea committed
816
            if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
817
                  ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
dionoea's avatar
dionoea committed
818
                ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
819
820
821
                  ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
                p_spu->p_scale && !p_region->p_cache &&
                ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
            {
                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 =
840
                    p_region->fmt.i_width * pi_scale_width[ i_scale_idx ] / 1000;
841
                p_spu->p_scale->fmt_out.video.i_visible_width =
842
                    p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000;
843
                p_spu->p_scale->fmt_out.video.i_height =
844
                    p_region->fmt.i_height * pi_scale_height[ i_scale_idx ] / 1000;
845
                p_spu->p_scale->fmt_out.video.i_visible_height =
846
                    p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000;
847
                p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
848
849
                p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
                p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
Laurent Aimar's avatar
Laurent Aimar committed
850
                p_region->p_cache->i_align = p_region->i_align;
851
                p_region->p_cache->i_alpha = p_region->i_alpha;
852
853
854
855
856
857
858
859
860
861
862

                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 );
                }
            }
863
864
865
866
867
868
869

            if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
                  ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
                ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
                  ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
                p_spu->p_scale && p_region->p_cache &&
                ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )  )
870
871
872
873
            {
                p_region = p_region->p_cache;
            }

Laurent Aimar's avatar
Laurent Aimar committed
874
            if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
875
876
            {
                i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
877
                    (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / 1000;
878
            }
Laurent Aimar's avatar
Laurent Aimar committed
879
            else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
880
881
882
883
            {
                i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
            }

Laurent Aimar's avatar
Laurent Aimar committed
884
            if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
885
886
            {
                i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
887
888
                    (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
                    * i_inv_scale_x / 1000;
889
            }
Laurent Aimar's avatar
Laurent Aimar committed
890
            else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
891
892
893
894
            {
                i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
            }

895
896
            if( p_subpic->b_absolute )
            {
897
898
899
900
901
902
903
                i_x_offset = (p_region->i_x +
                    pi_subpic_x[ i_scale_idx ] *
                                     pi_scale_width[ i_scale_idx ] / 1000)
                    * i_inv_scale_x / 1000;
                i_y_offset = (p_region->i_y +
                    p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000)
                    * i_inv_scale_y / 1000;
904

905
            }
dionoea's avatar
dionoea committed
906

907
908
            i_x_offset = __MAX( i_x_offset, 0 );
            i_y_offset = __MAX( i_y_offset, 0 );
909

910
            if( ( p_spu->i_margin != 0 ) &&
911
                ( p_spu->b_force_crop == false ) )
912
913
            {
                int i_diff = 0;
914
915
                int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000;
                int i_high = i_low + p_region->fmt.i_height;
916
917

                /* crop extra margin to keep within bounds */
dionoea's avatar
dionoea committed
918
919
920
921
                if( i_low < 0 )
                    i_diff = i_low;
                if( i_high > (int)p_fmt->i_height )
                    i_diff = i_high - p_fmt->i_height;
922
                i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / 1000 + i_diff );
923
924
            }

925
926
927
928
929
930
931
932
933
934
935
936
            if( p_subpic->b_fade )
            {
                mtime_t i_fade_start = ( p_subpic->i_stop +
                                         p_subpic->i_start ) / 2;
                mtime_t i_now = mdate();
                if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
                {
                    i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
                                   ( p_subpic->i_stop - i_fade_start );
                }
            }

937
938
            if( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )
            {
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
                if( p_spu->p_blend->fmt_in.video.i_chroma != p_region->fmt.i_chroma )
                {
                    /* The chroma is not the same, we need to reload the blend module
                     * XXX to match the old behaviour just test !p_spu->p_blend->fmt_in.video.i_chroma */
                    if( p_spu->p_blend->p_module )
                        module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );

                    p_spu->p_blend->fmt_in.video = p_region->fmt;
                    p_spu->p_blend->p_module = module_Need( p_spu->p_blend, "video blending", 0, 0 );
                }
                else
                {
                    p_spu->p_blend->fmt_in.video = p_region->fmt;
                }

                /* Force cropping if requested */
                if( p_spu->b_force_crop )
                {
                    video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
                    int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000
                                        * i_inv_scale_x / 1000;
                    int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000
                                        * i_inv_scale_y / 1000;
                    int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000
                                        * i_inv_scale_x / 1000;
                    int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000
                                        * i_inv_scale_y / 1000;

                    /* Find the intersection */
                    if( i_crop_x + i_crop_width <= i_x_offset ||
                        i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
                        i_crop_y + i_crop_height <= i_y_offset ||
                        i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
                    {
                        /* 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;
                        i_x = __MAX( i_crop_x, i_x_offset );
                        i_y = __MAX( i_crop_y, i_y_offset );
                        i_x_end = __MIN( i_crop_x + i_crop_width,
                                       i_x_offset + (int)p_fmt->i_visible_width );
                        i_y_end = __MIN( i_crop_y + i_crop_height,
                                       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;
                    }
                }

                i_x_offset = __MAX( i_x_offset, 0 );
                i_y_offset = __MAX( i_y_offset, 0 );