]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Privatized SUBPICTURE_RENDERED.
[vlc] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_vout.h>
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
37 #include <vlc_osd.h>
38 #include "../libvlc.h"
39
40 #include <assert.h>
41 #include <limits.h>
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 #define VLC_FOURCC_YUVP VLC_FOURCC('Y','U','V','P')
47 #define VLC_FOURCC_YUVA VLC_FOURCC('Y','U','V','A')
48 #define VLC_FOURCC_RGBA VLC_FOURCC('R','G','B','A')
49 #define VLC_FOURCC_TEXT VLC_FOURCC('T','E','X','T')
50
51 typedef struct
52 {
53     subpicture_t *p_subpicture;
54     bool          b_reject;
55 } spu_heap_entry_t;
56
57 typedef struct
58 {
59     spu_heap_entry_t p_entry[VOUT_MAX_SUBPICTURES];
60
61 } spu_heap_t;
62
63 struct spu_private_t
64 {
65     vlc_mutex_t lock;   /* lock to protect all followings fields */
66
67     spu_heap_t heap;
68
69     int i_channel;             /**< number of subpicture channels registered */
70     int64_t i_subpicture_order; /**< number of created subpicture since spu creation */
71
72     filter_t *p_blend;                            /**< alpha blending module */
73     filter_t *p_text;                              /**< text renderer module */
74     filter_t *p_scale_yuvp;                     /**< scaling module for YUVP */
75     filter_t *p_scale;                    /**< scaling module (all but YUVP) */
76     bool b_force_crop;                     /**< force cropping of subpicture */
77     int i_crop_x, i_crop_y, i_crop_width, i_crop_height;       /**< cropping */
78
79     int i_margin;                        /**< force position of a subpicture */
80     bool b_force_palette;             /**< force palette of subpicture */
81     uint8_t palette[4][4];                               /**< forced palette */
82
83     /* Supciture filters */
84     filter_chain_t *p_chain;
85 };
86
87 /* */
88 struct subpicture_region_private_t
89 {
90     video_format_t fmt;
91     picture_t      *p_picture;
92 };
93
94 /* Subpicture rendered flag
95  * FIXME ? it could be moved to private ? */
96 #define SUBPICTURE_RENDERED  (0x1000)
97 #ifdef SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
98 #   error SUBPICTURE_RENDERED too low
99 #endif
100
101 static void SpuRegionPrivateDestroy( subpicture_region_private_t *p_private );
102
103 static void UpdateSPU   ( spu_t *, vlc_object_t * );
104 static int  CropCallback( vlc_object_t *, char const *,
105                           vlc_value_t, vlc_value_t, void * );
106
107 static int SpuControl( spu_t *, int, va_list );
108
109 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked );
110
111 /* Buffer allocation for SPU filter (blend, scale, ...) */
112 static subpicture_t *spu_new_buffer( filter_t * );
113 static void spu_del_buffer( filter_t *, subpicture_t * );
114 static picture_t *spu_new_video_buffer( filter_t * );
115 static void spu_del_video_buffer( filter_t *, picture_t * );
116
117 static int spu_ParseChain( spu_t * );
118
119 /* Buffer aloccation fir SUB filter */
120 static int SubFilterCallback( vlc_object_t *, char const *,
121                               vlc_value_t, vlc_value_t, void * );
122
123 static int SubFilterAllocationInit( filter_t *, void * );
124 static void SubFilterAllocationClean( filter_t * );
125
126 #define SCALE_UNIT (1000)
127
128 /* */
129 subpicture_region_t *subpicture_region_New( const video_format_t *p_fmt )
130 {
131     subpicture_region_t *p_region = calloc( 1, sizeof(*p_region ) );
132     if( !p_region )
133         return NULL;
134
135     p_region->fmt = *p_fmt;
136     p_region->fmt.p_palette = NULL;
137     if( p_fmt->i_chroma == VLC_FOURCC_YUVP )
138     {
139         p_region->fmt.p_palette = calloc( 1, sizeof(*p_region->fmt.p_palette) );
140         if( p_fmt->p_palette )
141             *p_region->fmt.p_palette = *p_fmt->p_palette;
142     }
143     p_region->i_alpha = 0xff;
144     p_region->p_next = NULL;
145     p_region->p_private = NULL;
146     p_region->psz_text = NULL;
147     p_region->p_style = NULL;
148     p_region->p_picture = NULL;
149
150     if( p_fmt->i_chroma == VLC_FOURCC_TEXT )
151         return p_region;
152
153     p_region->p_picture = picture_New( p_fmt->i_chroma, p_fmt->i_width, p_fmt->i_height,
154                                        p_fmt->i_aspect );
155     if( !p_region->p_picture )
156     {
157         free( p_fmt->p_palette );
158         free( p_region );
159         return NULL;
160     }
161
162     return p_region;
163 }
164
165 /* */
166 void subpicture_region_Delete( subpicture_region_t *p_region )
167 {
168     if( !p_region )
169         return;
170
171     if( p_region->p_private )
172         SpuRegionPrivateDestroy( p_region->p_private );
173
174     if( p_region->p_picture )
175         picture_Release( p_region->p_picture );
176
177     free( p_region->fmt.p_palette );
178
179     free( p_region->psz_text );
180     free( p_region->psz_html );
181     //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
182     free( p_region );
183 }
184
185 /* */
186 void subpicture_region_ChainDelete( subpicture_region_t *p_head )
187 {
188     while( p_head )
189     {
190         subpicture_region_t *p_next = p_head->p_next;
191
192         subpicture_region_Delete( p_head );
193
194         p_head = p_next;
195     }
196 }
197
198 /**
199  * This function create a new empty subpicture.
200  */
201 static subpicture_t *subpicture_New( void )
202 {
203     subpicture_t *p_subpic = calloc( 1, sizeof(*p_subpic) );
204     if( !p_subpic )
205         return NULL;
206
207     p_subpic->i_order    = 0;
208     p_subpic->b_absolute = true;
209     p_subpic->b_fade     = false;
210     p_subpic->b_subtitle = false;
211     p_subpic->i_alpha    = 0xFF;
212     p_subpic->p_region   = NULL;
213     p_subpic->pf_render  = NULL;
214     p_subpic->pf_destroy = NULL;
215     p_subpic->p_sys      = NULL;
216
217     return p_subpic;
218 }
219
220 static void subpicture_Delete( subpicture_t *p_subpic )
221 {
222     subpicture_region_ChainDelete( p_subpic->p_region );
223     p_subpic->p_region = NULL;
224
225     if( p_subpic->pf_destroy )
226     {
227         p_subpic->pf_destroy( p_subpic );
228     }
229     free( p_subpic );
230 }
231
232
233 static void SpuHeapInit( spu_heap_t *p_heap )
234 {
235     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
236     {
237         spu_heap_entry_t *e = &p_heap->p_entry[i];
238
239         e->p_subpicture = NULL;
240         e->b_reject = false;
241     }
242 }
243
244 static int SpuHeapPush( spu_heap_t *p_heap, subpicture_t *p_subpic )
245 {
246     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
247     {
248         spu_heap_entry_t *e = &p_heap->p_entry[i];
249
250         if( e->p_subpicture )
251             continue;
252
253         e->p_subpicture = p_subpic;
254         e->b_reject = false;
255         return VLC_SUCCESS;
256     }
257     return VLC_EGENERIC;
258 }
259
260 static void SpuHeapDeleteAt( spu_heap_t *p_heap, int i_index )
261 {
262     spu_heap_entry_t *e = &p_heap->p_entry[i_index];
263
264     if( e->p_subpicture )
265         subpicture_Delete( e->p_subpicture );
266
267     e->p_subpicture = NULL;
268 }
269
270 static int SpuHeapDeleteSubpicture( spu_heap_t *p_heap, subpicture_t *p_subpic )
271 {
272     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
273     {
274         spu_heap_entry_t *e = &p_heap->p_entry[i];
275
276         if( e->p_subpicture != p_subpic )
277             continue;
278
279         SpuHeapDeleteAt( p_heap, i );
280         return VLC_SUCCESS;
281     }
282     return VLC_EGENERIC;
283 }
284
285 static void SpuHeapClean( spu_heap_t *p_heap )
286 {
287     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
288     {
289         spu_heap_entry_t *e = &p_heap->p_entry[i];
290         if( e->p_subpicture )
291             subpicture_Delete( e->p_subpicture );
292     }
293 }
294 static subpicture_region_private_t *SpuRegionPrivateCreate( video_format_t *p_fmt )
295 {
296     subpicture_region_private_t *p_private = malloc( sizeof(*p_private) );
297
298     if( !p_private )
299         return NULL;
300
301     p_private->fmt = *p_fmt;
302     if( p_fmt->p_palette )
303     {
304         p_private->fmt.p_palette = malloc( sizeof(*p_private->fmt.p_palette) );
305         if( p_private->fmt.p_palette )
306             *p_private->fmt.p_palette = *p_fmt->p_palette;
307     }
308     p_private->p_picture = NULL;
309
310     return p_private;
311 }
312 static void SpuRegionPrivateDestroy( subpicture_region_private_t *p_private )
313 {
314     if( p_private->p_picture )
315         picture_Release( p_private->p_picture );
316     free( p_private->fmt.p_palette );
317     free( p_private );
318 }
319
320 /* */
321 static void SpuRenderCreateAndLoadText( spu_t *p_spu );
322 static void SpuRenderCreateAndLoadScale( spu_t *p_spu );
323 static void FilterRelease( filter_t *p_filter );
324
325 /**
326  * Creates the subpicture unit
327  *
328  * \param p_this the parent object which creates the subpicture unit
329  */
330 spu_t *__spu_Create( vlc_object_t *p_this )
331 {
332     spu_t *p_spu;
333     spu_private_t *p_sys;
334     
335     p_spu = vlc_custom_create( p_this, sizeof(spu_t) + sizeof(spu_private_t),
336                                VLC_OBJECT_GENERIC, "subpicture" );
337
338     if( !p_spu )
339         return NULL;
340
341     /* Initialize spu fields */
342     p_spu->pf_control = SpuControl;
343     p_spu->p = p_sys = (spu_private_t*)&p_spu[1];
344
345     /* Initialize private fields */
346     vlc_mutex_init( &p_sys->lock );
347
348     SpuHeapInit( &p_sys->heap );
349
350     p_sys->i_subpicture_order = 1;
351
352     p_sys->p_blend = NULL;
353     p_sys->p_text = NULL;
354     p_sys->p_scale = NULL;
355     p_sys->p_scale_yuvp = NULL;
356
357     /* Register the default subpicture channel */
358     p_sys->i_channel = 2;
359
360     vlc_object_attach( p_spu, p_this );
361
362     p_sys->p_chain = filter_chain_New( p_spu, "sub filter", false,
363                                        SubFilterAllocationInit,
364                                        SubFilterAllocationClean,
365                                        p_spu );
366
367     /* Load text and scale module */
368     SpuRenderCreateAndLoadText( p_spu );
369     SpuRenderCreateAndLoadScale( p_spu );
370
371     return p_spu;
372 }
373
374 /**
375  * Initialise the subpicture unit
376  *
377  * \param p_spu the subpicture unit object
378  */
379 int spu_Init( spu_t *p_spu )
380 {
381     spu_private_t *p_sys = p_spu->p;
382
383     /* If the user requested a sub margin, we force the position. */
384     p_sys->i_margin = var_CreateGetInteger( p_spu, "sub-margin" );
385
386     var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
387     var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
388
389     spu_ParseChain( p_spu );
390
391     return VLC_SUCCESS;
392 }
393
394 int spu_ParseChain( spu_t *p_spu )
395 {
396     char *psz_parser = var_GetString( p_spu, "sub-filter" );
397     int i_ret;
398
399     if( !psz_parser )
400         return VLC_EGENERIC;
401
402     i_ret = filter_chain_AppendFromString( p_spu->p->p_chain, psz_parser );
403
404     free( psz_parser );
405
406     return i_ret;
407 }
408
409 /**
410  * Destroy the subpicture unit
411  *
412  * \param p_this the parent object which destroys the subpicture unit
413  */
414 void spu_Destroy( spu_t *p_spu )
415 {
416     spu_private_t *p_sys = p_spu->p;
417
418     if( p_sys->p_blend )
419         FilterRelease( p_sys->p_blend );
420
421     if( p_sys->p_text )
422         FilterRelease( p_sys->p_text );
423
424     if( p_sys->p_scale_yuvp )
425         FilterRelease( p_sys->p_scale_yuvp );
426
427     if( p_sys->p_scale )
428         FilterRelease( p_sys->p_scale );
429
430     filter_chain_Delete( p_sys->p_chain );
431
432     /* Destroy all remaining subpictures */
433     SpuHeapClean( &p_sys->heap );
434
435     vlc_mutex_destroy( &p_sys->lock );
436
437     vlc_object_release( p_spu );
438 }
439
440 /**
441  * Attach/Detach the SPU from any input
442  *
443  * \param p_this the object in which to destroy the subpicture unit
444  * \param b_attach to select attach or detach
445  */
446 void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
447 {
448     vlc_object_t *p_input;
449
450     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
451     if( !p_input ) return;
452
453     if( b_attach )
454     {
455         UpdateSPU( p_spu, VLC_OBJECT(p_input) );
456         var_AddCallback( p_input, "highlight", CropCallback, p_spu );
457         vlc_object_release( p_input );
458     }
459     else
460     {
461         /* Delete callback */
462         var_DelCallback( p_input, "highlight", CropCallback, p_spu );
463         vlc_object_release( p_input );
464     }
465 }
466
467 /**
468  * Display a subpicture
469  *
470  * Remove the reservation flag of a subpicture, which will cause it to be
471  * ready for display.
472  * \param p_spu the subpicture unit object
473  * \param p_subpic the subpicture to display
474  */
475 void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
476 {
477     spu_private_t *p_sys = p_spu->p;
478
479     /* DEFAULT_CHAN always reset itself */
480     if( p_subpic->i_channel == DEFAULT_CHAN )
481         SpuClearChannel( p_spu, DEFAULT_CHAN, false );
482
483     /* p_private is for spu only and cannot be non NULL here */
484     for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
485         assert( r->p_private == NULL );
486
487     /* */
488     vlc_mutex_lock( &p_sys->lock );
489     if( SpuHeapPush( &p_sys->heap, p_subpic ) )
490     {
491         vlc_mutex_unlock( &p_sys->lock );
492         msg_Err( p_spu, "subpicture heap full" );
493         subpicture_Delete( p_subpic );
494         return;
495     }
496     vlc_mutex_unlock( &p_sys->lock );
497 }
498
499 /**
500  * Allocate a subpicture in the spu heap.
501  *
502  * This function create a reserved subpicture in the spu heap.
503  * A null pointer is returned if the function fails. This method provides an
504  * already allocated zone of memory in the spu data fields. It needs locking
505  * since several pictures can be created by several producers threads.
506  * \param p_spu the subpicture unit in which to create the subpicture
507  * \return NULL on error, a reserved subpicture otherwise
508  */
509 subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
510 {
511     VLC_UNUSED(p_spu);
512
513     return subpicture_New();
514 }
515
516 /**
517  * Remove a subpicture from the heap
518  *
519  * This function frees a previously reserved subpicture.
520  * It is meant to be used when the construction of a picture aborted.
521  * This function does not need locking since reserved subpictures are ignored
522  * by the spu.
523  */
524 void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
525 {
526     VLC_UNUSED(p_spu);
527
528     subpicture_Delete( p_subpic );
529 }
530
531 static void FilterRelease( filter_t *p_filter )
532 {
533     if( p_filter->p_module )
534         module_unneed( p_filter, p_filter->p_module );
535
536     vlc_object_detach( p_filter );
537     vlc_object_release( p_filter );
538 }
539
540 static void SpuRenderCreateBlend( spu_t *p_spu, vlc_fourcc_t i_chroma, int i_aspect )
541 {
542     filter_t *p_blend;
543
544     assert( !p_spu->p->p_blend );
545
546     p_spu->p->p_blend =
547     p_blend        = vlc_custom_create( p_spu, sizeof(filter_t),
548                                         VLC_OBJECT_GENERIC, "blend" );
549     if( !p_blend )
550         return;
551
552     es_format_Init( &p_blend->fmt_in, VIDEO_ES, 0 );
553
554     es_format_Init( &p_blend->fmt_out, VIDEO_ES, 0 );
555     p_blend->fmt_out.video.i_x_offset = 0;
556     p_blend->fmt_out.video.i_y_offset = 0;
557     p_blend->fmt_out.video.i_chroma = i_chroma;
558     p_blend->fmt_out.video.i_aspect = i_aspect;
559
560     /* The blend module will be loaded when needed with the real
561     * input format */
562     p_blend->p_module = NULL;
563
564     /* */
565     vlc_object_attach( p_blend, p_spu );
566 }
567 static void SpuRenderUpdateBlend( spu_t *p_spu, int i_out_width, int i_out_height,
568                                   const video_format_t *p_in_fmt )
569 {
570     filter_t *p_blend = p_spu->p->p_blend;
571
572     assert( p_blend );
573
574     /* */
575     if( p_blend->p_module && p_blend->fmt_in.video.i_chroma != p_in_fmt->i_chroma )
576     {
577         /* The chroma is not the same, we need to reload the blend module
578          * XXX to match the old behaviour just test !p_blend->fmt_in.video.i_chroma */
579         module_unneed( p_blend, p_blend->p_module );
580         p_blend->p_module = NULL;
581     }
582
583     /* */
584     p_blend->fmt_in.video = *p_in_fmt;
585
586     /* */
587     p_blend->fmt_out.video.i_width =
588     p_blend->fmt_out.video.i_visible_width = i_out_width;
589     p_blend->fmt_out.video.i_height =
590     p_blend->fmt_out.video.i_visible_height = i_out_height;
591
592     /* */
593     if( !p_blend->p_module )
594         p_blend->p_module = module_need( p_blend, "video blending", 0, 0 );
595 }
596 static void SpuRenderCreateAndLoadText( spu_t *p_spu )
597 {
598     filter_t *p_text;
599
600     assert( !p_spu->p->p_text );
601
602     p_spu->p->p_text =
603     p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
604                                        VLC_OBJECT_GENERIC, "spu text" );
605     if( !p_text )
606         return;
607
608     es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
609
610     es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
611     p_text->fmt_out.video.i_width =
612     p_text->fmt_out.video.i_visible_width = 32;
613     p_text->fmt_out.video.i_height =
614     p_text->fmt_out.video.i_visible_height = 32;
615
616     p_text->pf_sub_buffer_new = spu_new_buffer;
617     p_text->pf_sub_buffer_del = spu_del_buffer;
618
619     vlc_object_attach( p_text, p_spu );
620
621     /* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
622      * same than these 2 calls ? */
623     char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
624     if( psz_modulename && *psz_modulename )
625     {
626         p_text->p_module = module_need( p_text, "text renderer",
627                                         psz_modulename, true );
628     }
629     free( psz_modulename );
630
631     if( !p_text->p_module )
632         p_text->p_module = module_need( p_text, "text renderer", NULL, false );
633
634     /* Create a few variables used for enhanced text rendering */
635     var_Create( p_text, "spu-duration", VLC_VAR_TIME );
636     var_Create( p_text, "spu-elapsed", VLC_VAR_TIME );
637     var_Create( p_text, "text-rerender", VLC_VAR_BOOL );
638     var_Create( p_text, "scale", VLC_VAR_INTEGER );
639 }
640
641 static filter_t *CreateAndLoadScale( vlc_object_t *p_obj,
642                                      vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
643                                      bool b_resize )
644 {
645     filter_t *p_scale;
646
647     p_scale = vlc_custom_create( p_obj, sizeof(filter_t),
648                                  VLC_OBJECT_GENERIC, "scale" );
649     if( !p_scale )
650         return NULL;
651
652     es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
653     p_scale->fmt_in.video.i_chroma = i_src_chroma;
654     p_scale->fmt_in.video.i_width =
655     p_scale->fmt_in.video.i_height = 32;
656
657     es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
658     p_scale->fmt_out.video.i_chroma = i_dst_chroma;
659     p_scale->fmt_out.video.i_width =
660     p_scale->fmt_out.video.i_height = b_resize ? 16 : 32;
661
662     p_scale->pf_vout_buffer_new = spu_new_video_buffer;
663     p_scale->pf_vout_buffer_del = spu_del_video_buffer;
664
665     vlc_object_attach( p_scale, p_obj );
666     p_scale->p_module = module_need( p_scale, "video filter2", 0, 0 );
667
668     return p_scale;
669 }
670 static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
671 {
672     assert( !p_spu->p->p_scale );
673     assert( !p_spu->p->p_scale_yuvp );
674     /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
675      * yuva/rgba */
676     p_spu->p->p_scale = CreateAndLoadScale( VLC_OBJECT(p_spu),
677                                             VLC_FOURCC_YUVA, VLC_FOURCC_YUVA, true );
678     /* This one is used for YUVP to YUVA/RGBA without scaling
679      * FIXME rename it */
680     p_spu->p->p_scale_yuvp = CreateAndLoadScale( VLC_OBJECT(p_spu),
681                                                  VLC_FOURCC_YUVP, VLC_FOURCC_YUVA, false );
682 }
683
684 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
685                            subpicture_t *p_subpic, subpicture_region_t *p_region,
686                            int i_min_scale_ratio )
687 {
688     filter_t *p_text = p_spu->p->p_text;
689
690     assert( p_region->fmt.i_chroma == VLC_FOURCC_TEXT );
691
692     if( !p_text || !p_text->p_module )
693         goto exit;
694
695     /* Setup 3 variables which can be used to render
696      * time-dependent text (and effects). The first indicates
697      * the total amount of time the text will be on screen,
698      * the second the amount of time it has already been on
699      * screen (can be a negative value as text is layed out
700      * before it is rendered) and the third is a feedback
701      * variable from the renderer - if the renderer sets it
702      * then this particular text is time-dependent, eg. the
703      * visual progress bar inside the text in karaoke and the
704      * text needs to be rendered multiple times in order for
705      * the effect to work - we therefore need to return the
706      * region to its original state at the end of the loop,
707      * instead of leaving it in YUVA or YUVP.
708      * Any renderer which is unaware of how to render
709      * time-dependent text can happily ignore the variables
710      * and render the text the same as usual - it should at
711      * least show up on screen, but the effect won't change
712      * the text over time.
713      */
714     var_SetTime( p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
715     var_SetTime( p_text, "spu-elapsed", mdate() - p_subpic->i_start );
716     var_SetBool( p_text, "text-rerender", false );
717     var_SetInteger( p_text, "scale", i_min_scale_ratio );
718
719     if( p_text->pf_render_html && p_region->psz_html )
720     {
721         p_text->pf_render_html( p_text, p_region, p_region );
722     }
723     else if( p_text->pf_render_text )
724     {
725         p_text->pf_render_text( p_text, p_region, p_region );
726     }
727     *pb_rerender_text = var_GetBool( p_text, "text-rerender" );
728
729 exit:
730     p_region->i_align |= SUBPICTURE_RENDERED;
731 }
732
733 /**
734  * A few scale functions helpers.
735  */
736 typedef struct
737 {
738     int w;
739     int h;
740 } spu_scale_t;
741
742 static spu_scale_t spu_scale_create( int w, int h )
743 {
744     spu_scale_t s = { .w = w, .h = h };
745     if( s.w <= 0 )
746         s.w = SCALE_UNIT;
747     if( s.h <= 0 )
748         s.h = SCALE_UNIT;
749     return s;
750 }
751 static spu_scale_t spu_scale_unit(void )
752 {
753     return spu_scale_create( SCALE_UNIT, SCALE_UNIT );
754 }
755 static spu_scale_t spu_scale_createq( int wn, int wd, int hn, int hd )
756 {
757     return spu_scale_create( wn * SCALE_UNIT / wd,
758                              hn * SCALE_UNIT / hd );
759 }
760 static int spu_scale_w( int v, const spu_scale_t s )
761 {
762     return v * s.w / SCALE_UNIT;
763 }
764 static int spu_scale_h( int v, const spu_scale_t s )
765 {
766     return v * s.h / SCALE_UNIT;
767 }
768 static int spu_invscale_w( int v, const spu_scale_t s )
769 {
770     return v * SCALE_UNIT / s.w;
771 }
772 static int spu_invscale_h( int v, const spu_scale_t s )
773 {
774     return v * SCALE_UNIT / s.h;
775 }
776
777 /**
778  * A few area functions helpers
779  */
780
781 typedef struct
782 {
783     int i_x;
784     int i_y;
785     int i_width;
786     int i_height;
787
788     spu_scale_t scale;
789 } spu_area_t;
790
791 static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t s )
792 {
793     spu_area_t a = { .i_x = x, .i_y = y, .i_width = w, .i_height = h, .scale = s };
794     return a;
795 }
796 static spu_area_t spu_area_scaled( spu_area_t a )
797 {
798     if( a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT )
799         return a;
800
801     a.i_x = spu_scale_w( a.i_x, a.scale );
802     a.i_y = spu_scale_h( a.i_y, a.scale );
803
804     a.i_width  = spu_scale_w( a.i_width,  a.scale );
805     a.i_height = spu_scale_h( a.i_height, a.scale );
806
807     a.scale = spu_scale_unit();
808     return a;
809 }
810 static spu_area_t spu_area_unscaled( spu_area_t a, spu_scale_t s )
811 {
812     if( a.scale.w == s.w && a.scale.h == s.h )
813         return a;
814
815     a = spu_area_scaled( a );
816
817     a.i_x = spu_invscale_w( a.i_x, s );
818     a.i_y = spu_invscale_h( a.i_y, s );
819
820     a.i_width  = spu_invscale_w( a.i_width, s );
821     a.i_height = spu_invscale_h( a.i_height, s );
822
823     a.scale = s;
824     return a;
825 }
826 static bool spu_area_overlap( spu_area_t a, spu_area_t b )
827 {
828     const int i_dx = 0;
829     const int i_dy = 0;
830
831     a = spu_area_scaled( a );
832     b = spu_area_scaled( b );
833
834     return  __MAX( a.i_x-i_dx, b.i_x ) < __MIN( a.i_x+a.i_width +i_dx, b.i_x+b.i_width  ) &&
835             __MAX( a.i_y-i_dy, b.i_y ) < __MIN( a.i_y+a.i_height+i_dy, b.i_y+b.i_height );
836 }
837
838 /**
839  * Avoid area overlapping
840  */
841 static void SpuAreaFixOverlap( spu_area_t *p_dst,
842                                const spu_area_t *p_master,
843                                const spu_area_t *p_sub, int i_sub, int i_align )
844 {
845     spu_area_t a = spu_area_scaled( *p_dst );
846     bool b_moved = false;
847     bool b_ok;
848
849     assert( p_master->i_x == 0 && p_master->i_y == 0 );
850
851     /* Check for overlap
852      * XXX It is not fast O(n^2) but we should not have a lot of region */
853     do
854     {
855         b_ok = true;
856         for( int i = 0; i < i_sub; i++ )
857         {
858             spu_area_t sub = spu_area_scaled( p_sub[i] );
859
860             if( !spu_area_overlap( a, sub ) )
861                 continue;
862
863             if( i_align & SUBPICTURE_ALIGN_TOP )
864             {
865                 /* We go down */
866                 int i_y = sub.i_y + sub.i_height;
867                 if( i_y + a.i_height > p_master->i_height )
868                     break;
869                 a.i_y = i_y;
870                 b_moved = true;
871             }
872             else if( i_align & SUBPICTURE_ALIGN_BOTTOM )
873             {
874                 /* We go up */
875                 int i_y = sub.i_y - a.i_height;
876                 if( i_y < 0 )
877                     break;
878                 a.i_y = i_y;
879                 b_moved = true;
880             }
881             else
882             {
883                 /* TODO what to do in this case? */
884                 //fprintf( stderr, "Overlap with unsupported alignment\n" );
885                 break;
886             }
887
888             b_ok = false;
889             break;
890         }
891     } while( !b_ok );
892
893     if( b_moved )
894         *p_dst = spu_area_unscaled( a, p_dst->scale );
895 }
896
897
898 /**
899  * Place a region
900  */
901 static void SpuRegionPlace( int *pi_x, int *pi_y,
902                             const subpicture_t *p_subpic,
903                             const subpicture_region_t *p_region,
904                             int i_margin_y )
905 {
906     const int i_delta_x = p_region->i_x;
907     const int i_delta_y = p_region->i_y;
908     int i_x, i_y;
909
910     assert( p_region->i_x != INT_MAX && p_region->i_y != INT_MAX );
911     if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
912     {
913         i_y = i_delta_y;
914     }
915     else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
916     {
917         i_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - i_delta_y;
918     }
919     else
920     {
921         i_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
922     }
923
924     if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
925     {
926         i_x = i_delta_x;
927     }
928     else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
929     {
930         i_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - i_delta_x;
931     }
932     else
933     {
934         i_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
935     }
936
937     if( p_subpic->b_absolute )
938     {
939         i_x = i_delta_x;
940         i_y = i_delta_y;
941     }
942
943     /* Margin shifts all subpictures */
944     if( i_margin_y != 0 )
945         i_y -= i_margin_y;
946
947     /* Clamp offset to not go out of the screen (when possible) */
948     const int i_error_x = (i_x + p_region->fmt.i_width) - p_subpic->i_original_picture_width;
949     if( i_error_x > 0 )
950         i_x -= i_error_x;
951     if( i_x < 0 )
952         i_x = 0;
953
954     const int i_error_y = (i_y + p_region->fmt.i_height) - p_subpic->i_original_picture_height;
955     if( i_error_y > 0 )
956         i_y -= i_error_y;
957     if( i_y < 0 )
958         i_y = 0;
959
960     *pi_x = i_x;
961     *pi_y = i_y;
962 }
963
964 /**
965  * This function computes the current alpha value for a given region.
966  */
967 static int SpuRegionAlpha( subpicture_t *p_subpic, subpicture_region_t *p_region )
968 {
969     /* Compute alpha blend value */
970     int i_fade_alpha = 255;
971     if( p_subpic->b_fade )
972     {
973         mtime_t i_fade_start = ( p_subpic->i_stop +
974                                  p_subpic->i_start ) / 2;
975         mtime_t i_now = mdate();
976
977         if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
978         {
979             i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
980                            ( p_subpic->i_stop - i_fade_start );
981         }
982     }
983     return i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025;
984 }
985
986 /**
987  * It will render the provided region onto p_pic_dst.
988  */
989
990 static void SpuRenderRegion( spu_t *p_spu,
991                              picture_t *p_pic_dst, spu_area_t *p_area,
992                              subpicture_t *p_subpic, subpicture_region_t *p_region,
993                              const spu_scale_t scale_size,
994                              const video_format_t *p_fmt,
995                              const spu_area_t *p_subtitle_area, int i_subtitle_area )
996 {
997     spu_private_t *p_sys = p_spu->p;
998
999     video_format_t fmt_original = p_region->fmt;
1000     bool b_rerender_text = false;
1001     bool b_restore_format = false;
1002     int i_x_offset;
1003     int i_y_offset;
1004
1005     video_format_t region_fmt;
1006     picture_t *p_region_picture;
1007
1008     /* Invalidate area by default */
1009     *p_area = spu_area_create( 0,0, 0,0, scale_size );
1010
1011     /* Render text region */
1012     if( p_region->fmt.i_chroma == VLC_FOURCC_TEXT )
1013     {
1014         const int i_min_scale_ratio = SCALE_UNIT; /* FIXME what is the right value? (scale_size is not) */
1015         SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region, i_min_scale_ratio );
1016         b_restore_format = b_rerender_text;
1017
1018         /* Check if the rendering has failed ... */
1019         if( p_region->fmt.i_chroma == VLC_FOURCC_TEXT )
1020             goto exit;
1021     }
1022
1023     /* Force palette if requested
1024      * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
1025      * instead of only the right one (being the dvd spu).
1026      */
1027     const bool b_using_palette = p_region->fmt.i_chroma == VLC_FOURCC_YUVP;
1028     const bool b_force_palette = b_using_palette && p_sys->b_force_palette;
1029     const bool b_force_crop    = b_force_palette && p_sys->b_force_crop;
1030     bool b_changed_palette     = false;
1031
1032
1033     /* Compute the margin which is expressed in destination pixel unit
1034      * The margin is applied only to subtitle and when no forced crop is
1035      * requested (dvd menu) */
1036     int i_margin_y = 0;
1037     if( !b_force_crop && p_subpic->b_subtitle )
1038         i_margin_y = spu_invscale_h( p_sys->i_margin, scale_size );
1039
1040     /* Place the picture
1041      * We compute the position in the rendered size */
1042     SpuRegionPlace( &i_x_offset, &i_y_offset,
1043                     p_subpic, p_region, i_margin_y );
1044
1045     /* Save this position for subtitle overlap support
1046      * it is really important that there are given without scale_size applied */
1047     *p_area = spu_area_create( i_x_offset, i_y_offset,
1048                                p_region->fmt.i_width, p_region->fmt.i_height,
1049                                scale_size );
1050
1051     /* Handle overlapping subtitles when possible */
1052     if( p_subpic->b_subtitle && !p_subpic->b_absolute )
1053     {
1054         spu_area_t display = spu_area_create( 0, 0, p_fmt->i_width, p_fmt->i_height,
1055                                               spu_scale_unit() );
1056
1057         SpuAreaFixOverlap( p_area, &display, p_subtitle_area, i_subtitle_area,
1058                            p_region->i_align );
1059     }
1060
1061     /* Fix the position for the current scale_size */
1062     i_x_offset = spu_scale_w( p_area->i_x, p_area->scale );
1063     i_y_offset = spu_scale_h( p_area->i_y, p_area->scale );
1064
1065     /* */
1066     if( b_force_palette )
1067     {
1068         video_palette_t *p_palette = p_region->fmt.p_palette;
1069         video_palette_t palette;
1070
1071         /* We suppose DVD palette here */
1072         palette.i_entries = 4;
1073         for( int i = 0; i < 4; i++ )
1074             for( int j = 0; j < 4; j++ )
1075                 palette.palette[i][j] = p_sys->palette[i][j];
1076
1077         if( p_palette->i_entries == palette.i_entries )
1078         {
1079             for( int i = 0; i < p_palette->i_entries; i++ )
1080                 for( int j = 0; j < 4; j++ )
1081                     b_changed_palette |= p_palette->palette[i][j] != palette.palette[i][j];
1082         }
1083         else
1084         {
1085             b_changed_palette = true;
1086         }
1087         *p_palette = palette;
1088     }
1089
1090     /* */
1091     region_fmt = p_region->fmt;
1092     p_region_picture = p_region->p_picture;
1093
1094
1095     /* Scale from rendered size to destination size */
1096     if( p_sys->p_scale && p_sys->p_scale->p_module &&
1097         ( !b_using_palette || ( p_sys->p_scale_yuvp && p_sys->p_scale_yuvp->p_module ) ) &&
1098         ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT || b_using_palette ) )
1099     {
1100         const unsigned i_dst_width  = spu_scale_w( p_region->fmt.i_width, scale_size );
1101         const unsigned i_dst_height = spu_scale_h( p_region->fmt.i_height, scale_size );
1102
1103         /* Destroy the cache if unusable */
1104         if( p_region->p_private )
1105         {
1106             subpicture_region_private_t *p_private = p_region->p_private;
1107             bool b_changed = false;
1108
1109             /* Check resize changes */
1110             if( i_dst_width  != p_private->fmt.i_width ||
1111                 i_dst_height != p_private->fmt.i_height )
1112                 b_changed = true;
1113
1114             /* Check forced palette changes */
1115             if( b_changed_palette )
1116                 b_changed = true;
1117
1118             if( b_changed )
1119             {
1120                 SpuRegionPrivateDestroy( p_private );
1121                 p_region->p_private = NULL;
1122             }
1123         }
1124
1125         /* Scale if needed into cache */
1126         if( !p_region->p_private )
1127         {
1128             filter_t *p_scale = p_sys->p_scale;
1129
1130             picture_t *p_picture = p_region->p_picture;
1131             picture_Hold( p_picture );
1132
1133             /* Convert YUVP to YUVA/RGBA first for better scaling quality */
1134             if( b_using_palette )
1135             {
1136                 filter_t *p_scale_yuvp = p_sys->p_scale_yuvp;
1137
1138                 p_scale_yuvp->fmt_in.video = p_region->fmt;
1139
1140                 /* TODO converting to RGBA for RGB video output is better */
1141                 p_scale_yuvp->fmt_out.video = p_region->fmt;
1142                 p_scale_yuvp->fmt_out.video.i_chroma = VLC_FOURCC_YUVA;
1143
1144                 p_picture = p_scale_yuvp->pf_video_filter( p_scale_yuvp, p_picture );
1145                 if( !p_picture )
1146                 {
1147                     /* Well we will try conversion+scaling */
1148                     msg_Warn( p_spu, "%4.4s to %4.4s conversion failed",
1149                              (const char*)&p_scale_yuvp->fmt_in.video.i_chroma,
1150                              (const char*)&p_scale_yuvp->fmt_out.video.i_chroma );
1151                 }
1152             }
1153
1154             /* Conversion(except from YUVP)/Scaling */
1155             if( p_picture &&
1156                 ( p_picture->format.i_width != i_dst_width ||
1157                   p_picture->format.i_height != i_dst_height ) )
1158             {
1159                 p_scale->fmt_in.video = p_picture->format;
1160                 p_scale->fmt_out.video = p_picture->format;
1161
1162                 p_scale->fmt_out.video.i_width = i_dst_width;
1163                 p_scale->fmt_out.video.i_height = i_dst_height;
1164
1165                 p_scale->fmt_out.video.i_visible_width =
1166                     spu_scale_w( p_region->fmt.i_visible_width, scale_size );
1167                 p_scale->fmt_out.video.i_visible_height =
1168                     spu_scale_h( p_region->fmt.i_visible_height, scale_size );
1169
1170                 p_picture = p_scale->pf_video_filter( p_scale, p_picture );
1171                 if( !p_picture )
1172                     msg_Err( p_spu, "scaling failed" );
1173             }
1174
1175             /* */
1176             if( p_picture )
1177             {
1178                 p_region->p_private = SpuRegionPrivateCreate( &p_picture->format );
1179                 if( p_region->p_private )
1180                 {
1181                     p_region->p_private->p_picture = p_picture;
1182                     if( !p_region->p_private->p_picture )
1183                     {
1184                         SpuRegionPrivateDestroy( p_region->p_private );
1185                         p_region->p_private = NULL;
1186                     }
1187                 }
1188                 else
1189                 {
1190                     picture_Release( p_picture );
1191                 }
1192             }
1193         }
1194
1195         /* And use the scaled picture */
1196         if( p_region->p_private )
1197         {
1198             region_fmt = p_region->p_private->fmt;
1199             p_region_picture = p_region->p_private->p_picture;
1200         }
1201     }
1202
1203     /* Force cropping if requested */
1204     if( b_force_crop )
1205     {
1206         int i_crop_x = spu_scale_w( p_sys->i_crop_x, scale_size );
1207         int i_crop_y = spu_scale_h( p_sys->i_crop_y, scale_size );
1208         int i_crop_width = spu_scale_w( p_sys->i_crop_width, scale_size );
1209         int i_crop_height= spu_scale_h( p_sys->i_crop_height,scale_size );
1210
1211         /* Find the intersection */
1212         if( i_crop_x + i_crop_width <= i_x_offset ||
1213             i_x_offset + (int)region_fmt.i_visible_width < i_crop_x ||
1214             i_crop_y + i_crop_height <= i_y_offset ||
1215             i_y_offset + (int)region_fmt.i_visible_height < i_crop_y )
1216         {
1217             /* No intersection */
1218             region_fmt.i_visible_width =
1219             region_fmt.i_visible_height = 0;
1220         }
1221         else
1222         {
1223             int i_x, i_y, i_x_end, i_y_end;
1224             i_x = __MAX( i_crop_x, i_x_offset );
1225             i_y = __MAX( i_crop_y, i_y_offset );
1226             i_x_end = __MIN( i_crop_x + i_crop_width,
1227                            i_x_offset + (int)region_fmt.i_visible_width );
1228             i_y_end = __MIN( i_crop_y + i_crop_height,
1229                            i_y_offset + (int)region_fmt.i_visible_height );
1230
1231             region_fmt.i_x_offset = i_x - i_x_offset;
1232             region_fmt.i_y_offset = i_y - i_y_offset;
1233             region_fmt.i_visible_width = i_x_end - i_x;
1234             region_fmt.i_visible_height = i_y_end - i_y;
1235
1236             i_x_offset = __MAX( i_x, 0 );
1237             i_y_offset = __MAX( i_y, 0 );
1238         }
1239     }
1240
1241     /* Update the blender */
1242     SpuRenderUpdateBlend( p_spu, p_fmt->i_width, p_fmt->i_height, &region_fmt );
1243
1244     if( p_sys->p_blend->p_module )
1245     {
1246         const int i_alpha = SpuRegionAlpha( p_subpic, p_region );
1247
1248         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
1249             p_region_picture, i_x_offset, i_y_offset, i_alpha );
1250     }
1251     else
1252     {
1253         msg_Err( p_spu, "blending %4.4s to %4.4s failed",
1254                  (char *)&p_sys->p_blend->fmt_out.video.i_chroma,
1255                  (char *)&p_sys->p_blend->fmt_out.video.i_chroma );
1256     }
1257
1258 exit:
1259     if( b_rerender_text )
1260     {
1261         /* Some forms of subtitles need to be re-rendered more than
1262          * once, eg. karaoke. We therefore restore the region to its
1263          * pre-rendered state, so the next time through everything is
1264          * calculated again.
1265          */
1266         picture_Release( p_region->p_picture );
1267         p_region->p_picture = NULL;
1268         if( p_region->p_private )
1269         {
1270             SpuRegionPrivateDestroy( p_region->p_private );
1271             p_region->p_private = NULL;
1272         }
1273         p_region->i_align &= ~SUBPICTURE_RENDERED;
1274     }
1275     if( b_restore_format )
1276         p_region->fmt = fmt_original;
1277 }
1278
1279 /**
1280  * This function compares two 64 bits integers.
1281  * It can be used by qsort.
1282  */
1283 static int IntegerCmp( int64_t i0, int64_t i1 )
1284 {
1285     return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
1286 }
1287 /**
1288  * This function compares 2 subpictures using the following properties 
1289  * (ordered by priority)
1290  * 1. absolute positionning
1291  * 2. start time
1292  * 3. creation order
1293  *
1294  * It can be used by qsort.
1295  *
1296  * XXX spu_RenderSubpictures depends heavily on this order.
1297  */
1298 static int SubpictureCmp( const void *s0, const void *s1 )
1299 {
1300     subpicture_t *p_subpic0 = *(subpicture_t**)s0;
1301     subpicture_t *p_subpic1 = *(subpicture_t**)s1;
1302     int r;
1303
1304     r = IntegerCmp( !p_subpic0->b_absolute, !p_subpic1->b_absolute );
1305     if( !r )
1306         r = IntegerCmp( p_subpic0->i_start, p_subpic1->i_start );
1307     if( !r )
1308         r = IntegerCmp( p_subpic0->i_order, p_subpic1->i_order );
1309     return r;
1310 }
1311 /**
1312  * This function renders all sub picture units in the list.
1313  */
1314 void spu_RenderSubpictures( spu_t *p_spu,
1315                             picture_t *p_pic_dst, const video_format_t *p_fmt_dst,
1316                             subpicture_t *p_subpic_list,
1317                             const video_format_t *p_fmt_src )
1318 {
1319     spu_private_t *p_sys = p_spu->p;
1320
1321     const int i_source_video_width  = p_fmt_src->i_width;
1322     const int i_source_video_height = p_fmt_src->i_height;
1323     const mtime_t i_current_date = mdate();
1324
1325     unsigned int i_subpicture;
1326     subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
1327
1328     unsigned int i_subtitle_region_count;
1329     spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
1330     spu_area_t *p_subtitle_area;
1331     int i_subtitle_area;
1332
1333     vlc_mutex_lock( &p_sys->lock );
1334
1335     /* Preprocess subpictures */
1336     i_subpicture = 0;
1337     i_subtitle_region_count = 0;
1338     for( subpicture_t * p_subpic = p_subpic_list;
1339             p_subpic != NULL;
1340                 p_subpic = p_subpic->p_next )
1341     {
1342         /* */
1343         if( p_subpic->pf_pre_render )
1344             p_subpic->pf_pre_render( p_spu, p_subpic, p_fmt_dst );
1345
1346         if( p_subpic->pf_update_regions )
1347         {
1348             video_format_t fmt_org = *p_fmt_dst;
1349             fmt_org.i_width =
1350             fmt_org.i_visible_width = i_source_video_width;
1351             fmt_org.i_height =
1352             fmt_org.i_visible_height = i_source_video_height;
1353
1354             p_subpic->pf_update_regions( p_spu, p_subpic, &fmt_org, i_current_date );
1355         }
1356
1357         /* */
1358         if( p_subpic->b_subtitle )
1359         {
1360             for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1361                 i_subtitle_region_count++;
1362         }
1363
1364         /* */
1365         pp_subpicture[i_subpicture++] = p_subpic;
1366     }
1367
1368     /* Be sure we have at least 1 picture to process */
1369     if( i_subpicture <= 0 )
1370     {
1371         vlc_mutex_unlock( &p_sys->lock );
1372         return;
1373     }
1374
1375     /* Now order subpicture array
1376      * XXX The order is *really* important for overlap subtitles positionning */
1377     qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
1378
1379     /* Allocate area array for subtitle overlap */
1380     i_subtitle_area = 0;
1381     p_subtitle_area = p_subtitle_area_buffer;
1382     if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
1383         p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
1384
1385     /* Create the blending module */
1386     if( !p_sys->p_blend )
1387         SpuRenderCreateBlend( p_spu, p_fmt_dst->i_chroma, p_fmt_dst->i_aspect );
1388
1389     /* Process all subpictures and regions (in the right order) */
1390     for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
1391     {
1392         subpicture_t *p_subpic = pp_subpicture[i_index];
1393         subpicture_region_t *p_region;
1394
1395         if( !p_subpic->p_region )
1396             continue;
1397
1398         /* FIXME when possible use a better rendering size than source size
1399          * (max of display size and source size for example) FIXME */
1400         int i_render_width  = p_subpic->i_original_picture_width;
1401         int i_render_height = p_subpic->i_original_picture_height;
1402         if( !i_render_width || !i_render_height )
1403         {
1404             if( i_render_width != 0 || i_render_height != 0 )
1405                 msg_Err( p_spu, "unsupported original picture size %dx%d",
1406                          i_render_width, i_render_height );
1407
1408             p_subpic->i_original_picture_width  = i_render_width = i_source_video_width;
1409             p_subpic->i_original_picture_height = i_render_height = i_source_video_height;
1410         }
1411
1412         if( p_sys->p_text )
1413         {
1414             p_sys->p_text->fmt_out.video.i_width          =
1415             p_sys->p_text->fmt_out.video.i_visible_width  = i_render_width;
1416
1417             p_sys->p_text->fmt_out.video.i_height         =
1418             p_sys->p_text->fmt_out.video.i_visible_height = i_render_height;
1419         }
1420
1421         /* Compute scaling from picture to source size */
1422         spu_scale_t scale = spu_scale_createq( i_source_video_width,  i_render_width,
1423                                                i_source_video_height, i_render_height );
1424
1425         /* Update scaling from source size to display size(p_fmt_dst) */
1426         scale.w = scale.w * p_fmt_dst->i_width  / i_source_video_width;
1427         scale.h = scale.h * p_fmt_dst->i_height / i_source_video_height;
1428
1429         /* Set default subpicture aspect ratio
1430          * FIXME if we only handle 1 aspect ratio per picture, why is it set per
1431          * region ? */
1432         p_region = p_subpic->p_region;
1433         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
1434         {
1435             if( p_region->fmt.i_aspect != 0 )
1436             {
1437                 p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
1438                 p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
1439             }
1440             else
1441             {
1442                 p_region->fmt.i_sar_den = p_fmt_dst->i_sar_den;
1443                 p_region->fmt.i_sar_num = p_fmt_dst->i_sar_num;
1444             }
1445         }
1446
1447         /* Take care of the aspect ratio */
1448         if( p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den !=
1449             p_region->fmt.i_sar_den * p_fmt_dst->i_sar_num )
1450         {
1451             /* FIXME FIXME what about region->i_x/i_y ? */
1452             scale.w = scale.w *
1453                 (int64_t)p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den /
1454                 p_region->fmt.i_sar_den / p_fmt_dst->i_sar_num;
1455         }
1456
1457         /* Render all regions
1458          * We always transform non absolute subtitle into absolute one on the
1459          * first rendering to allow good subtitle overlap support.
1460          */
1461         for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
1462         {
1463             spu_area_t area;
1464
1465             /* Check scale validity */
1466             if( scale.w <= 0 || scale.h <= 0 )
1467                 continue;
1468
1469             /* */
1470             SpuRenderRegion( p_spu, p_pic_dst, &area,
1471                              p_subpic, p_region, scale, p_fmt_dst,
1472                              p_subtitle_area, i_subtitle_area );
1473
1474             if( p_subpic->b_subtitle )
1475             {
1476                 area = spu_area_unscaled( area, scale );
1477                 if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
1478                 {
1479                     p_region->i_x = area.i_x;
1480                     p_region->i_y = area.i_y;
1481                 }
1482                 if( p_subtitle_area )
1483                     p_subtitle_area[i_subtitle_area++] = area;
1484             }
1485         }
1486         if( p_subpic->b_subtitle )
1487             p_subpic->b_absolute = true;
1488     }
1489
1490     /* */
1491     if( p_subtitle_area != p_subtitle_area_buffer )
1492         free( p_subtitle_area );
1493
1494     vlc_mutex_unlock( &p_sys->lock );
1495 }
1496
1497 /*****************************************************************************
1498  * spu_SortSubpictures: find the subpictures to display
1499  *****************************************************************************
1500  * This function parses all subpictures and decides which ones need to be
1501  * displayed. If no picture has been selected, display_date will depend on
1502  * the subpicture.
1503  * We also check for ephemer DVD subpictures (subpictures that have
1504  * to be removed if a newer one is available), which makes it a lot
1505  * more difficult to guess if a subpicture has to be rendered or not.
1506  *****************************************************************************/
1507 static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic )
1508 {
1509     p_subpic->p_next = *pp_head;
1510
1511     *pp_head = p_subpic;
1512 }
1513
1514 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1515                                    bool b_paused, bool b_subtitle_only )
1516 {
1517     spu_private_t *p_sys = p_spu->p;
1518     int i_channel;
1519     subpicture_t *p_subpic = NULL;
1520
1521     /* Run subpicture filters */
1522     filter_chain_SubFilter( p_sys->p_chain, display_date );
1523
1524     vlc_mutex_lock( &p_sys->lock );
1525
1526     /* We get an easily parsable chained list of subpictures which
1527      * ends with NULL since p_subpic was initialized to NULL. */
1528     for( i_channel = 0; i_channel < p_sys->i_channel; i_channel++ )
1529     {
1530         subpicture_t *p_ephemer = NULL;
1531         mtime_t      ephemer_date = 0;
1532         int i_index;
1533
1534         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1535         {
1536             spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
1537             subpicture_t *p_current = p_entry->p_subpicture;
1538
1539             if( !p_current || p_entry->b_reject )
1540             {
1541                 if( p_entry->b_reject )
1542                     SpuHeapDeleteAt( &p_sys->heap, i_index );
1543                 continue;
1544             }
1545
1546             if( p_current->i_channel != i_channel ||
1547                 ( b_subtitle_only && !p_current->b_subtitle ) )
1548             {
1549                 continue;
1550             }
1551             if( display_date &&
1552                 display_date < p_current->i_start )
1553             {
1554                 /* Too early, come back next monday */
1555                 continue;
1556             }
1557
1558             if( p_current->i_start > ephemer_date )
1559                 ephemer_date = p_current->i_start;
1560
1561             if( display_date > p_current->i_stop &&
1562                 ( !p_current->b_ephemer || p_current->i_stop > p_current->i_start ) &&
1563                 !( p_current->b_subtitle && b_paused ) ) /* XXX Assume that subtitle are pausable */
1564             {
1565                 SpuHeapDeleteAt( &p_sys->heap, i_index );
1566             }
1567             else if( p_current->b_ephemer )
1568             {
1569                 SubpictureChain( &p_ephemer, p_current );
1570             }
1571             else
1572             {
1573                 SubpictureChain( &p_subpic, p_current );
1574             }
1575         }
1576
1577         /* If we found ephemer subpictures, check if they have to be
1578          * displayed or destroyed */
1579         while( p_ephemer != NULL )
1580         {
1581             subpicture_t *p_tmp = p_ephemer;
1582             p_ephemer = p_ephemer->p_next;
1583
1584             if( p_tmp->i_start < ephemer_date )
1585             {
1586                 SpuHeapDeleteSubpicture( &p_sys->heap, p_tmp );
1587             }
1588             else
1589             {
1590                 SubpictureChain( &p_subpic, p_tmp );
1591             }
1592         }
1593     }
1594     vlc_mutex_unlock( &p_sys->lock );
1595
1596     return p_subpic;
1597 }
1598
1599 /*****************************************************************************
1600  * SpuClearChannel: clear an spu channel
1601  *****************************************************************************
1602  * This function destroys the subpictures which belong to the spu channel
1603  * corresponding to i_channel_id.
1604  *****************************************************************************/
1605 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1606 {
1607     spu_private_t *p_sys = p_spu->p;
1608     int          i_subpic;                               /* subpicture index */
1609
1610     if( !b_locked )
1611         vlc_mutex_lock( &p_sys->lock );
1612
1613     vlc_assert_locked( &p_sys->lock );
1614
1615     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1616     {
1617         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_subpic];
1618         subpicture_t *p_subpic = p_entry->p_subpicture;
1619
1620         if( !p_subpic || p_subpic->i_channel != i_channel )
1621             continue;
1622
1623         /* You cannot delete subpicture outside of spu_SortSubpictures */
1624         p_entry->b_reject = true;
1625     }
1626
1627     if( !b_locked )
1628         vlc_mutex_unlock( &p_sys->lock );
1629 }
1630
1631 /*****************************************************************************
1632  * spu_ControlDefault: default methods for the subpicture unit control.
1633  *****************************************************************************/
1634 static int SpuControl( spu_t *p_spu, int i_query, va_list args )
1635 {
1636     spu_private_t *p_sys = p_spu->p;
1637     int *pi, i;
1638
1639     switch( i_query )
1640     {
1641     case SPU_CHANNEL_REGISTER:
1642         pi = (int *)va_arg( args, int * );
1643         vlc_mutex_lock( &p_sys->lock );
1644         if( pi )
1645             *pi = p_sys->i_channel++;
1646         vlc_mutex_unlock( &p_sys->lock );
1647         break;
1648
1649     case SPU_CHANNEL_CLEAR:
1650         i = (int)va_arg( args, int );
1651         SpuClearChannel( p_spu, i, false );
1652         break;
1653
1654     default:
1655         msg_Dbg( p_spu, "control query not supported" );
1656         return VLC_EGENERIC;
1657     }
1658
1659     return VLC_SUCCESS;
1660 }
1661
1662 /*****************************************************************************
1663  * Object variables callbacks
1664  *****************************************************************************/
1665
1666 /*****************************************************************************
1667  * UpdateSPU: update subpicture settings
1668  *****************************************************************************
1669  * This function is called from CropCallback and at initialization time, to
1670  * retrieve crop information from the input.
1671  *****************************************************************************/
1672 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1673 {
1674     spu_private_t *p_sys = p_spu->p;
1675     vlc_value_t val;
1676
1677     vlc_mutex_lock( &p_sys->lock );
1678
1679     p_sys->b_force_palette = false;
1680     p_sys->b_force_crop = false;
1681
1682     if( var_Get( p_object, "highlight", &val ) || !val.b_bool )
1683     {
1684         vlc_mutex_unlock( &p_sys->lock );
1685         return;
1686     }
1687
1688     p_sys->b_force_crop = true;
1689     p_sys->i_crop_x = var_GetInteger( p_object, "x-start" );
1690     p_sys->i_crop_y = var_GetInteger( p_object, "y-start" );
1691     p_sys->i_crop_width  = var_GetInteger( p_object, "x-end" ) - p_sys->i_crop_x;
1692     p_sys->i_crop_height = var_GetInteger( p_object, "y-end" ) - p_sys->i_crop_y;
1693
1694     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1695     {
1696         memcpy( p_sys->palette, val.p_address, 16 );
1697         p_sys->b_force_palette = true;
1698     }
1699     vlc_mutex_unlock( &p_sys->lock );
1700
1701     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1702              p_sys->i_crop_x, p_sys->i_crop_y,
1703              p_sys->i_crop_width, p_sys->i_crop_height,
1704              p_sys->b_force_palette );
1705 }
1706
1707 /*****************************************************************************
1708  * CropCallback: called when the highlight properties are changed
1709  *****************************************************************************
1710  * This callback is called from the input thread when we need cropping
1711  *****************************************************************************/
1712 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1713                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1714 {
1715     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1716
1717     UpdateSPU( (spu_t *)p_data, p_object );
1718     return VLC_SUCCESS;
1719 }
1720
1721 /*****************************************************************************
1722  * Buffers allocation callbacks for the filters
1723  *****************************************************************************/
1724 struct filter_owner_sys_t
1725 {
1726     spu_t *p_spu;
1727     int i_channel;
1728 };
1729
1730 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1731 {
1732     filter_owner_sys_t *p_sys = p_filter->p_owner;
1733
1734     subpicture_t *p_subpicture = subpicture_New();
1735     if( p_subpicture )
1736         p_subpicture->i_channel = p_sys->i_channel;
1737     return p_subpicture;
1738 }
1739 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1740 {
1741     VLC_UNUSED( p_filter );
1742     subpicture_Delete( p_subpic );
1743 }
1744
1745 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1746 {
1747     VLC_UNUSED(p_filter);
1748     return subpicture_New();
1749 }
1750 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1751 {
1752     VLC_UNUSED(p_filter);
1753     subpicture_Delete( p_subpic );
1754 }
1755
1756 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1757 {
1758     const video_format_t *p_fmt = &p_filter->fmt_out.video;
1759
1760     VLC_UNUSED(p_filter);
1761     return picture_New( p_fmt->i_chroma,
1762                         p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
1763 }
1764 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_picture )
1765 {
1766     VLC_UNUSED(p_filter);
1767     picture_Release( p_picture );
1768 }
1769
1770 static int SubFilterAllocationInit( filter_t *p_filter, void *p_data )
1771 {
1772     spu_t *p_spu = p_data;
1773
1774     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1775     if( !p_sys )
1776         return VLC_EGENERIC;
1777
1778     p_filter->pf_sub_buffer_new = sub_new_buffer;
1779     p_filter->pf_sub_buffer_del = sub_del_buffer;
1780
1781     p_filter->p_owner = p_sys;
1782     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1783     p_sys->p_spu = p_spu;
1784
1785     return VLC_SUCCESS;
1786 }
1787
1788 static void SubFilterAllocationClean( filter_t *p_filter )
1789 {
1790     filter_owner_sys_t *p_sys = p_filter->p_owner;
1791
1792     SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1793     free( p_filter->p_owner );
1794 }
1795
1796 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1797                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1798 {
1799     spu_t *p_spu = p_data;
1800     spu_private_t *p_sys = p_spu->p;
1801
1802     VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1803     VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1804
1805     vlc_mutex_lock( &p_sys->lock );
1806     filter_chain_Reset( p_sys->p_chain, NULL, NULL );
1807     spu_ParseChain( p_spu );
1808     vlc_mutex_unlock( &p_sys->lock );
1809     return VLC_SUCCESS;
1810 }
1811