vout_subpictures.c 39 KB
Newer Older
Sam Hocevar's avatar
   
Sam Hocevar committed
1
2
3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
4
 * Copyright (C) 2000-2005 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
29
30
31
32
 *****************************************************************************/

/*****************************************************************************
 * 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
#include "vlc_filter.h"

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

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

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

gbazin's avatar
gbazin committed
57
58
59
60
61
62
struct filter_owner_sys_t
{
    spu_t *p_spu;
    int i_channel;
};

63
/**
64
 * Creates the subpicture unit
65
 *
66
 * \param p_this the parent object which creates the subpicture unit
67
 */
68
spu_t *__spu_Create( vlc_object_t *p_this )
69
70
{
    int i_index;
71
    spu_t *p_spu = vlc_object_create( p_this, VLC_OBJECT_SPU );
72
73
74

    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
75
        p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
76
77
    }

78
79
80
    p_spu->p_blend = NULL;
    p_spu->p_text = NULL;
    p_spu->p_scale = NULL;
81
    p_spu->i_filter = 0;
gbazin's avatar
gbazin committed
82
    p_spu->pf_control = spu_vaControlDefault;
83
84

    /* Register the default subpicture channel */
gbazin's avatar
gbazin committed
85
    p_spu->i_channel = 2;
86
87

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

89
90
    vlc_object_attach( p_spu, p_this );

91
92
93
94
95
96
97
98
99
100
101
102
103
    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;

104
105
106
    /* 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 );
107
108
109
110
111
    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;
112
    while( psz_filter && *psz_filter )
113
    {
114
        char *psz_parser = strchr( psz_filter, ':' );
115
116
117

        if( psz_parser ) *psz_parser++ = 0;

118
119
120
121
122
123
124
        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_module =
            module_Need( p_spu->pp_filter[p_spu->i_filter],
125
                         "sub filter", psz_filter, 0 );
gbazin's avatar
gbazin committed
126
127
128
129
130
131
132
133
        if( p_spu->pp_filter[p_spu->i_filter]->p_module )
        {
            filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
            p_spu->pp_filter[p_spu->i_filter]->p_owner = p_sys;
            spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
            p_sys->p_spu = p_spu;
            p_spu->i_filter++;
        }
134
135
        else
        {
136
            msg_Dbg( p_spu, "no sub filter found" );
137
138
139
            vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
            vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
        }
140
141
142
143
144
145
146

        if( p_spu->i_filter >= 10 )
        {
            msg_Dbg( p_spu, "can't add anymore filters" );
        }

        psz_filter = psz_parser;
147
148
    }
    if( psz_filter_orig ) free( psz_filter_orig );
149

150
    return VLC_EGENERIC;
151
152
153
}

/**
154
 * Destroy the subpicture unit
155
 *
156
 * \param p_this the parent object which destroys the subpicture unit
157
 */
158
void spu_Destroy( spu_t *p_spu )
159
160
161
{
    int i_index;

162
163
    vlc_object_detach( p_spu );

164
165
166
    /* Destroy all remaining subpictures */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
167
        if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
168
        {
169
            spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
170
171
172
        }
    }

173
    if( p_spu->p_blend )
174
    {
175
176
        if( p_spu->p_blend->p_module )
            module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
177

178
179
        vlc_object_detach( p_spu->p_blend );
        vlc_object_destroy( p_spu->p_blend );
180
181
    }

182
    if( p_spu->p_text )
183
    {
184
185
        if( p_spu->p_text->p_module )
            module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
186

187
188
        vlc_object_detach( p_spu->p_text );
        vlc_object_destroy( p_spu->p_text );
189
190
    }

191
192
193
194
195
196
197
198
199
    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 );
    }

200
201
202
203
    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 );
gbazin's avatar
gbazin committed
204
        free( p_spu->pp_filter[p_spu->i_filter]->p_owner );
205
206
207
208
        vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
        vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
    }

209
210
    vlc_mutex_destroy( &p_spu->subpicture_lock );
    vlc_object_destroy( p_spu );
211
212
213
214
215
}

/**
 * Attach/Detach the SPU from any input
 *
216
 * \param p_this the object in which to destroy the subpicture unit
217
218
 * \param b_attach to select attach or detach
 */
219
void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, vlc_bool_t b_attach )
220
221
222
{
    vlc_object_t *p_input;

223
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
224
225
226
    if( !p_input ) return;

    if( b_attach )
227
    {
228
229
        UpdateSPU( p_spu, VLC_OBJECT(p_input) );
        var_AddCallback( p_input, "highlight", CropCallback, p_spu );
230
231
232
233
234
        vlc_object_release( p_input );
    }
    else
    {
        /* Delete callback */
235
        var_DelCallback( p_input, "highlight", CropCallback, p_spu );
236
237
238
239
240
241
242
243
244
245
        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
 */
246
247
248
249
static void RegionPictureRelease( picture_t *p_pic )
{
    if( p_pic->p_data_orig ) free( p_pic->p_data_orig );
}
250
251
252
253
254
255
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;
256
    p_region->p_cache = 0;
257
    p_region->fmt = *p_fmt;
258
    p_region->psz_text = 0;
hartman's avatar
hartman committed
259
    p_region->p_style = NULL;
260
261
262
263
264
265

    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;

266
    p_region->picture.p_data_orig = NULL;
267
268

    if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
269
270
271
272
273
274
275

    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 );
276
        free( p_fmt->p_palette );
277
278
279
        return NULL;
    }

280
281
    p_region->picture.pf_release = RegionPictureRelease;

282
283
284
    return p_region;
}

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/**
 * 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) );
    memset( p_region, 0, sizeof(subpicture_region_t) );
    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
302
    p_region->p_style = NULL;
303
304
305
306
307
308
309
310
311
312
313
314

    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;
}

315
316
317
318
319
320
321
322
323
/**
 * 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;
324
325
    if( p_region->picture.pf_release )
        p_region->picture.pf_release( &p_region->picture );
326
    if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
327
    if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
328
329
    free( p_region );
}
Sam Hocevar's avatar
   
Sam Hocevar committed
330

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

348
349
    /* Remove reservation flag */
    p_subpic->i_status = READY_SUBPICTURE;
Sam Hocevar's avatar
   
Sam Hocevar committed
350

351
    if( p_subpic->i_channel == DEFAULT_CHAN )
Sam Hocevar's avatar
   
Sam Hocevar committed
352
    {
353
354
355
        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
356
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
357
358
}

359
/**
360
 * Allocate a subpicture in the spu heap.
361
 *
362
 * This function create a reserved subpicture in the spu heap.
Sam Hocevar's avatar
   
Sam Hocevar committed
363
364
365
 * 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.
366
 * \param p_spu the subpicture unit in which to create the subpicture
367
368
 * \return NULL on error, a reserved subpicture otherwise
 */
369
subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
Sam Hocevar's avatar
   
Sam Hocevar committed
370
371
{
    int                 i_subpic;                        /* subpicture index */
372
    subpicture_t *      p_subpic = NULL;            /* first free subpicture */
Sam Hocevar's avatar
   
Sam Hocevar committed
373

374
    /* Get lock */
375
    vlc_mutex_lock( &p_spu->subpicture_lock );
376

Sam Hocevar's avatar
   
Sam Hocevar committed
377
378
379
    /*
     * Look for an empty place
     */
yoann's avatar
yoann committed
380
    p_subpic = NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
381
382
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
383
        if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
Sam Hocevar's avatar
   
Sam Hocevar committed
384
385
        {
            /* Subpicture is empty and ready for allocation */
386
387
            p_subpic = &p_spu->p_subpicture[i_subpic];
            p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
388
            break;
Sam Hocevar's avatar
   
Sam Hocevar committed
389
390
391
        }
    }

392
393
    /* If no free subpicture could be found */
    if( p_subpic == NULL )
Sam Hocevar's avatar
   
Sam Hocevar committed
394
    {
395
396
        msg_Err( p_spu, "subpicture heap is full" );
        vlc_mutex_unlock( &p_spu->subpicture_lock );
397
        return NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
398
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
399

400
    /* Copy subpicture information, set some default values */
401
402
403
    memset( p_subpic, 0, sizeof(subpicture_t) );
    p_subpic->i_status   = RESERVED_SUBPICTURE;
    p_subpic->b_absolute = VLC_TRUE;
404
    p_subpic->b_fade     = VLC_FALSE;
405
406
    p_subpic->i_alpha    = 0xFF;
    p_subpic->p_region   = 0;
407
408
409
410
    p_subpic->pf_render  = 0;
    p_subpic->pf_destroy = 0;
    p_subpic->p_sys      = 0;
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
411

412
    p_subpic->pf_create_region = __spu_CreateRegion;
413
    p_subpic->pf_make_region = __spu_MakeRegion;
414
    p_subpic->pf_destroy_region = __spu_DestroyRegion;
415
    
416
    return p_subpic;
Sam Hocevar's avatar
   
Sam Hocevar committed
417
418
}

419
420
421
/**
 * Remove a subpicture from the heap
 *
Sam Hocevar's avatar
   
Sam Hocevar committed
422
423
424
 * 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
425
 * by the spu.
426
 */
427
void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
   
Sam Hocevar committed
428
{
gbazin's avatar
   
gbazin committed
429
    /* Get lock */
430
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
   
gbazin committed
431
432
433
434

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

439
440
441
442
    /* Check if status is valid */
    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
           && ( p_subpic->i_status != READY_SUBPICTURE ) )
    {
443
        msg_Err( p_spu, "subpicture %p has invalid status %d",
444
445
446
                         p_subpic, p_subpic->i_status );
    }

447
448
449
450
    while( p_subpic->p_region )
    {
        subpicture_region_t *p_region = p_subpic->p_region;
        p_subpic->p_region = p_region->p_next;
451
        spu_DestroyRegion( p_spu, p_region );
452
453
    }

454
455
456
457
458
459
    if( p_subpic->pf_destroy )
    {
        p_subpic->pf_destroy( p_subpic );
    }

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

461
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
462
463
464
}

/*****************************************************************************
465
 * spu_RenderSubpictures: render a subpicture list
Sam Hocevar's avatar
   
Sam Hocevar committed
466
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
467
 * This function renders all sub picture units in the list.
Sam Hocevar's avatar
   
Sam Hocevar committed
468
 *****************************************************************************/
469
470
471
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,
472
                            int i_scale_width_orig, int i_scale_height_orig )
Sam Hocevar's avatar
   
Sam Hocevar committed
473
{
gbazin's avatar
   
gbazin committed
474
    /* Get lock */
475
    vlc_mutex_lock( &p_spu->subpicture_lock );
gbazin's avatar
   
gbazin committed
476
477
478

    /* 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
479
    {
480
        subpicture_region_t *p_region = p_subpic->p_region;
481
        int i_scale_width, i_scale_height;
482
        int i_subpic_x = p_subpic->i_x;
483

484
        /* Load the blending module */
485
        if( !p_spu->p_blend && p_region )
486
        {
487
            p_spu->p_blend = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
488
489
490
491
492
493
494
495
496
            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 );
497
        }
498

499
        /* Load the text rendering module */
500
        if( !p_spu->p_text && p_region && p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
501
        {
502
503
            char *psz_modulename = NULL;

504
            p_spu->p_text = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
505
506
507
508
            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 =
509
                p_fmt->i_width;
510
511
            p_spu->p_text->fmt_out.video.i_height =
                p_spu->p_text->fmt_out.video.i_visible_height =
512
                p_fmt->i_height;
513

514
515
            p_spu->p_text->pf_sub_buffer_new = spu_new_buffer;
            p_spu->p_text->pf_sub_buffer_del = spu_del_buffer;
516
517
518
519
520
521
522
523
524
525
526
527
528

            psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
            if( psz_modulename && *psz_modulename )
            {
                p_spu->p_text->p_module =
                    module_Need( p_spu->p_text, "text renderer", psz_modulename, VLC_TRUE );
            }
            if( !p_spu->p_text->p_module )
            {
                p_spu->p_text->p_module =
                    module_Need( p_spu->p_text, "text renderer", 0, 0 );
            }
            if( psz_modulename ) free( psz_modulename );
529
        }
530
        if( p_spu->p_text )
531
        {
532
533
534
535
536
537
538
539
540
541
542
543
544
545
            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 =
546
                    p_fmt->i_width;
547
548
                p_spu->p_text->fmt_out.video.i_height =
                    p_spu->p_text->fmt_out.video.i_visible_height =
549
                    p_fmt->i_height;
550
            }
551
        }
552

553
554
555
        i_scale_width = i_scale_width_orig;
        i_scale_height = i_scale_height_orig;

hartman's avatar
hartman committed
556
557
        if( p_subpic->i_original_picture_height > 0 &&
            p_subpic->i_original_picture_width  > 0 )
558
559
        {
            i_scale_width = i_scale_width * p_fmt->i_width /
zorglub's avatar
zorglub committed
560
                             p_subpic->i_original_picture_width;
561
            i_scale_height = i_scale_height * p_fmt->i_height /
zorglub's avatar
zorglub committed
562
                             p_subpic->i_original_picture_height;
563
        }
hartman's avatar
hartman committed
564
565
566
567
568
569
        else if( p_subpic->i_original_picture_height > 0 )
        {
            i_scale_height = i_scale_height * p_fmt->i_height /
                             p_subpic->i_original_picture_height;
            i_scale_width = i_scale_height * i_scale_height / p_fmt->i_height; 
        }
570

571
572
573
        /* Set default subpicture aspect ratio */
        if( p_region && p_region->fmt.i_aspect &&
            (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) )
zorglub's avatar
zorglub committed
574
        {
575
576
577
578
579
580
581
582
583
            p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
            p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
        }
        if( p_region &&
            (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) )
        {
            p_region->fmt.i_sar_den = p_fmt->i_sar_den;
            p_region->fmt.i_sar_num = p_fmt->i_sar_num;
        }
584

585
        /* Take care of the aspect ratio */
586
        if( p_region && p_region->fmt.i_sar_num * p_fmt->i_sar_den !=
587
588
589
590
591
            p_region->fmt.i_sar_den * p_fmt->i_sar_num )
        {
            i_scale_width = i_scale_width *
                (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
                p_region->fmt.i_sar_den / p_fmt->i_sar_num;
592
            i_subpic_x = p_subpic->i_x * i_scale_width / 1000;
593
594
        }

595
        /* Load the scaling module */
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
        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 );
613
614
        }

615
        while( p_region && p_spu->p_blend && p_spu->p_blend->pf_video_blend )
616
        {
617
            int i_fade_alpha = 255;
618
            int i_x_offset = p_region->i_x + i_subpic_x;
619
620
            int i_y_offset = p_region->i_y + p_subpic->i_y;

621
622
            if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
            {
623
                if( p_spu->p_text && p_spu->p_text->p_module &&
624
                    p_spu->p_text->pf_render_text )
625
626
                {
                    p_region->i_align = p_subpic->i_flags;
627
628
                    p_spu->p_text->pf_render_text( p_spu->p_text,
                                                   p_region, p_region ); 
629
630
631
                }
            }

632
            /* Force palette if requested */
633
            if( p_spu->b_force_palette && VLC_FOURCC('Y','U','V','P') ==
634
                p_region->fmt.i_chroma )
635
            {
636
637
                memcpy( p_region->fmt.p_palette->palette,
                        p_spu->palette, 16 );
638
639
640
641
            }

            /* Scale SPU if necessary */
            if( p_region->p_cache )
642
            {
643
644
645
646
647
648
649
650
651
                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;
                }
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
            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;
682
683
                p_region->p_cache->i_x = p_region->i_x * i_scale_width / 1000;
                p_region->p_cache->i_y = p_region->i_y * i_scale_height / 1000;
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700

                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;
            }

701
702
703
704
705
706
707
708
709
710
711
712
713
            if( p_subpic->i_flags & SUBPICTURE_ALIGN_BOTTOM )
            {
                i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
                    p_subpic->i_y;
            }
            else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_TOP) )
            {
                i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
            }

            if( p_subpic->i_flags & SUBPICTURE_ALIGN_RIGHT )
            {
                i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
714
                    i_subpic_x;
715
716
717
718
719
720
            }
            else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_LEFT) )
            {
                i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
            }

721
722
            if( p_subpic->b_absolute )
            {
723
                i_x_offset = p_region->i_x +
724
                    i_subpic_x * i_scale_width / 1000;
725
726
727
                i_y_offset = p_region->i_y +
                    p_subpic->i_y * i_scale_height / 1000;

728
729
730
731
732
733
734
735
736
737
738
739
            }

            if( p_spu->i_margin != 0 && p_spu->b_force_crop == VLC_FALSE )
            {
                int i_diff = 0;
                int i_low = i_y_offset - p_spu->i_margin;
                int i_high = i_y_offset + p_region->fmt.i_height - p_spu->i_margin;

                /* crop extra margin to keep within bounds */
                if( i_low < 0 ) i_diff = i_low;
                if( i_high > (int)p_fmt->i_height ) i_diff = i_high - p_fmt->i_height;
                i_y_offset -= ( p_spu->i_margin + i_diff );
740
741
742
            }

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

744
745
746
747
            /* Force cropping if requested */
            if( p_spu->b_force_crop )
            {
                video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
748
749
750
751
                int i_crop_x = p_spu->i_crop_x * i_scale_width / 1000;
                int i_crop_y = p_spu->i_crop_y * i_scale_height / 1000;
                int i_crop_width = p_spu->i_crop_width * i_scale_width / 1000;
                int i_crop_height = p_spu->i_crop_height * i_scale_height/1000;
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

                /* 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;
                }
            }

782
            /* Update the output picture size */
783
784
785
786
787
788
789
            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;

790
791
792
793
794
795
796
797
798
799
800
801
802
803
            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 );
                }
            }

            p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
                p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
804
                i_fade_alpha * p_subpic->i_alpha / 255 );
805
806
807
808

            p_region = p_region->p_next;
        }

Sam Hocevar's avatar
   
Sam Hocevar committed
809
810
        p_subpic = p_subpic->p_next;
    }
gbazin's avatar
   
gbazin committed
811

812
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
813
814
815
}

/*****************************************************************************
816
 * spu_SortSubpictures: find the subpictures to display
Sam Hocevar's avatar
   
Sam Hocevar committed
817
818
819
820
821
822
823
824
825
 *****************************************************************************
 * 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.
 *****************************************************************************/
826
subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date )
Sam Hocevar's avatar
   
Sam Hocevar committed
827
{
gbazin's avatar
gbazin committed
828
829
830
831
    int i_index, i_channel;
    subpicture_t *p_subpic = NULL;
    subpicture_t *p_ephemer;
    mtime_t      ephemer_date;
Sam Hocevar's avatar
   
Sam Hocevar committed
832

833
834
835
836
837
838
839
840
841
842
843
844
    /* 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
845
846
    /* We get an easily parsable chained list of subpictures which
     * ends with NULL since p_subpic was initialized to NULL. */
gbazin's avatar
gbazin committed
847
    for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
848
    {
gbazin's avatar
gbazin committed
849
850
851
852
        p_ephemer = 0;
        ephemer_date = 0;

        for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
853
        {
gbazin's avatar
gbazin committed
854
855
            if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
                p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
Sam Hocevar's avatar
   
Sam Hocevar committed
856
            {
gbazin's avatar
gbazin committed
857
858
859
860
861
862
863
864
                continue;
            }
            if( display_date &&
                display_date < p_spu->p_subpicture[i_index].i_start )
            {
                /* Too early, come back next monday */
                continue;
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
865

gbazin's avatar
gbazin committed
866
867
            if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
                ephemer_date = p_spu->p_subpicture[i_index].i_start;
Sam Hocevar's avatar
   
Sam Hocevar committed
868

gbazin's avatar
gbazin committed
869
870
871
872
873
874
875
876
877
            if( display_date > p_spu->p_subpicture[i_index].i_stop &&
                ( !p_spu->p_subpicture[i_index].b_ephemer ||
                  p_spu->p_subpicture[i_index].i_stop >
                  p_spu->p_subpicture[i_index].i_start ) )
            {
                /* Too late, destroy the subpic */
                spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
                continue;
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
878

gbazin's avatar
gbazin committed
879
880
881
882
883
            /* If this is an ephemer subpic, add it to our list */
            if( p_spu->p_subpicture[i_index].b_ephemer )
            {
                p_spu->p_subpicture[i_index].p_next = p_ephemer;
                p_ephemer = &p_spu->p_subpicture[i_index];
Sam Hocevar's avatar
   
Sam Hocevar committed
884

gbazin's avatar
gbazin committed
885
886
                continue;
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
887

gbazin's avatar
gbazin committed
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
            p_spu->p_subpicture[i_index].p_next = p_subpic;
            p_subpic = &p_spu->p_subpicture[i_index];
        }

        /* If we found ephemer subpictures, check if they have to be
         * displayed or destroyed */
        while( p_ephemer != NULL )
        {
            subpicture_t *p_tmp = p_ephemer;
            p_ephemer = p_ephemer->p_next;

            if( p_tmp->i_start < ephemer_date )
            {
                /* Ephemer subpicture has lived too long */
                spu_DestroySubpicture( p_spu, p_tmp );
Sam Hocevar's avatar
   
Sam Hocevar committed
903
904
905
            }
            else
            {
gbazin's avatar
gbazin committed
906
907
908
                /* Ephemer subpicture can still live a bit */
                p_tmp->p_next = p_subpic;
                p_subpic = p_tmp;
Sam Hocevar's avatar
   
Sam Hocevar committed
909
910
911
912
913
914
915
            }
        }
    }

    return p_subpic;
}

916
/*****************************************************************************
917
 * SpuClearChannel: clear an spu channel
918
 *****************************************************************************
919
 * This function destroys the subpictures which belong to the spu channel
920
921
 * corresponding to i_channel_id.
 *****************************************************************************/
922
static void SpuClearChannel( spu_t *p_spu, int i_channel )
923
{
gbazin's avatar
gbazin committed
924
925
    int          i_subpic;                               /* subpicture index */
    subpicture_t *p_subpic = NULL;                  /* first free subpicture */
926

927
    vlc_mutex_lock( &p_spu->subpicture_lock );
928
929
930

    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
931
        p_subpic = &p_spu->p_subpicture[i_subpic];
932
933
934
935
936
937
        if( p_subpic->i_status == FREE_SUBPICTURE
            || ( p_subpic->i_status != RESERVED_SUBPICTURE
                 && p_subpic->i_status != READY_SUBPICTURE ) )
        {
            continue;
        }
938

939
940
        if( p_subpic->i_channel == i_channel )
        {
941
942
943
944
            while( p_subpic->p_region )
            {
                subpicture_region_t *p_region = p_subpic->p_region;
                p_subpic->p_region = p_region->p_next;
945
                spu_DestroyRegion( p_spu, p_region );
946
947
            }

948
            if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
949
950
951
952
            p_subpic->i_status = FREE_SUBPICTURE;
        }
    }

953
    vlc_mutex_unlock( &p_spu->subpicture_lock );
954
}
955

956
957
958
/*****************************************************************************
 * spu_ControlDefault: default methods for the subpicture unit control.
 *****************************************************************************/
gbazin's avatar
gbazin committed
959
static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
960
961
962
963
964
965
966
{
    int *pi, i;

    switch( i_query )
    {
    case SPU_CHANNEL_REGISTER:
        pi = (int *)va_arg( args, int * );
gbazin's avatar
gbazin committed
967
        if( pi ) *pi = p_spu->i_channel++;
968
        msg_Dbg( p_spu, "Registering subpicture channel, ID: %i",
gbazin's avatar
gbazin committed
969
                 p_spu->i_channel - 1 );
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
        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
 *****************************************************************************/

989
990
991
992
993
994
/*****************************************************************************
 * UpdateSPU: update subpicture settings
 *****************************************************************************
 * This function is called from CropCallback and at initialization time, to
 * retrieve crop information from the input.
 *****************************************************************************/
995
static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
996
997
998
{
    vlc_value_t val;

999
    p_spu->b_force_palette = VLC_FALSE;
1000
    p_spu->b_force_crop = VLC_FALSE;
1001
1002
1003

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

1004
    p_spu->b_force_crop = VLC_TRUE;
1005
    var_Get( p_object, "x-start", &val );
1006
    p_spu->i_crop_x = val.i_int;
1007
    var_Get( p_object, "y-start", &val );
1008
    p_spu->i_crop_y = val.i_int;
1009
    var_Get( p_object, "x-end", &val );
1010
    p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1011
    var_Get( p_object, "y-end", &val );
1012
    p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1013

1014
    if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1015
    {
1016
1017
        memcpy( p_spu->palette, val.p_address, 16 );
        p_spu->b_force_palette = VLC_TRUE;
1018
1019
    }

1020
    msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1021
             p_spu->i_crop_x, p_spu->i_crop_y,
1022
1023
             p_spu->i_crop_width, p_spu->i_crop_height,
             p_spu->b_force_palette );
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
}

/*****************************************************************************
 * 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 )
{
1034
    UpdateSPU( (spu_t *)p_data, p_object );
1035
1036
1037
1038
1039
1040
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Buffers allocation callbacks for the filters
 *****************************************************************************/
1041
1042
static subpicture_t *sub_new_buffer( filter_t *p_filter )
{
gbazin's avatar
gbazin committed
1043
1044
1045
1046
    filter_owner_sys_t *p_sys = p_filter->p_owner;
    subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
    if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
    return p_subpicture;
1047
1048
1049
1050
}

static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
{
gbazin's avatar
gbazin committed
1051
1052
    filter_owner_sys_t *p_sys = p_filter->p_owner;
    spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1053
1054
}

1055
1056
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
1057
1058
1059
1060
1061
    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;
1062
    p_subpic->pf_make_region = __spu_MakeRegion;
1063
    p_subpic->pf_destroy_region = __spu_DestroyRegion;
1064

1065
    return p_subpic;
1066
1067
}

1068
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1069
{
1070
1071
1072
1073
1074
1075
1076
1077
    while( p_subpic->p_region )
    {
        subpicture_region_t *p_region = p_subpic->p_region;
        p_subpic->p_region = p_region->p_next;
        p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
    }

    free( p_subpic );
1078
}
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104

static picture_t *spu_new_video_buffer( filter_t *p_filter )
{
    picture_t *p_picture = malloc( sizeof(picture_t) );

    if( vout_AllocatePicture( p_filter, p_picture,
                              p_filter->fmt_out.video.i_chroma,
                              p_filter->fmt_out.video.i_width,
                              p_filter->fmt_out.video.i_height,
                              p_filter->fmt_out.video.i_aspect )
        != VLC_SUCCESS )
    {
        free( p_picture );
        return NULL;
    }

    p_picture->pf_release = RegionPictureRelease;

    return p_picture;
}

static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
{
    if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
    if( p_pic ) free( p_pic );
}