1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2007 the VideoLAN team
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_modules.h>
38 #include <vlc_input.h>
40 #include <vlc_filter.h>
43 #include "../libvlc.h"
44 #include "vout_internal.h"
45 #include "../misc/subpicture.h"
47 /*****************************************************************************
49 *****************************************************************************/
51 /* Number of simultaneous subpictures */
52 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
57 subpicture_t
*p_subpicture
;
63 spu_heap_entry_t p_entry
[VOUT_MAX_SUBPICTURES
];
69 vlc_mutex_t lock
; /* lock to protect all followings fields */
70 vlc_object_t
*p_input
;
74 int i_channel
; /**< number of subpicture channels registered */
75 filter_t
*p_text
; /**< text renderer module */
76 filter_t
*p_scale_yuvp
; /**< scaling module for YUVP */
77 filter_t
*p_scale
; /**< scaling module (all but YUVP) */
78 bool b_force_crop
; /**< force cropping of subpicture */
79 int i_crop_x
, i_crop_y
, i_crop_width
, i_crop_height
; /**< cropping */
81 int i_margin
; /**< force position of a subpicture */
82 bool b_force_palette
; /**< force palette of subpicture */
83 uint8_t palette
[4][4]; /**< forced palette */
85 /* Subpiture filters */
86 char *psz_chain_update
;
87 vlc_mutex_t chain_lock
;
88 filter_chain_t
*p_chain
;
91 mtime_t i_last_sort_date
;
94 /*****************************************************************************
96 *****************************************************************************/
97 static void SpuHeapInit( spu_heap_t
*p_heap
)
99 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
101 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
103 e
->p_subpicture
= NULL
;
108 static int SpuHeapPush( spu_heap_t
*p_heap
, subpicture_t
*p_subpic
)
110 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
112 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
114 if( e
->p_subpicture
)
117 e
->p_subpicture
= p_subpic
;
124 static void SpuHeapDeleteAt( spu_heap_t
*p_heap
, int i_index
)
126 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i_index
];
128 if( e
->p_subpicture
)
129 subpicture_Delete( e
->p_subpicture
);
131 e
->p_subpicture
= NULL
;
134 static int SpuHeapDeleteSubpicture( spu_heap_t
*p_heap
, subpicture_t
*p_subpic
)
136 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
138 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
140 if( e
->p_subpicture
!= p_subpic
)
143 SpuHeapDeleteAt( p_heap
, i
);
149 static void SpuHeapClean( spu_heap_t
*p_heap
)
151 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
153 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
154 if( e
->p_subpicture
)
155 subpicture_Delete( e
->p_subpicture
);
159 struct filter_owner_sys_t
165 static void FilterRelease( filter_t
*p_filter
)
167 if( p_filter
->p_module
)
168 module_unneed( p_filter
, p_filter
->p_module
);
169 if( p_filter
->p_owner
)
170 free( p_filter
->p_owner
);
172 vlc_object_release( p_filter
);
175 static picture_t
*spu_new_video_buffer( filter_t
*p_filter
)
177 const video_format_t
*p_fmt
= &p_filter
->fmt_out
.video
;
179 VLC_UNUSED(p_filter
);
180 return picture_NewFromFormat( p_fmt
);
182 static void spu_del_video_buffer( filter_t
*p_filter
, picture_t
*p_picture
)
184 VLC_UNUSED(p_filter
);
185 picture_Release( p_picture
);
188 static int spu_get_attachments( filter_t
*p_filter
,
189 input_attachment_t
***ppp_attachment
,
192 spu_t
*p_spu
= p_filter
->p_owner
->p_spu
;
194 int i_ret
= VLC_EGENERIC
;
195 if( p_spu
->p
->p_input
)
196 i_ret
= input_Control( (input_thread_t
*)p_spu
->p
->p_input
,
197 INPUT_GET_ATTACHMENTS
,
198 ppp_attachment
, pi_attachment
);
202 static filter_t
*SpuRenderCreateAndLoadText( spu_t
*p_spu
)
204 filter_t
*p_text
= vlc_custom_create( p_spu
, sizeof(*p_text
),
205 VLC_OBJECT_GENERIC
, "spu text" );
209 p_text
->p_owner
= xmalloc( sizeof(*p_text
->p_owner
) );
210 p_text
->p_owner
->p_spu
= p_spu
;
212 es_format_Init( &p_text
->fmt_in
, VIDEO_ES
, 0 );
214 es_format_Init( &p_text
->fmt_out
, VIDEO_ES
, 0 );
215 p_text
->fmt_out
.video
.i_width
=
216 p_text
->fmt_out
.video
.i_visible_width
= 32;
217 p_text
->fmt_out
.video
.i_height
=
218 p_text
->fmt_out
.video
.i_visible_height
= 32;
220 p_text
->pf_get_attachments
= spu_get_attachments
;
222 vlc_object_attach( p_text
, p_spu
);
223 p_text
->p_module
= module_need( p_text
, "text renderer", "$text-renderer", false );
225 /* Create a few variables used for enhanced text rendering */
226 var_Create( p_text
, "spu-elapsed", VLC_VAR_TIME
);
227 var_Create( p_text
, "text-rerender", VLC_VAR_BOOL
);
228 var_Create( p_text
, "scale", VLC_VAR_INTEGER
);
233 static filter_t
*SpuRenderCreateAndLoadScale( vlc_object_t
*p_obj
,
234 vlc_fourcc_t i_src_chroma
, vlc_fourcc_t i_dst_chroma
,
237 filter_t
*p_scale
= vlc_custom_create( p_obj
, sizeof(*p_scale
),
238 VLC_OBJECT_GENERIC
, "scale" );
242 es_format_Init( &p_scale
->fmt_in
, VIDEO_ES
, 0 );
243 p_scale
->fmt_in
.video
.i_chroma
= i_src_chroma
;
244 p_scale
->fmt_in
.video
.i_width
=
245 p_scale
->fmt_in
.video
.i_height
= 32;
247 es_format_Init( &p_scale
->fmt_out
, VIDEO_ES
, 0 );
248 p_scale
->fmt_out
.video
.i_chroma
= i_dst_chroma
;
249 p_scale
->fmt_out
.video
.i_width
=
250 p_scale
->fmt_out
.video
.i_height
= b_resize
? 16 : 32;
252 p_scale
->pf_video_buffer_new
= spu_new_video_buffer
;
253 p_scale
->pf_video_buffer_del
= spu_del_video_buffer
;
255 vlc_object_attach( p_scale
, p_obj
);
256 p_scale
->p_module
= module_need( p_scale
, "video filter2", NULL
, false );
261 static void SpuRenderText( spu_t
*p_spu
, bool *pb_rerender_text
,
262 subpicture_region_t
*p_region
,
263 mtime_t render_date
)
265 filter_t
*p_text
= p_spu
->p
->p_text
;
267 assert( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
);
269 if( !p_text
|| !p_text
->p_module
)
272 /* Setup 3 variables which can be used to render
273 * time-dependent text (and effects). The first indicates
274 * the total amount of time the text will be on screen,
275 * the second the amount of time it has already been on
276 * screen (can be a negative value as text is layed out
277 * before it is rendered) and the third is a feedback
278 * variable from the renderer - if the renderer sets it
279 * then this particular text is time-dependent, eg. the
280 * visual progress bar inside the text in karaoke and the
281 * text needs to be rendered multiple times in order for
282 * the effect to work - we therefore need to return the
283 * region to its original state at the end of the loop,
284 * instead of leaving it in YUVA or YUVP.
285 * Any renderer which is unaware of how to render
286 * time-dependent text can happily ignore the variables
287 * and render the text the same as usual - it should at
288 * least show up on screen, but the effect won't change
289 * the text over time.
291 var_SetTime( p_text
, "spu-elapsed", render_date
);
292 var_SetBool( p_text
, "text-rerender", false );
294 if( p_text
->pf_render_html
&& p_region
->psz_html
)
296 p_text
->pf_render_html( p_text
, p_region
, p_region
);
298 else if( p_text
->pf_render_text
)
300 p_text
->pf_render_text( p_text
, p_region
, p_region
);
302 *pb_rerender_text
= var_GetBool( p_text
, "text-rerender" );
306 * A few scale functions helpers.
309 #define SCALE_UNIT (1000)
316 static spu_scale_t
spu_scale_create( int w
, int h
)
318 spu_scale_t s
= { .w
= w
, .h
= h
};
325 static spu_scale_t
spu_scale_unit( void )
327 return spu_scale_create( SCALE_UNIT
, SCALE_UNIT
);
329 static spu_scale_t
spu_scale_createq( int64_t wn
, int64_t wd
, int64_t hn
, int64_t hd
)
331 return spu_scale_create( wn
* SCALE_UNIT
/ wd
,
332 hn
* SCALE_UNIT
/ hd
);
334 static int spu_scale_w( int v
, const spu_scale_t s
)
336 return v
* s
.w
/ SCALE_UNIT
;
338 static int spu_scale_h( int v
, const spu_scale_t s
)
340 return v
* s
.h
/ SCALE_UNIT
;
342 static int spu_invscale_w( int v
, const spu_scale_t s
)
344 return v
* SCALE_UNIT
/ s
.w
;
346 static int spu_invscale_h( int v
, const spu_scale_t s
)
348 return v
* SCALE_UNIT
/ s
.h
;
352 * A few area functions helpers
364 static spu_area_t
spu_area_create( int x
, int y
, int w
, int h
, spu_scale_t s
)
366 spu_area_t a
= { .i_x
= x
, .i_y
= y
, .i_width
= w
, .i_height
= h
, .scale
= s
};
369 static spu_area_t
spu_area_scaled( spu_area_t a
)
371 if( a
.scale
.w
== SCALE_UNIT
&& a
.scale
.h
== SCALE_UNIT
)
374 a
.i_x
= spu_scale_w( a
.i_x
, a
.scale
);
375 a
.i_y
= spu_scale_h( a
.i_y
, a
.scale
);
377 a
.i_width
= spu_scale_w( a
.i_width
, a
.scale
);
378 a
.i_height
= spu_scale_h( a
.i_height
, a
.scale
);
380 a
.scale
= spu_scale_unit();
383 static spu_area_t
spu_area_unscaled( spu_area_t a
, spu_scale_t s
)
385 if( a
.scale
.w
== s
.w
&& a
.scale
.h
== s
.h
)
388 a
= spu_area_scaled( a
);
390 a
.i_x
= spu_invscale_w( a
.i_x
, s
);
391 a
.i_y
= spu_invscale_h( a
.i_y
, s
);
393 a
.i_width
= spu_invscale_w( a
.i_width
, s
);
394 a
.i_height
= spu_invscale_h( a
.i_height
, s
);
399 static bool spu_area_overlap( spu_area_t a
, spu_area_t b
)
404 a
= spu_area_scaled( a
);
405 b
= spu_area_scaled( b
);
407 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
) &&
408 __MAX( a
.i_y
-i_dy
, b
.i_y
) < __MIN( a
.i_y
+a
.i_height
+i_dy
, b
.i_y
+b
.i_height
);
412 * Avoid area overlapping
414 static void SpuAreaFixOverlap( spu_area_t
*p_dst
,
415 const spu_area_t
*p_sub
, int i_sub
, int i_align
)
417 spu_area_t a
= spu_area_scaled( *p_dst
);
418 bool b_moved
= false;
422 * XXX It is not fast O(n^2) but we should not have a lot of region */
426 for( int i
= 0; i
< i_sub
; i
++ )
428 spu_area_t sub
= spu_area_scaled( p_sub
[i
] );
430 if( !spu_area_overlap( a
, sub
) )
433 if( i_align
& SUBPICTURE_ALIGN_TOP
)
436 int i_y
= sub
.i_y
+ sub
.i_height
;
440 else if( i_align
& SUBPICTURE_ALIGN_BOTTOM
)
443 int i_y
= sub
.i_y
- a
.i_height
;
449 /* TODO what to do in this case? */
450 //fprintf( stderr, "Overlap with unsupported alignment\n" );
460 *p_dst
= spu_area_unscaled( a
, p_dst
->scale
);
464 static void SpuAreaFitInside( spu_area_t
*p_area
, const spu_area_t
*p_boundary
)
466 spu_area_t a
= spu_area_scaled( *p_area
);
468 const int i_error_x
= (a
.i_x
+ a
.i_width
) - p_boundary
->i_width
;
474 const int i_error_y
= (a
.i_y
+ a
.i_height
) - p_boundary
->i_height
;
480 *p_area
= spu_area_unscaled( a
, p_area
->scale
);
486 static void SpuRegionPlace( int *pi_x
, int *pi_y
,
487 const subpicture_t
*p_subpic
,
488 const subpicture_region_t
*p_region
)
490 assert( p_region
->i_x
!= INT_MAX
&& p_region
->i_y
!= INT_MAX
);
491 if( p_subpic
->b_absolute
)
493 *pi_x
= p_region
->i_x
;
494 *pi_y
= p_region
->i_y
;
498 if( p_region
->i_align
& SUBPICTURE_ALIGN_TOP
)
500 *pi_y
= p_region
->i_y
;
502 else if( p_region
->i_align
& SUBPICTURE_ALIGN_BOTTOM
)
504 *pi_y
= p_subpic
->i_original_picture_height
- p_region
->fmt
.i_height
- p_region
->i_y
;
508 *pi_y
= p_subpic
->i_original_picture_height
/ 2 - p_region
->fmt
.i_height
/ 2;
511 if( p_region
->i_align
& SUBPICTURE_ALIGN_LEFT
)
513 *pi_x
= p_region
->i_x
;
515 else if( p_region
->i_align
& SUBPICTURE_ALIGN_RIGHT
)
517 *pi_x
= p_subpic
->i_original_picture_width
- p_region
->fmt
.i_width
- p_region
->i_x
;
521 *pi_x
= p_subpic
->i_original_picture_width
/ 2 - p_region
->fmt
.i_width
/ 2;
527 * This function compares two 64 bits integers.
528 * It can be used by qsort.
530 static int IntegerCmp( int64_t i0
, int64_t i1
)
532 return i0
< i1
? -1 : i0
> i1
? 1 : 0;
535 * This function compares 2 subpictures using the following properties
536 * (ordered by priority)
537 * 1. absolute positionning
539 * 3. creation order (per channel)
541 * It can be used by qsort.
543 * XXX spu_RenderSubpictures depends heavily on this order.
545 static int SubpictureCmp( const void *s0
, const void *s1
)
547 subpicture_t
*p_subpic0
= *(subpicture_t
**)s0
;
548 subpicture_t
*p_subpic1
= *(subpicture_t
**)s1
;
551 r
= IntegerCmp( !p_subpic0
->b_absolute
, !p_subpic1
->b_absolute
);
553 r
= IntegerCmp( p_subpic0
->i_start
, p_subpic1
->i_start
);
555 r
= IntegerCmp( p_subpic0
->i_channel
, p_subpic1
->i_channel
);
557 r
= IntegerCmp( p_subpic0
->i_order
, p_subpic1
->i_order
);
561 /*****************************************************************************
562 * SpuSelectSubpictures: find the subpictures to display
563 *****************************************************************************
564 * This function parses all subpictures and decides which ones need to be
565 * displayed. If no picture has been selected, display_date will depend on
567 * We also check for ephemer DVD subpictures (subpictures that have
568 * to be removed if a newer one is available), which makes it a lot
569 * more difficult to guess if a subpicture has to be rendered or not.
570 *****************************************************************************/
571 static void SpuSelectSubpictures( spu_t
*p_spu
,
572 unsigned int *pi_subpicture
,
573 subpicture_t
**pp_subpicture
,
574 mtime_t render_subtitle_date
,
575 mtime_t render_osd_date
,
576 bool b_subtitle_only
)
578 spu_private_t
*p_sys
= p_spu
->p
;
583 /* Create a list of channels */
584 int pi_channel
[VOUT_MAX_SUBPICTURES
];
585 int i_channel_count
= 0;
587 for( int i_index
= 0; i_index
< VOUT_MAX_SUBPICTURES
; i_index
++ )
589 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i_index
];
590 if( !p_entry
->p_subpicture
|| p_entry
->b_reject
)
592 const int i_channel
= p_entry
->p_subpicture
->i_channel
;
594 for( i
= 0; i
< i_channel_count
; i
++ )
596 if( pi_channel
[i
] == i_channel
)
599 if( i_channel_count
<= i
)
600 pi_channel
[i_channel_count
++] = i_channel
;
603 /* Fill up the pp_subpicture arrays with relevent pictures */
604 for( int i
= 0; i
< i_channel_count
; i
++ )
606 const int i_channel
= pi_channel
[i
];
607 subpicture_t
*p_available_subpic
[VOUT_MAX_SUBPICTURES
];
608 bool pb_available_late
[VOUT_MAX_SUBPICTURES
];
611 mtime_t start_date
= render_subtitle_date
;
612 mtime_t ephemer_subtitle_date
= 0;
613 mtime_t ephemer_osd_date
= 0;
614 int64_t i_ephemer_subtitle_order
= INT64_MIN
;
615 int64_t i_ephemer_system_order
= INT64_MIN
;
618 /* Select available pictures */
619 for( i_index
= 0; i_index
< VOUT_MAX_SUBPICTURES
; i_index
++ )
621 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i_index
];
622 subpicture_t
*p_current
= p_entry
->p_subpicture
;
626 if( !p_current
|| p_entry
->b_reject
)
628 if( p_entry
->b_reject
)
629 SpuHeapDeleteAt( &p_sys
->heap
, i_index
);
633 if( p_current
->i_channel
!= i_channel
||
634 ( b_subtitle_only
&& !p_current
->b_subtitle
) )
638 const mtime_t render_date
= p_current
->b_subtitle
? render_subtitle_date
: render_osd_date
;
640 render_date
< p_current
->i_start
)
642 /* Too early, come back next monday */
646 mtime_t
*pi_ephemer_date
= p_current
->b_subtitle
? &ephemer_subtitle_date
: &ephemer_osd_date
;
647 int64_t *pi_ephemer_order
= p_current
->b_subtitle
? &i_ephemer_subtitle_order
: &i_ephemer_system_order
;
648 if( p_current
->i_start
>= *pi_ephemer_date
)
650 *pi_ephemer_date
= p_current
->i_start
;
651 if( p_current
->i_order
> *pi_ephemer_order
)
652 *pi_ephemer_order
= p_current
->i_order
;
655 b_stop_valid
= !p_current
->b_ephemer
|| p_current
->i_stop
> p_current
->i_start
;
657 b_late
= b_stop_valid
&& p_current
->i_stop
<= render_date
;
659 /* start_date will be used for correct automatic overlap support
660 * in case picture that should not be displayed anymore (display_time)
661 * overlap with a picture to be displayed (p_current->i_start) */
662 if( p_current
->b_subtitle
&& !b_late
&& !p_current
->b_ephemer
)
663 start_date
= p_current
->i_start
;
666 p_available_subpic
[i_available
] = p_current
;
667 pb_available_late
[i_available
] = b_late
;
671 /* Only forced old picture display at the transition */
672 if( start_date
< p_sys
->i_last_sort_date
)
673 start_date
= p_sys
->i_last_sort_date
;
674 if( start_date
<= 0 )
675 start_date
= INT64_MAX
;
677 /* Select pictures to be displayed */
678 for( i_index
= 0; i_index
< i_available
; i_index
++ )
680 subpicture_t
*p_current
= p_available_subpic
[i_index
];
681 bool b_late
= pb_available_late
[i_index
];
683 const mtime_t stop_date
= p_current
->b_subtitle
? __MAX( start_date
, p_sys
->i_last_sort_date
) : render_osd_date
;
684 const mtime_t ephemer_date
= p_current
->b_subtitle
? ephemer_subtitle_date
: ephemer_osd_date
;
685 const int64_t i_ephemer_order
= p_current
->b_subtitle
? i_ephemer_subtitle_order
: i_ephemer_system_order
;
687 /* Destroy late and obsolete ephemer subpictures */
688 bool b_rejet
= b_late
&& p_current
->i_stop
<= stop_date
;
689 if( p_current
->b_ephemer
)
691 if( p_current
->i_start
< ephemer_date
)
693 else if( p_current
->i_start
== ephemer_date
&&
694 p_current
->i_order
< i_ephemer_order
)
699 SpuHeapDeleteSubpicture( &p_sys
->heap
, p_current
);
701 pp_subpicture
[(*pi_subpicture
)++] = p_current
;
705 p_sys
->i_last_sort_date
= render_subtitle_date
;
711 * It will transform the provided region into another region suitable for rendering.
714 static void SpuRenderRegion( spu_t
*p_spu
,
715 subpicture_region_t
**pp_dst
, spu_area_t
*p_dst_area
,
716 subpicture_t
*p_subpic
, subpicture_region_t
*p_region
,
717 const spu_scale_t scale_size
,
718 const vlc_fourcc_t
*p_chroma_list
,
719 const video_format_t
*p_fmt
,
720 const spu_area_t
*p_subtitle_area
, int i_subtitle_area
,
721 mtime_t render_date
)
723 spu_private_t
*p_sys
= p_spu
->p
;
725 video_format_t fmt_original
= p_region
->fmt
;
726 bool b_restore_text
= false;
730 video_format_t region_fmt
;
731 picture_t
*p_region_picture
;
733 /* Invalidate area by default */
734 *p_dst_area
= spu_area_create( 0,0, 0,0, scale_size
);
737 /* Render text region */
738 if( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
)
740 SpuRenderText( p_spu
, &b_restore_text
, p_region
,
743 /* Check if the rendering has failed ... */
744 if( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
)
748 /* Force palette if requested
749 * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
750 * instead of only the right one (being the dvd spu).
752 const bool b_using_palette
= p_region
->fmt
.i_chroma
== VLC_CODEC_YUVP
;
753 const bool b_force_palette
= b_using_palette
&& p_sys
->b_force_palette
;
754 const bool b_force_crop
= b_force_palette
&& p_sys
->b_force_crop
;
755 bool b_changed_palette
= false;
757 /* Compute the margin which is expressed in destination pixel unit
758 * The margin is applied only to subtitle and when no forced crop is
759 * requested (dvd menu) */
761 if( !b_force_crop
&& p_subpic
->b_subtitle
)
762 i_margin_y
= spu_invscale_h( p_sys
->i_margin
, scale_size
);
765 * We compute the position in the rendered size */
766 SpuRegionPlace( &i_x_offset
, &i_y_offset
,
767 p_subpic
, p_region
);
769 /* Save this position for subtitle overlap support
770 * it is really important that there are given without scale_size applied */
771 *p_dst_area
= spu_area_create( i_x_offset
, i_y_offset
,
772 p_region
->fmt
.i_width
, p_region
->fmt
.i_height
,
775 /* Handle overlapping subtitles when possible */
776 if( p_subpic
->b_subtitle
&& !p_subpic
->b_absolute
)
778 SpuAreaFixOverlap( p_dst_area
, p_subtitle_area
, i_subtitle_area
,
782 /* we copy the area: for the subtitle overlap support we want
783 * to only save the area without margin applied */
784 spu_area_t restrained
= *p_dst_area
;
786 /* apply margin to subtitles and correct if they go over the picture edge */
787 if( p_subpic
->b_subtitle
)
788 restrained
.i_y
-= i_margin_y
;
790 spu_area_t display
= spu_area_create( 0, 0, p_fmt
->i_width
, p_fmt
->i_height
,
792 SpuAreaFitInside( &restrained
, &display
);
794 /* Fix the position for the current scale_size */
795 i_x_offset
= spu_scale_w( restrained
.i_x
, restrained
.scale
);
796 i_y_offset
= spu_scale_h( restrained
.i_y
, restrained
.scale
);
799 if( b_force_palette
)
801 video_palette_t
*p_palette
= p_region
->fmt
.p_palette
;
802 video_palette_t palette
;
804 /* We suppose DVD palette here */
805 palette
.i_entries
= 4;
806 for( int i
= 0; i
< 4; i
++ )
807 for( int j
= 0; j
< 4; j
++ )
808 palette
.palette
[i
][j
] = p_sys
->palette
[i
][j
];
810 if( p_palette
->i_entries
== palette
.i_entries
)
812 for( int i
= 0; i
< p_palette
->i_entries
; i
++ )
813 for( int j
= 0; j
< 4; j
++ )
814 b_changed_palette
|= p_palette
->palette
[i
][j
] != palette
.palette
[i
][j
];
818 b_changed_palette
= true;
820 *p_palette
= palette
;
824 region_fmt
= p_region
->fmt
;
825 p_region_picture
= p_region
->p_picture
;
827 bool b_convert_chroma
= true;
828 for( int i
= 0; p_chroma_list
[i
] && b_convert_chroma
; i
++ )
830 if( region_fmt
.i_chroma
== p_chroma_list
[i
] )
831 b_convert_chroma
= false;
834 /* Scale from rendered size to destination size */
835 if( p_sys
->p_scale
&& p_sys
->p_scale
->p_module
&&
836 ( !b_using_palette
|| ( p_sys
->p_scale_yuvp
&& p_sys
->p_scale_yuvp
->p_module
) ) &&
837 ( scale_size
.w
!= SCALE_UNIT
|| scale_size
.h
!= SCALE_UNIT
||
838 b_using_palette
|| b_convert_chroma
) )
840 const unsigned i_dst_width
= spu_scale_w( p_region
->fmt
.i_width
, scale_size
);
841 const unsigned i_dst_height
= spu_scale_h( p_region
->fmt
.i_height
, scale_size
);
843 /* Destroy the cache if unusable */
844 if( p_region
->p_private
)
846 subpicture_region_private_t
*p_private
= p_region
->p_private
;
847 bool b_changed
= false;
849 /* Check resize changes */
850 if( i_dst_width
!= p_private
->fmt
.i_width
||
851 i_dst_height
!= p_private
->fmt
.i_height
)
854 /* Check forced palette changes */
855 if( b_changed_palette
)
858 if( b_convert_chroma
&& p_private
->fmt
.i_chroma
!= p_chroma_list
[0] )
863 subpicture_region_private_Delete( p_private
);
864 p_region
->p_private
= NULL
;
868 /* Scale if needed into cache */
869 if( !p_region
->p_private
&& i_dst_width
> 0 && i_dst_height
> 0 )
871 filter_t
*p_scale
= p_sys
->p_scale
;
873 picture_t
*p_picture
= p_region
->p_picture
;
874 picture_Hold( p_picture
);
876 /* Convert YUVP to YUVA/RGBA first for better scaling quality */
877 if( b_using_palette
)
879 filter_t
*p_scale_yuvp
= p_sys
->p_scale_yuvp
;
881 p_scale_yuvp
->fmt_in
.video
= p_region
->fmt
;
883 p_scale_yuvp
->fmt_out
.video
= p_region
->fmt
;
884 p_scale_yuvp
->fmt_out
.video
.i_chroma
= p_chroma_list
[0];
886 p_picture
= p_scale_yuvp
->pf_video_filter( p_scale_yuvp
, p_picture
);
889 /* Well we will try conversion+scaling */
890 msg_Warn( p_spu
, "%4.4s to %4.4s conversion failed",
891 (const char*)&p_scale_yuvp
->fmt_in
.video
.i_chroma
,
892 (const char*)&p_scale_yuvp
->fmt_out
.video
.i_chroma
);
896 /* Conversion(except from YUVP)/Scaling */
898 ( p_picture
->format
.i_width
!= i_dst_width
||
899 p_picture
->format
.i_height
!= i_dst_height
||
900 ( b_convert_chroma
&& !b_using_palette
) ) )
902 p_scale
->fmt_in
.video
= p_picture
->format
;
903 p_scale
->fmt_out
.video
= p_picture
->format
;
904 if( b_convert_chroma
)
905 p_scale
->fmt_out
.i_codec
=
906 p_scale
->fmt_out
.video
.i_chroma
= p_chroma_list
[0];
908 p_scale
->fmt_out
.video
.i_width
= i_dst_width
;
909 p_scale
->fmt_out
.video
.i_height
= i_dst_height
;
911 p_scale
->fmt_out
.video
.i_visible_width
=
912 spu_scale_w( p_region
->fmt
.i_visible_width
, scale_size
);
913 p_scale
->fmt_out
.video
.i_visible_height
=
914 spu_scale_h( p_region
->fmt
.i_visible_height
, scale_size
);
916 p_picture
= p_scale
->pf_video_filter( p_scale
, p_picture
);
918 msg_Err( p_spu
, "scaling failed" );
924 p_region
->p_private
= subpicture_region_private_New( &p_picture
->format
);
925 if( p_region
->p_private
)
927 p_region
->p_private
->p_picture
= p_picture
;
928 if( !p_region
->p_private
->p_picture
)
930 subpicture_region_private_Delete( p_region
->p_private
);
931 p_region
->p_private
= NULL
;
936 picture_Release( p_picture
);
941 /* And use the scaled picture */
942 if( p_region
->p_private
)
944 region_fmt
= p_region
->p_private
->fmt
;
945 p_region_picture
= p_region
->p_private
->p_picture
;
949 /* Force cropping if requested */
952 int i_crop_x
= spu_scale_w( p_sys
->i_crop_x
, scale_size
);
953 int i_crop_y
= spu_scale_h( p_sys
->i_crop_y
, scale_size
);
954 int i_crop_width
= spu_scale_w( p_sys
->i_crop_width
, scale_size
);
955 int i_crop_height
= spu_scale_h( p_sys
->i_crop_height
,scale_size
);
957 /* Find the intersection */
958 if( i_crop_x
+ i_crop_width
<= i_x_offset
||
959 i_x_offset
+ (int)region_fmt
.i_visible_width
< i_crop_x
||
960 i_crop_y
+ i_crop_height
<= i_y_offset
||
961 i_y_offset
+ (int)region_fmt
.i_visible_height
< i_crop_y
)
963 /* No intersection */
964 region_fmt
.i_visible_width
=
965 region_fmt
.i_visible_height
= 0;
969 int i_x
, i_y
, i_x_end
, i_y_end
;
970 i_x
= __MAX( i_crop_x
, i_x_offset
);
971 i_y
= __MAX( i_crop_y
, i_y_offset
);
972 i_x_end
= __MIN( i_crop_x
+ i_crop_width
,
973 i_x_offset
+ (int)region_fmt
.i_visible_width
);
974 i_y_end
= __MIN( i_crop_y
+ i_crop_height
,
975 i_y_offset
+ (int)region_fmt
.i_visible_height
);
977 region_fmt
.i_x_offset
= i_x
- i_x_offset
;
978 region_fmt
.i_y_offset
= i_y
- i_y_offset
;
979 region_fmt
.i_visible_width
= i_x_end
- i_x
;
980 region_fmt
.i_visible_height
= i_y_end
- i_y
;
982 i_x_offset
= __MAX( i_x
, 0 );
983 i_y_offset
= __MAX( i_y
, 0 );
987 subpicture_region_t
*p_dst
= *pp_dst
= subpicture_region_New( ®ion_fmt
);
990 p_dst
->i_x
= i_x_offset
;
991 p_dst
->i_y
= i_y_offset
;
993 if( p_dst
->p_picture
)
994 picture_Release( p_dst
->p_picture
);
995 p_dst
->p_picture
= picture_Hold( p_region_picture
);
996 int i_fade_alpha
= 255;
997 if( p_subpic
->b_fade
)
999 mtime_t fade_start
= ( p_subpic
->i_stop
+
1000 p_subpic
->i_start
) / 2;
1002 if( fade_start
<= render_date
&& fade_start
< p_subpic
->i_stop
)
1003 i_fade_alpha
= 255 * ( p_subpic
->i_stop
- render_date
) /
1004 ( p_subpic
->i_stop
- fade_start
);
1006 p_dst
->i_alpha
= i_fade_alpha
* p_subpic
->i_alpha
* p_region
->i_alpha
/ 65025;
1010 if( b_restore_text
)
1012 /* Some forms of subtitles need to be re-rendered more than
1013 * once, eg. karaoke. We therefore restore the region to its
1014 * pre-rendered state, so the next time through everything is
1017 if( p_region
->p_picture
)
1019 picture_Release( p_region
->p_picture
);
1020 p_region
->p_picture
= NULL
;
1022 if( p_region
->p_private
)
1024 subpicture_region_private_Delete( p_region
->p_private
);
1025 p_region
->p_private
= NULL
;
1027 p_region
->fmt
= fmt_original
;
1032 * This function renders all sub picture units in the list.
1034 static subpicture_t
*SpuRenderSubpictures( spu_t
*p_spu
,
1035 unsigned int i_subpicture
,
1036 subpicture_t
**pp_subpicture
,
1037 const vlc_fourcc_t
*p_chroma_list
,
1038 const video_format_t
*p_fmt_dst
,
1039 const video_format_t
*p_fmt_src
,
1040 mtime_t render_subtitle_date
,
1041 mtime_t render_osd_date
)
1043 spu_private_t
*p_sys
= p_spu
->p
;
1045 /* Count the number of regions and subtitle regions */
1046 unsigned int i_subtitle_region_count
= 0;
1047 unsigned int i_region_count
= 0;
1048 for( unsigned i
= 0; i
< i_subpicture
; i
++ )
1050 const subpicture_t
*p_subpic
= pp_subpicture
[i
];
1052 unsigned i_count
= 0;
1053 for( subpicture_region_t
*r
= p_subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
1056 if( p_subpic
->b_subtitle
)
1057 i_subtitle_region_count
+= i_count
;
1058 i_region_count
+= i_count
;
1060 if( i_region_count
<= 0 )
1063 /* Create the output subpicture */
1064 subpicture_t
*p_output
= subpicture_New( NULL
);
1067 p_output
->i_original_picture_width
= p_fmt_dst
->i_width
;
1068 p_output
->i_original_picture_height
= p_fmt_dst
->i_height
;
1069 subpicture_region_t
**pp_output_last
= &p_output
->p_region
;
1071 /* Allocate area array for subtitle overlap */
1072 spu_area_t p_subtitle_area_buffer
[VOUT_MAX_SUBPICTURES
];
1073 spu_area_t
*p_subtitle_area
;
1074 int i_subtitle_area
;
1076 i_subtitle_area
= 0;
1077 p_subtitle_area
= p_subtitle_area_buffer
;
1078 if( i_subtitle_region_count
> sizeof(p_subtitle_area_buffer
)/sizeof(*p_subtitle_area_buffer
) )
1079 p_subtitle_area
= calloc( i_subtitle_region_count
, sizeof(*p_subtitle_area
) );
1081 /* Process all subpictures and regions (in the right order) */
1082 for( unsigned int i_index
= 0; i_index
< i_subpicture
; i_index
++ )
1084 subpicture_t
*p_subpic
= pp_subpicture
[i_index
];
1085 subpicture_region_t
*p_region
;
1087 if( !p_subpic
->p_region
)
1090 if( p_subpic
->i_original_picture_width
<= 0 ||
1091 p_subpic
->i_original_picture_height
<= 0 )
1093 if( p_subpic
->i_original_picture_width
> 0 ||
1094 p_subpic
->i_original_picture_height
> 0 )
1095 msg_Err( p_spu
, "original picture size %dx%d is unsupported",
1096 p_subpic
->i_original_picture_width
,
1097 p_subpic
->i_original_picture_height
);
1099 msg_Warn( p_spu
, "original picture size is undefined" );
1101 p_subpic
->i_original_picture_width
= p_fmt_src
->i_width
;
1102 p_subpic
->i_original_picture_height
= p_fmt_src
->i_height
;
1107 /* FIXME aspect ratio ? */
1108 p_sys
->p_text
->fmt_out
.video
.i_width
=
1109 p_sys
->p_text
->fmt_out
.video
.i_visible_width
= p_subpic
->i_original_picture_width
;
1111 p_sys
->p_text
->fmt_out
.video
.i_height
=
1112 p_sys
->p_text
->fmt_out
.video
.i_visible_height
= p_subpic
->i_original_picture_height
;
1114 var_SetInteger( p_sys
->p_text
, "scale", SCALE_UNIT
);
1117 /* Render all regions
1118 * We always transform non absolute subtitle into absolute one on the
1119 * first rendering to allow good subtitle overlap support.
1121 for( p_region
= p_subpic
->p_region
; p_region
!= NULL
; p_region
= p_region
->p_next
)
1125 /* Compute region scale AR */
1126 video_format_t region_fmt
=p_region
->fmt
;
1127 if( region_fmt
.i_sar_num
<= 0 || region_fmt
.i_sar_den
<= 0 )
1129 region_fmt
.i_sar_num
= p_fmt_src
->i_sar_num
;
1130 region_fmt
.i_sar_den
= p_fmt_src
->i_sar_den
;
1133 /* Compute scaling from original size to destination size
1134 * FIXME The current scaling ensure that the heights match, the width being
1137 spu_scale_t scale
= spu_scale_createq( (int64_t)p_fmt_dst
->i_height
* p_fmt_dst
->i_sar_den
* region_fmt
.i_sar_num
,
1138 (int64_t)p_subpic
->i_original_picture_height
* p_fmt_dst
->i_sar_num
* region_fmt
.i_sar_den
,
1139 p_fmt_dst
->i_height
,
1140 p_subpic
->i_original_picture_height
);
1142 /* Check scale validity */
1143 if( scale
.w
<= 0 || scale
.h
<= 0 )
1147 SpuRenderRegion( p_spu
, pp_output_last
, &area
,
1148 p_subpic
, p_region
, scale
,
1149 p_chroma_list
, p_fmt_dst
,
1150 p_subtitle_area
, i_subtitle_area
,
1151 p_subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
1152 if( *pp_output_last
)
1153 pp_output_last
= &(*pp_output_last
)->p_next
;
1155 if( p_subpic
->b_subtitle
)
1157 area
= spu_area_unscaled( area
, scale
);
1158 if( !p_subpic
->b_absolute
&& area
.i_width
> 0 && area
.i_height
> 0 )
1160 p_region
->i_x
= area
.i_x
;
1161 p_region
->i_y
= area
.i_y
;
1163 if( p_subtitle_area
)
1164 p_subtitle_area
[i_subtitle_area
++] = area
;
1167 if( p_subpic
->b_subtitle
&& p_subpic
->p_region
)
1168 p_subpic
->b_absolute
= true;
1172 if( p_subtitle_area
!= p_subtitle_area_buffer
)
1173 free( p_subtitle_area
);
1178 /*****************************************************************************
1179 * Object variables callbacks
1180 *****************************************************************************/
1182 /*****************************************************************************
1183 * UpdateSPU: update subpicture settings
1184 *****************************************************************************
1185 * This function is called from CropCallback and at initialization time, to
1186 * retrieve crop information from the input.
1187 *****************************************************************************/
1188 static void UpdateSPU( spu_t
*p_spu
, vlc_object_t
*p_object
)
1190 spu_private_t
*p_sys
= p_spu
->p
;
1193 vlc_mutex_lock( &p_sys
->lock
);
1195 p_sys
->b_force_palette
= false;
1196 p_sys
->b_force_crop
= false;
1198 if( var_Get( p_object
, "highlight", &val
) || !val
.b_bool
)
1200 vlc_mutex_unlock( &p_sys
->lock
);
1204 p_sys
->b_force_crop
= true;
1205 p_sys
->i_crop_x
= var_GetInteger( p_object
, "x-start" );
1206 p_sys
->i_crop_y
= var_GetInteger( p_object
, "y-start" );
1207 p_sys
->i_crop_width
= var_GetInteger( p_object
, "x-end" ) - p_sys
->i_crop_x
;
1208 p_sys
->i_crop_height
= var_GetInteger( p_object
, "y-end" ) - p_sys
->i_crop_y
;
1210 if( var_Get( p_object
, "menu-palette", &val
) == VLC_SUCCESS
)
1212 memcpy( p_sys
->palette
, val
.p_address
, 16 );
1213 p_sys
->b_force_palette
= true;
1215 vlc_mutex_unlock( &p_sys
->lock
);
1217 msg_Dbg( p_object
, "crop: %i,%i,%i,%i, palette forced: %i",
1218 p_sys
->i_crop_x
, p_sys
->i_crop_y
,
1219 p_sys
->i_crop_width
, p_sys
->i_crop_height
,
1220 p_sys
->b_force_palette
);
1223 /*****************************************************************************
1224 * CropCallback: called when the highlight properties are changed
1225 *****************************************************************************
1226 * This callback is called from the input thread when we need cropping
1227 *****************************************************************************/
1228 static int CropCallback( vlc_object_t
*p_object
, char const *psz_var
,
1229 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1231 VLC_UNUSED(oldval
); VLC_UNUSED(newval
); VLC_UNUSED(psz_var
);
1233 UpdateSPU( (spu_t
*)p_data
, p_object
);
1237 /*****************************************************************************
1238 * Buffers allocation callbacks for the filters
1239 *****************************************************************************/
1241 static subpicture_t
*sub_new_buffer( filter_t
*p_filter
)
1243 filter_owner_sys_t
*p_sys
= p_filter
->p_owner
;
1245 subpicture_t
*p_subpicture
= subpicture_New( NULL
);
1247 p_subpicture
->i_channel
= p_sys
->i_channel
;
1248 return p_subpicture
;
1250 static void sub_del_buffer( filter_t
*p_filter
, subpicture_t
*p_subpic
)
1252 VLC_UNUSED( p_filter
);
1253 subpicture_Delete( p_subpic
);
1256 static int SubSourceAllocationInit( filter_t
*p_filter
, void *p_data
)
1258 spu_t
*p_spu
= p_data
;
1260 filter_owner_sys_t
*p_sys
= malloc( sizeof(filter_owner_sys_t
) );
1262 return VLC_EGENERIC
;
1264 p_filter
->pf_sub_buffer_new
= sub_new_buffer
;
1265 p_filter
->pf_sub_buffer_del
= sub_del_buffer
;
1267 p_filter
->p_owner
= p_sys
;
1268 p_sys
->i_channel
= spu_RegisterChannel( p_spu
);
1269 p_sys
->p_spu
= p_spu
;
1274 static void SubSourceAllocationClean( filter_t
*p_filter
)
1276 filter_owner_sys_t
*p_sys
= p_filter
->p_owner
;
1278 spu_ClearChannel( p_sys
->p_spu
, p_sys
->i_channel
);
1279 free( p_filter
->p_owner
);
1282 /*****************************************************************************
1284 *****************************************************************************/
1288 * Creates the subpicture unit
1290 * \param p_this the parent object which creates the subpicture unit
1292 spu_t
*spu_Create( vlc_object_t
*p_this
)
1295 spu_private_t
*p_sys
;
1297 p_spu
= vlc_custom_create( p_this
, sizeof(spu_t
) + sizeof(spu_private_t
),
1298 VLC_OBJECT_GENERIC
, "subpicture" );
1301 vlc_object_attach( p_spu
, p_this
);
1303 /* Initialize spu fields */
1304 p_spu
->p
= p_sys
= (spu_private_t
*)&p_spu
[1];
1306 /* Initialize private fields */
1307 vlc_mutex_init( &p_sys
->lock
);
1309 SpuHeapInit( &p_sys
->heap
);
1311 p_sys
->p_text
= NULL
;
1312 p_sys
->p_scale
= NULL
;
1313 p_sys
->p_scale_yuvp
= NULL
;
1315 p_sys
->i_margin
= var_InheritInteger( p_spu
, "sub-margin" );
1317 /* Register the default subpicture channel */
1318 p_sys
->i_channel
= SPU_DEFAULT_CHANNEL
+ 1;
1320 p_sys
->psz_chain_update
= NULL
;
1321 vlc_mutex_init( &p_sys
->chain_lock
);
1322 p_sys
->p_chain
= filter_chain_New( p_spu
, "sub source", false,
1323 SubSourceAllocationInit
,
1324 SubSourceAllocationClean
,
1327 /* Load text and scale module */
1328 p_sys
->p_text
= SpuRenderCreateAndLoadText( p_spu
);
1330 /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
1332 p_sys
->p_scale
= SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu
),
1333 VLC_CODEC_YUVA
, VLC_CODEC_RGBA
, true );
1335 /* This one is used for YUVP to YUVA/RGBA without scaling
1336 * FIXME rename it */
1337 p_sys
->p_scale_yuvp
= SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu
),
1338 VLC_CODEC_YUVP
, VLC_CODEC_YUVA
, false );
1341 p_sys
->i_last_sort_date
= -1;
1347 * Destroy the subpicture unit
1349 * \param p_this the parent object which destroys the subpicture unit
1351 void spu_Destroy( spu_t
*p_spu
)
1353 spu_private_t
*p_sys
= p_spu
->p
;
1356 FilterRelease( p_sys
->p_text
);
1358 if( p_sys
->p_scale_yuvp
)
1359 FilterRelease( p_sys
->p_scale_yuvp
);
1361 if( p_sys
->p_scale
)
1362 FilterRelease( p_sys
->p_scale
);
1364 filter_chain_Delete( p_sys
->p_chain
);
1365 vlc_mutex_destroy( &p_sys
->chain_lock
);
1366 free( p_sys
->psz_chain_update
);
1368 /* Destroy all remaining subpictures */
1369 SpuHeapClean( &p_sys
->heap
);
1371 vlc_mutex_destroy( &p_sys
->lock
);
1373 vlc_object_release( p_spu
);
1377 * Attach/Detach the SPU from any input
1379 * \param p_this the object in which to destroy the subpicture unit
1380 * \param b_attach to select attach or detach
1382 void spu_Attach( spu_t
*p_spu
, vlc_object_t
*p_input
, bool b_attach
)
1386 UpdateSPU( p_spu
, p_input
);
1387 var_Create( p_input
, "highlight", VLC_VAR_BOOL
);
1388 var_AddCallback( p_input
, "highlight", CropCallback
, p_spu
);
1390 vlc_mutex_lock( &p_spu
->p
->lock
);
1391 p_spu
->p
->p_input
= p_input
;
1393 if( p_spu
->p
->p_text
)
1394 FilterRelease( p_spu
->p
->p_text
);
1395 p_spu
->p
->p_text
= SpuRenderCreateAndLoadText( p_spu
);
1397 vlc_mutex_unlock( &p_spu
->p
->lock
);
1401 vlc_mutex_lock( &p_spu
->p
->lock
);
1402 p_spu
->p
->p_input
= NULL
;
1403 vlc_mutex_unlock( &p_spu
->p
->lock
);
1405 /* Delete callbacks */
1406 var_DelCallback( p_input
, "highlight", CropCallback
, p_spu
);
1407 var_Destroy( p_input
, "highlight" );
1412 * Inform the SPU filters of mouse event
1414 int spu_ProcessMouse( spu_t
*p_spu
,
1415 const vlc_mouse_t
*p_mouse
,
1416 const video_format_t
*p_fmt
)
1418 spu_private_t
*p_sys
= p_spu
->p
;
1420 vlc_mutex_lock( &p_sys
->chain_lock
);
1421 filter_chain_MouseEvent( p_sys
->p_chain
, p_mouse
, p_fmt
);
1422 vlc_mutex_unlock( &p_sys
->chain_lock
);
1428 * Display a subpicture
1430 * Remove the reservation flag of a subpicture, which will cause it to be
1431 * ready for display.
1432 * \param p_spu the subpicture unit object
1433 * \param p_subpic the subpicture to display
1435 void spu_PutSubpicture( spu_t
*p_spu
, subpicture_t
*p_subpic
)
1437 spu_private_t
*p_sys
= p_spu
->p
;
1439 /* SPU_DEFAULT_CHANNEL always reset itself */
1440 if( p_subpic
->i_channel
== SPU_DEFAULT_CHANNEL
)
1441 spu_ClearChannel( p_spu
, SPU_DEFAULT_CHANNEL
);
1443 /* p_private is for spu only and cannot be non NULL here */
1444 for( subpicture_region_t
*r
= p_subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
1445 assert( r
->p_private
== NULL
);
1448 vlc_mutex_lock( &p_sys
->lock
);
1449 if( SpuHeapPush( &p_sys
->heap
, p_subpic
) )
1451 vlc_mutex_unlock( &p_sys
->lock
);
1452 msg_Err( p_spu
, "subpicture heap full" );
1453 subpicture_Delete( p_subpic
);
1456 vlc_mutex_unlock( &p_sys
->lock
);
1459 subpicture_t
*spu_Render( spu_t
*p_spu
,
1460 const vlc_fourcc_t
*p_chroma_list
,
1461 const video_format_t
*p_fmt_dst
,
1462 const video_format_t
*p_fmt_src
,
1463 mtime_t render_subtitle_date
,
1464 mtime_t render_osd_date
,
1465 bool b_subtitle_only
)
1467 spu_private_t
*p_sys
= p_spu
->p
;
1469 /* Update sub-source chain */
1470 vlc_mutex_lock( &p_sys
->lock
);
1471 char *psz_chain_update
= p_sys
->psz_chain_update
;
1472 p_sys
->psz_chain_update
= NULL
;
1473 vlc_mutex_unlock( &p_sys
->lock
);
1475 vlc_mutex_lock( &p_sys
->chain_lock
);
1476 if( psz_chain_update
)
1478 filter_chain_Reset( p_sys
->p_chain
, NULL
, NULL
);
1480 filter_chain_AppendFromString( p_spu
->p
->p_chain
, psz_chain_update
);
1482 free( psz_chain_update
);
1484 /* Run subpicture sources */
1485 filter_chain_SubSource( p_sys
->p_chain
, render_osd_date
);
1486 vlc_mutex_unlock( &p_sys
->chain_lock
);
1488 static const vlc_fourcc_t p_chroma_list_default_yuv
[] = {
1494 static const vlc_fourcc_t p_chroma_list_default_rgb
[] = {
1501 if( !p_chroma_list
|| *p_chroma_list
== 0 )
1502 p_chroma_list
= vlc_fourcc_IsYUV(p_fmt_dst
->i_chroma
) ? p_chroma_list_default_yuv
1503 : p_chroma_list_default_rgb
;
1505 vlc_mutex_lock( &p_sys
->lock
);
1507 unsigned int i_subpicture
;
1508 subpicture_t
*pp_subpicture
[VOUT_MAX_SUBPICTURES
];
1510 /* Get an array of subpictures to render */
1511 SpuSelectSubpictures( p_spu
, &i_subpicture
, pp_subpicture
,
1512 render_subtitle_date
, render_osd_date
, b_subtitle_only
);
1513 if( i_subpicture
<= 0 )
1515 vlc_mutex_unlock( &p_sys
->lock
);
1519 /* Updates the subpictures */
1520 for( unsigned i
= 0; i
< i_subpicture
; i
++ )
1522 subpicture_t
*p_subpic
= pp_subpicture
[i
];
1523 subpicture_Update( p_subpic
,
1524 p_fmt_src
, p_fmt_dst
,
1525 p_subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
1528 /* Now order the subpicture array
1529 * XXX The order is *really* important for overlap subtitles positionning */
1530 qsort( pp_subpicture
, i_subpicture
, sizeof(*pp_subpicture
), SubpictureCmp
);
1532 /* Render the subpictures */
1533 subpicture_t
*p_render
= SpuRenderSubpictures( p_spu
,
1534 i_subpicture
, pp_subpicture
,
1538 render_subtitle_date
,
1540 vlc_mutex_unlock( &p_sys
->lock
);
1545 void spu_OffsetSubtitleDate( spu_t
*p_spu
, mtime_t i_duration
)
1547 spu_private_t
*p_sys
= p_spu
->p
;
1549 vlc_mutex_lock( &p_sys
->lock
);
1550 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
1552 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i
];
1553 subpicture_t
*p_current
= p_entry
->p_subpicture
;
1555 if( p_current
&& p_current
->b_subtitle
)
1557 if( p_current
->i_start
> 0 )
1558 p_current
->i_start
+= i_duration
;
1559 if( p_current
->i_stop
> 0 )
1560 p_current
->i_stop
+= i_duration
;
1563 vlc_mutex_unlock( &p_sys
->lock
);
1566 int spu_RegisterChannel( spu_t
*p_spu
)
1568 spu_private_t
*p_sys
= p_spu
->p
;
1570 vlc_mutex_lock( &p_sys
->lock
);
1571 int i_channel
= p_sys
->i_channel
++;
1572 vlc_mutex_unlock( &p_sys
->lock
);
1577 void spu_ClearChannel( spu_t
*p_spu
, int i_channel
)
1579 spu_private_t
*p_sys
= p_spu
->p
;
1581 vlc_mutex_lock( &p_sys
->lock
);
1583 for( int i_subpic
= 0; i_subpic
< VOUT_MAX_SUBPICTURES
; i_subpic
++ )
1585 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i_subpic
];
1586 subpicture_t
*p_subpic
= p_entry
->p_subpicture
;
1590 if( p_subpic
->i_channel
!= i_channel
&& ( i_channel
!= -1 || p_subpic
->i_channel
== SPU_DEFAULT_CHANNEL
) )
1593 /* You cannot delete subpicture outside of spu_SortSubpictures */
1594 p_entry
->b_reject
= true;
1597 vlc_mutex_unlock( &p_sys
->lock
);
1600 void spu_ChangeSources( spu_t
*p_spu
, const char *psz_filters
)
1602 spu_private_t
*p_sys
= p_spu
->p
;
1604 vlc_mutex_lock( &p_sys
->lock
);
1606 free( p_sys
->psz_chain_update
);
1607 p_sys
->psz_chain_update
= strdup( psz_filters
);
1609 vlc_mutex_unlock( &p_sys
->lock
);
1612 void spu_ChangeMargin( spu_t
*p_spu
, int i_margin
)
1614 spu_private_t
*p_sys
= p_spu
->p
;
1616 vlc_mutex_lock( &p_sys
->lock
);
1617 p_sys
->i_margin
= i_margin
;
1618 vlc_mutex_unlock( &p_sys
->lock
);