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 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
38 #include "../libvlc.h"
39 #include "vout_internal.h"
40 #include <vlc_image.h>
45 /*****************************************************************************
47 *****************************************************************************/
49 /* Number of simultaneous subpictures */
50 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
55 subpicture_t
*p_subpicture
;
61 spu_heap_entry_t p_entry
[VOUT_MAX_SUBPICTURES
];
65 static void SpuHeapInit( spu_heap_t
* );
66 static int SpuHeapPush( spu_heap_t
*, subpicture_t
* );
67 static void SpuHeapDeleteAt( spu_heap_t
*, int i_index
);
68 static int SpuHeapDeleteSubpicture( spu_heap_t
*, subpicture_t
* );
69 static void SpuHeapClean( spu_heap_t
*p_heap
);
73 vlc_mutex_t lock
; /* lock to protect all followings fields */
77 int i_channel
; /**< number of subpicture channels registered */
78 filter_t
*p_blend
; /**< alpha blending module */
79 filter_t
*p_text
; /**< text renderer module */
80 filter_t
*p_scale_yuvp
; /**< scaling module for YUVP */
81 filter_t
*p_scale
; /**< scaling module (all but YUVP) */
82 bool b_force_crop
; /**< force cropping of subpicture */
83 int i_crop_x
, i_crop_y
, i_crop_width
, i_crop_height
; /**< cropping */
85 int i_margin
; /**< force position of a subpicture */
86 bool b_force_palette
; /**< force palette of subpicture */
87 uint8_t palette
[4][4]; /**< forced palette */
89 /* Subpiture filters */
90 char *psz_chain_update
;
91 filter_chain_t
*p_chain
;
94 mtime_t i_last_sort_date
;
98 struct subpicture_region_private_t
101 picture_t
*p_picture
;
103 static subpicture_region_private_t
*SpuRegionPrivateNew( video_format_t
* );
104 static void SpuRegionPrivateDelete( subpicture_region_private_t
* );
112 static spu_scale_t
spu_scale_create( int w
, int h
);
113 static spu_scale_t
spu_scale_unit(void );
114 static spu_scale_t
spu_scale_createq( int wn
, int wd
, int hn
, int hd
);
115 static int spu_scale_w( int v
, const spu_scale_t s
);
116 static int spu_scale_h( int v
, const spu_scale_t s
);
117 static int spu_invscale_w( int v
, const spu_scale_t s
);
118 static int spu_invscale_h( int v
, const spu_scale_t s
);
130 static spu_area_t
spu_area_create( int x
, int y
, int w
, int h
, spu_scale_t
);
131 static spu_area_t
spu_area_scaled( spu_area_t
);
132 static spu_area_t
spu_area_unscaled( spu_area_t
, spu_scale_t
);
133 static bool spu_area_overlap( spu_area_t
, spu_area_t
);
136 /* Subpicture rendered flag
137 * FIXME ? it could be moved to private ? */
138 #define SUBPICTURE_RENDERED (0x1000)
139 #if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
140 # error SUBPICTURE_RENDERED too low
143 #define SCALE_UNIT (1000)
145 static void SubpictureChain( subpicture_t
**pp_head
, subpicture_t
*p_subpic
);
146 static int SubpictureCmp( const void *s0
, const void *s1
);
148 static void SpuRenderRegion( spu_t
*,
149 picture_t
*p_pic_dst
, spu_area_t
*,
150 subpicture_t
*, subpicture_region_t
*,
151 const spu_scale_t scale_size
,
152 const video_format_t
*p_fmt
,
153 const spu_area_t
*p_subtitle_area
, int i_subtitle_area
,
154 mtime_t render_date
);
156 static void UpdateSPU ( spu_t
*, vlc_object_t
* );
157 static int CropCallback( vlc_object_t
*, char const *,
158 vlc_value_t
, vlc_value_t
, void * );
159 static int MarginCallback( vlc_object_t
*, char const *,
160 vlc_value_t
, vlc_value_t
, void * );
162 static int SpuControl( spu_t
*, int, va_list );
164 static void SpuClearChannel( spu_t
*p_spu
, int i_channel
);
166 /* Buffer allocation for SPU filter (blend, scale, ...) */
167 static subpicture_t
*spu_new_buffer( filter_t
* );
168 static void spu_del_buffer( filter_t
*, subpicture_t
* );
169 static picture_t
*spu_new_video_buffer( filter_t
* );
170 static void spu_del_video_buffer( filter_t
*, picture_t
* );
172 /* Buffer aloccation fir SUB filter */
173 static int SubFilterCallback( vlc_object_t
*, char const *,
174 vlc_value_t
, vlc_value_t
, void * );
176 static int SubFilterAllocationInit( filter_t
*, void * );
177 static void SubFilterAllocationClean( filter_t
* );
180 static void SpuRenderCreateAndLoadText( spu_t
* );
181 static void SpuRenderCreateAndLoadScale( spu_t
* );
182 static void FilterRelease( filter_t
*p_filter
);
184 /*****************************************************************************
186 *****************************************************************************/
190 * Creates the subpicture unit
192 * \param p_this the parent object which creates the subpicture unit
194 spu_t
*spu_Create( vlc_object_t
*p_this
)
197 spu_private_t
*p_sys
;
199 p_spu
= vlc_custom_create( p_this
, sizeof(spu_t
) + sizeof(spu_private_t
),
200 VLC_OBJECT_GENERIC
, "subpicture" );
203 vlc_object_attach( p_spu
, p_this
);
205 /* Initialize spu fields */
206 p_spu
->pf_control
= SpuControl
;
207 p_spu
->p
= p_sys
= (spu_private_t
*)&p_spu
[1];
209 /* Initialize private fields */
210 vlc_mutex_init( &p_sys
->lock
);
212 SpuHeapInit( &p_sys
->heap
);
214 p_sys
->p_blend
= NULL
;
215 p_sys
->p_text
= NULL
;
216 p_sys
->p_scale
= NULL
;
217 p_sys
->p_scale_yuvp
= NULL
;
219 p_sys
->i_margin
= var_InheritInteger( p_spu
, "sub-margin" );
221 /* Register the default subpicture channel */
222 p_sys
->i_channel
= 2;
224 p_sys
->psz_chain_update
= NULL
;
225 p_sys
->p_chain
= filter_chain_New( p_spu
, "sub filter", false,
226 SubFilterAllocationInit
,
227 SubFilterAllocationClean
,
230 /* Load text and scale module */
231 SpuRenderCreateAndLoadText( p_spu
);
232 SpuRenderCreateAndLoadScale( p_spu
);
235 p_sys
->i_last_sort_date
= -1;
241 * Initialise the subpicture unit
243 * \param p_spu the subpicture unit object
245 int spu_Init( spu_t
*p_spu
)
247 var_Create( p_spu
, "sub-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
248 var_AddCallback( p_spu
, "sub-filter", SubFilterCallback
, p_spu
);
249 var_TriggerCallback( p_spu
, "sub-filter" );
255 * Destroy the subpicture unit
257 * \param p_this the parent object which destroys the subpicture unit
259 void spu_Destroy( spu_t
*p_spu
)
261 spu_private_t
*p_sys
= p_spu
->p
;
263 var_DelCallback( p_spu
, "sub-filter", SubFilterCallback
, p_spu
);
266 filter_DeleteBlend( p_sys
->p_blend
);
269 FilterRelease( p_sys
->p_text
);
271 if( p_sys
->p_scale_yuvp
)
272 FilterRelease( p_sys
->p_scale_yuvp
);
275 FilterRelease( p_sys
->p_scale
);
277 filter_chain_Delete( p_sys
->p_chain
);
278 free( p_sys
->psz_chain_update
);
280 /* Destroy all remaining subpictures */
281 SpuHeapClean( &p_sys
->heap
);
283 vlc_mutex_destroy( &p_sys
->lock
);
285 vlc_object_release( p_spu
);
289 * Attach/Detach the SPU from any input
291 * \param p_this the object in which to destroy the subpicture unit
292 * \param b_attach to select attach or detach
294 void spu_Attach( spu_t
*p_spu
, vlc_object_t
*p_this
, bool b_attach
)
296 vlc_object_t
*p_input
;
298 p_input
= vlc_object_find( p_this
, VLC_OBJECT_INPUT
, FIND_PARENT
);
304 UpdateSPU( p_spu
, VLC_OBJECT(p_input
) );
305 var_Create( p_input
, "highlight", VLC_VAR_BOOL
);
306 var_AddCallback( p_input
, "highlight", CropCallback
, p_spu
);
307 var_AddCallback( p_input
, "sub-margin", MarginCallback
, p_spu
->p
);
309 vlc_mutex_lock( &p_spu
->p
->lock
);
310 p_spu
->p
->i_margin
= var_GetInteger( p_input
, "sub-margin" );
311 vlc_mutex_unlock( &p_spu
->p
->lock
);
313 vlc_object_release( p_input
);
317 /* Delete callbacks */
318 var_DelCallback( p_input
, "sub-margin", MarginCallback
, p_spu
->p
);
319 var_DelCallback( p_input
, "highlight", CropCallback
, p_spu
);
320 var_Destroy( p_input
, "highlight" );
321 vlc_object_release( p_input
);
326 * Display a subpicture
328 * Remove the reservation flag of a subpicture, which will cause it to be
330 * \param p_spu the subpicture unit object
331 * \param p_subpic the subpicture to display
333 void spu_DisplaySubpicture( spu_t
*p_spu
, subpicture_t
*p_subpic
)
335 spu_private_t
*p_sys
= p_spu
->p
;
337 /* DEFAULT_CHAN always reset itself */
338 if( p_subpic
->i_channel
== DEFAULT_CHAN
)
339 SpuClearChannel( p_spu
, DEFAULT_CHAN
);
341 /* p_private is for spu only and cannot be non NULL here */
342 for( subpicture_region_t
*r
= p_subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
343 assert( r
->p_private
== NULL
);
346 vlc_mutex_lock( &p_sys
->lock
);
347 if( SpuHeapPush( &p_sys
->heap
, p_subpic
) )
349 vlc_mutex_unlock( &p_sys
->lock
);
350 msg_Err( p_spu
, "subpicture heap full" );
351 subpicture_Delete( p_subpic
);
354 vlc_mutex_unlock( &p_sys
->lock
);
358 * This function renders all sub picture units in the list.
360 void spu_RenderSubpictures( spu_t
*p_spu
,
361 picture_t
*p_pic_dst
, const video_format_t
*p_fmt_dst
,
362 subpicture_t
*p_subpic_list
,
363 const video_format_t
*p_fmt_src
,
364 mtime_t render_subtitle_date
)
366 spu_private_t
*p_sys
= p_spu
->p
;
368 const mtime_t render_osd_date
= mdate();
370 const int i_source_video_width
= p_fmt_src
->i_width
;
371 const int i_source_video_height
= p_fmt_src
->i_height
;
373 unsigned int i_subpicture
;
374 subpicture_t
*pp_subpicture
[VOUT_MAX_SUBPICTURES
];
376 unsigned int i_subtitle_region_count
;
377 spu_area_t p_subtitle_area_buffer
[VOUT_MAX_SUBPICTURES
];
378 spu_area_t
*p_subtitle_area
;
381 vlc_mutex_lock( &p_sys
->lock
);
383 /* Preprocess subpictures */
385 i_subtitle_region_count
= 0;
386 for( subpicture_t
* p_subpic
= p_subpic_list
;
388 p_subpic
= p_subpic
->p_next
)
390 if( p_subpic
->pf_update_regions
)
392 video_format_t fmt_org
= *p_fmt_dst
;
394 fmt_org
.i_visible_width
= i_source_video_width
;
396 fmt_org
.i_visible_height
= i_source_video_height
;
398 p_subpic
->pf_update_regions( p_spu
, p_subpic
, &fmt_org
,
399 p_subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
403 if( p_subpic
->b_subtitle
)
405 for( subpicture_region_t
*r
= p_subpic
->p_region
; r
!= NULL
; r
= r
->p_next
)
406 i_subtitle_region_count
++;
410 pp_subpicture
[i_subpicture
++] = p_subpic
;
413 /* Be sure we have at least 1 picture to process */
414 if( i_subpicture
<= 0 )
416 vlc_mutex_unlock( &p_sys
->lock
);
420 /* Now order subpicture array
421 * XXX The order is *really* important for overlap subtitles positionning */
422 qsort( pp_subpicture
, i_subpicture
, sizeof(*pp_subpicture
), SubpictureCmp
);
424 /* Allocate area array for subtitle overlap */
426 p_subtitle_area
= p_subtitle_area_buffer
;
427 if( i_subtitle_region_count
> sizeof(p_subtitle_area_buffer
)/sizeof(*p_subtitle_area_buffer
) )
428 p_subtitle_area
= calloc( i_subtitle_region_count
, sizeof(*p_subtitle_area
) );
430 /* Create the blending module */
431 if( !p_sys
->p_blend
)
432 p_spu
->p
->p_blend
= filter_NewBlend( VLC_OBJECT(p_spu
), p_fmt_dst
);
434 /* Process all subpictures and regions (in the right order) */
435 for( unsigned int i_index
= 0; i_index
< i_subpicture
; i_index
++ )
437 subpicture_t
*p_subpic
= pp_subpicture
[i_index
];
438 subpicture_region_t
*p_region
;
440 if( !p_subpic
->p_region
)
443 /* FIXME when possible use a better rendering size than source size
444 * (max of display size and source size for example) FIXME */
445 int i_render_width
= p_subpic
->i_original_picture_width
;
446 int i_render_height
= p_subpic
->i_original_picture_height
;
447 if( !i_render_width
|| !i_render_height
)
449 if( i_render_width
!= 0 || i_render_height
!= 0 )
450 msg_Err( p_spu
, "unsupported original picture size %dx%d",
451 i_render_width
, i_render_height
);
453 p_subpic
->i_original_picture_width
= i_render_width
= i_source_video_width
;
454 p_subpic
->i_original_picture_height
= i_render_height
= i_source_video_height
;
459 p_sys
->p_text
->fmt_out
.video
.i_width
=
460 p_sys
->p_text
->fmt_out
.video
.i_visible_width
= i_render_width
;
462 p_sys
->p_text
->fmt_out
.video
.i_height
=
463 p_sys
->p_text
->fmt_out
.video
.i_visible_height
= i_render_height
;
466 /* Compute scaling from picture to source size */
467 spu_scale_t scale
= spu_scale_createq( i_source_video_width
, i_render_width
,
468 i_source_video_height
, i_render_height
);
470 /* Update scaling from source size to display size(p_fmt_dst) */
471 scale
.w
= scale
.w
* p_fmt_dst
->i_width
/ i_source_video_width
;
472 scale
.h
= scale
.h
* p_fmt_dst
->i_height
/ i_source_video_height
;
474 /* Set default subpicture aspect ratio
475 * FIXME if we only handle 1 aspect ratio per picture, why is it set per
477 p_region
= p_subpic
->p_region
;
478 if( !p_region
->fmt
.i_sar_num
|| !p_region
->fmt
.i_sar_den
)
480 p_region
->fmt
.i_sar_den
= p_fmt_dst
->i_sar_den
;
481 p_region
->fmt
.i_sar_num
= p_fmt_dst
->i_sar_num
;
484 /* Take care of the aspect ratio */
485 if( p_region
->fmt
.i_sar_num
* p_fmt_dst
->i_sar_den
!=
486 p_region
->fmt
.i_sar_den
* p_fmt_dst
->i_sar_num
)
488 /* FIXME FIXME what about region->i_x/i_y ? */
490 (int64_t)p_region
->fmt
.i_sar_num
* p_fmt_dst
->i_sar_den
/
491 p_region
->fmt
.i_sar_den
/ p_fmt_dst
->i_sar_num
;
494 /* Render all regions
495 * We always transform non absolute subtitle into absolute one on the
496 * first rendering to allow good subtitle overlap support.
498 for( p_region
= p_subpic
->p_region
; p_region
!= NULL
; p_region
= p_region
->p_next
)
502 /* Check scale validity */
503 if( scale
.w
<= 0 || scale
.h
<= 0 )
507 SpuRenderRegion( p_spu
, p_pic_dst
, &area
,
508 p_subpic
, p_region
, scale
, p_fmt_dst
,
509 p_subtitle_area
, i_subtitle_area
,
510 p_subpic
->b_subtitle
? render_subtitle_date
: render_osd_date
);
512 if( p_subpic
->b_subtitle
)
514 area
= spu_area_unscaled( area
, scale
);
515 if( !p_subpic
->b_absolute
&& area
.i_width
> 0 && area
.i_height
> 0 )
517 p_region
->i_x
= area
.i_x
;
518 p_region
->i_y
= area
.i_y
;
520 if( p_subtitle_area
)
521 p_subtitle_area
[i_subtitle_area
++] = area
;
524 if( p_subpic
->b_subtitle
)
525 p_subpic
->b_absolute
= true;
529 if( p_subtitle_area
!= p_subtitle_area_buffer
)
530 free( p_subtitle_area
);
532 vlc_mutex_unlock( &p_sys
->lock
);
535 /*****************************************************************************
536 * spu_SortSubpictures: find the subpictures to display
537 *****************************************************************************
538 * This function parses all subpictures and decides which ones need to be
539 * displayed. If no picture has been selected, display_date will depend on
541 * We also check for ephemer DVD subpictures (subpictures that have
542 * to be removed if a newer one is available), which makes it a lot
543 * more difficult to guess if a subpicture has to be rendered or not.
544 *****************************************************************************/
545 subpicture_t
*spu_SortSubpictures( spu_t
*p_spu
, mtime_t render_subtitle_date
,
546 bool b_subtitle_only
)
548 spu_private_t
*p_sys
= p_spu
->p
;
550 subpicture_t
*p_subpic
= NULL
;
551 const mtime_t render_osd_date
= mdate();
553 /* Update sub-filter chain */
554 vlc_mutex_lock( &p_sys
->lock
);
555 char *psz_chain_update
= p_sys
->psz_chain_update
;
556 p_sys
->psz_chain_update
= NULL
;
557 vlc_mutex_unlock( &p_sys
->lock
);
559 if( psz_chain_update
)
561 filter_chain_Reset( p_sys
->p_chain
, NULL
, NULL
);
563 filter_chain_AppendFromString( p_spu
->p
->p_chain
, psz_chain_update
);
565 free( psz_chain_update
);
568 /* Run subpicture filters */
569 filter_chain_SubFilter( p_sys
->p_chain
, render_osd_date
);
571 vlc_mutex_lock( &p_sys
->lock
);
573 /* We get an easily parsable chained list of subpictures which
574 * ends with NULL since p_subpic was initialized to NULL. */
575 for( i_channel
= 0; i_channel
< p_sys
->i_channel
; i_channel
++ )
577 subpicture_t
*p_available_subpic
[VOUT_MAX_SUBPICTURES
];
578 bool pb_available_late
[VOUT_MAX_SUBPICTURES
];
581 mtime_t start_date
= render_subtitle_date
;
582 mtime_t ephemer_subtitle_date
= 0;
583 mtime_t ephemer_osd_date
= 0;
584 int64_t i_ephemer_subtitle_order
= INT64_MIN
;
585 int64_t i_ephemer_system_order
= INT64_MIN
;
588 /* Select available pictures */
589 for( i_index
= 0; i_index
< VOUT_MAX_SUBPICTURES
; i_index
++ )
591 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i_index
];
592 subpicture_t
*p_current
= p_entry
->p_subpicture
;
596 if( !p_current
|| p_entry
->b_reject
)
598 if( p_entry
->b_reject
)
599 SpuHeapDeleteAt( &p_sys
->heap
, i_index
);
603 if( p_current
->i_channel
!= i_channel
||
604 ( b_subtitle_only
&& !p_current
->b_subtitle
) )
608 const mtime_t render_date
= p_current
->b_subtitle
? render_subtitle_date
: render_osd_date
;
610 render_date
< p_current
->i_start
)
612 /* Too early, come back next monday */
616 mtime_t
*pi_ephemer_date
= p_current
->b_subtitle
? &ephemer_subtitle_date
: &ephemer_osd_date
;
617 int64_t *pi_ephemer_order
= p_current
->b_subtitle
? &i_ephemer_subtitle_order
: &i_ephemer_system_order
;
618 if( p_current
->i_start
>= *pi_ephemer_date
)
620 *pi_ephemer_date
= p_current
->i_start
;
621 if( p_current
->i_order
> *pi_ephemer_order
)
622 *pi_ephemer_order
= p_current
->i_order
;
625 b_stop_valid
= !p_current
->b_ephemer
|| p_current
->i_stop
> p_current
->i_start
;
627 b_late
= b_stop_valid
&& p_current
->i_stop
<= render_date
;
629 /* start_date will be used for correct automatic overlap support
630 * in case picture that should not be displayed anymore (display_time)
631 * overlap with a picture to be displayed (p_current->i_start) */
632 if( p_current
->b_subtitle
&& !b_late
&& !p_current
->b_ephemer
)
633 start_date
= p_current
->i_start
;
636 p_available_subpic
[i_available
] = p_current
;
637 pb_available_late
[i_available
] = b_late
;
641 /* Only forced old picture display at the transition */
642 if( start_date
< p_sys
->i_last_sort_date
)
643 start_date
= p_sys
->i_last_sort_date
;
644 if( start_date
<= 0 )
645 start_date
= INT64_MAX
;
647 /* Select pictures to be displayed */
648 for( i_index
= 0; i_index
< i_available
; i_index
++ )
650 subpicture_t
*p_current
= p_available_subpic
[i_index
];
651 bool b_late
= pb_available_late
[i_index
];
653 const mtime_t stop_date
= p_current
->b_subtitle
? __MAX( start_date
, p_sys
->i_last_sort_date
) : render_osd_date
;
654 const mtime_t ephemer_date
= p_current
->b_subtitle
? ephemer_subtitle_date
: ephemer_osd_date
;
655 const int64_t i_ephemer_order
= p_current
->b_subtitle
? i_ephemer_subtitle_order
: i_ephemer_system_order
;
657 /* Destroy late and obsolete ephemer subpictures */
658 bool b_rejet
= b_late
&& p_current
->i_stop
<= stop_date
;
659 if( p_current
->b_ephemer
)
661 if( p_current
->i_start
< ephemer_date
)
663 else if( p_current
->i_start
== ephemer_date
&&
664 p_current
->i_order
< i_ephemer_order
)
669 SpuHeapDeleteSubpicture( &p_sys
->heap
, p_current
);
671 SubpictureChain( &p_subpic
, p_current
);
675 p_sys
->i_last_sort_date
= render_subtitle_date
;
676 vlc_mutex_unlock( &p_sys
->lock
);
681 void spu_OffsetSubtitleDate( spu_t
*p_spu
, mtime_t i_duration
)
683 spu_private_t
*p_sys
= p_spu
->p
;
685 vlc_mutex_lock( &p_sys
->lock
);
686 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
688 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i
];
689 subpicture_t
*p_current
= p_entry
->p_subpicture
;
691 if( p_current
&& p_current
->b_subtitle
)
693 if( p_current
->i_start
> 0 )
694 p_current
->i_start
+= i_duration
;
695 if( p_current
->i_stop
> 0 )
696 p_current
->i_stop
+= i_duration
;
699 vlc_mutex_unlock( &p_sys
->lock
);
702 /*****************************************************************************
703 * subpicture_t allocation
704 *****************************************************************************/
705 subpicture_t
*subpicture_New( void )
707 subpicture_t
*p_subpic
= calloc( 1, sizeof(*p_subpic
) );
711 p_subpic
->i_order
= 0;
712 p_subpic
->b_absolute
= true;
713 p_subpic
->b_fade
= false;
714 p_subpic
->b_subtitle
= false;
715 p_subpic
->i_alpha
= 0xFF;
716 p_subpic
->p_region
= NULL
;
717 p_subpic
->pf_destroy
= NULL
;
718 p_subpic
->p_sys
= NULL
;
723 void subpicture_Delete( subpicture_t
*p_subpic
)
725 subpicture_region_ChainDelete( p_subpic
->p_region
);
726 p_subpic
->p_region
= NULL
;
728 if( p_subpic
->pf_destroy
)
730 p_subpic
->pf_destroy( p_subpic
);
735 static void SubpictureChain( subpicture_t
**pp_head
, subpicture_t
*p_subpic
)
737 p_subpic
->p_next
= *pp_head
;
742 subpicture_t
*subpicture_NewFromPicture( vlc_object_t
*p_obj
,
743 picture_t
*p_picture
, vlc_fourcc_t i_chroma
)
746 video_format_t fmt_in
= p_picture
->format
;
749 video_format_t fmt_out
;
751 fmt_out
.i_chroma
= i_chroma
;
754 image_handler_t
*p_image
= image_HandlerCreate( p_obj
);
758 picture_t
*p_pip
= image_Convert( p_image
, p_picture
, &fmt_in
, &fmt_out
);
760 image_HandlerDelete( p_image
);
765 subpicture_t
*p_subpic
= subpicture_New();
768 picture_Release( p_pip
);
772 p_subpic
->i_original_picture_width
= fmt_out
.i_width
;
773 p_subpic
->i_original_picture_height
= fmt_out
.i_height
;
776 fmt_out
.i_sar_den
= 0;
778 p_subpic
->p_region
= subpicture_region_New( &fmt_out
);
779 if( p_subpic
->p_region
)
781 picture_Release( p_subpic
->p_region
->p_picture
);
782 p_subpic
->p_region
->p_picture
= p_pip
;
786 picture_Release( p_pip
);
791 /*****************************************************************************
792 * subpicture_region_t allocation
793 *****************************************************************************/
794 subpicture_region_t
*subpicture_region_New( const video_format_t
*p_fmt
)
796 subpicture_region_t
*p_region
= calloc( 1, sizeof(*p_region
) );
800 p_region
->fmt
= *p_fmt
;
801 p_region
->fmt
.p_palette
= NULL
;
802 if( p_fmt
->i_chroma
== VLC_CODEC_YUVP
)
804 p_region
->fmt
.p_palette
= calloc( 1, sizeof(*p_region
->fmt
.p_palette
) );
805 if( p_fmt
->p_palette
)
806 *p_region
->fmt
.p_palette
= *p_fmt
->p_palette
;
808 p_region
->i_alpha
= 0xff;
809 p_region
->p_next
= NULL
;
810 p_region
->p_private
= NULL
;
811 p_region
->psz_text
= NULL
;
812 p_region
->p_style
= NULL
;
813 p_region
->p_picture
= NULL
;
815 if( p_fmt
->i_chroma
== VLC_CODEC_TEXT
)
818 p_region
->p_picture
= picture_NewFromFormat( p_fmt
);
819 if( !p_region
->p_picture
)
821 free( p_region
->fmt
.p_palette
);
830 void subpicture_region_Delete( subpicture_region_t
*p_region
)
835 if( p_region
->p_private
)
836 SpuRegionPrivateDelete( p_region
->p_private
);
838 if( p_region
->p_picture
)
839 picture_Release( p_region
->p_picture
);
841 free( p_region
->fmt
.p_palette
);
843 free( p_region
->psz_text
);
844 free( p_region
->psz_html
);
845 if( p_region
->p_style
)
846 text_style_Delete( p_region
->p_style
);
851 void subpicture_region_ChainDelete( subpicture_region_t
*p_head
)
855 subpicture_region_t
*p_next
= p_head
->p_next
;
857 subpicture_region_Delete( p_head
);
865 /*****************************************************************************
867 *****************************************************************************/
868 static void SpuHeapInit( spu_heap_t
*p_heap
)
870 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
872 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
874 e
->p_subpicture
= NULL
;
879 static int SpuHeapPush( spu_heap_t
*p_heap
, subpicture_t
*p_subpic
)
881 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
883 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
885 if( e
->p_subpicture
)
888 e
->p_subpicture
= p_subpic
;
895 static void SpuHeapDeleteAt( spu_heap_t
*p_heap
, int i_index
)
897 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i_index
];
899 if( e
->p_subpicture
)
900 subpicture_Delete( e
->p_subpicture
);
902 e
->p_subpicture
= NULL
;
905 static int SpuHeapDeleteSubpicture( spu_heap_t
*p_heap
, subpicture_t
*p_subpic
)
907 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
909 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
911 if( e
->p_subpicture
!= p_subpic
)
914 SpuHeapDeleteAt( p_heap
, i
);
920 static void SpuHeapClean( spu_heap_t
*p_heap
)
922 for( int i
= 0; i
< VOUT_MAX_SUBPICTURES
; i
++ )
924 spu_heap_entry_t
*e
= &p_heap
->p_entry
[i
];
925 if( e
->p_subpicture
)
926 subpicture_Delete( e
->p_subpicture
);
930 static subpicture_region_private_t
*SpuRegionPrivateNew( video_format_t
*p_fmt
)
932 subpicture_region_private_t
*p_private
= malloc( sizeof(*p_private
) );
937 p_private
->fmt
= *p_fmt
;
938 if( p_fmt
->p_palette
)
940 p_private
->fmt
.p_palette
= malloc( sizeof(*p_private
->fmt
.p_palette
) );
941 if( p_private
->fmt
.p_palette
)
942 *p_private
->fmt
.p_palette
= *p_fmt
->p_palette
;
944 p_private
->p_picture
= NULL
;
948 static void SpuRegionPrivateDelete( subpicture_region_private_t
*p_private
)
950 if( p_private
->p_picture
)
951 picture_Release( p_private
->p_picture
);
952 free( p_private
->fmt
.p_palette
);
956 static void FilterRelease( filter_t
*p_filter
)
958 if( p_filter
->p_module
)
959 module_unneed( p_filter
, p_filter
->p_module
);
961 vlc_object_release( p_filter
);
964 static void SpuRenderCreateAndLoadText( spu_t
*p_spu
)
968 assert( !p_spu
->p
->p_text
);
971 p_text
= vlc_custom_create( p_spu
, sizeof(filter_t
),
972 VLC_OBJECT_GENERIC
, "spu text" );
976 es_format_Init( &p_text
->fmt_in
, VIDEO_ES
, 0 );
978 es_format_Init( &p_text
->fmt_out
, VIDEO_ES
, 0 );
979 p_text
->fmt_out
.video
.i_width
=
980 p_text
->fmt_out
.video
.i_visible_width
= 32;
981 p_text
->fmt_out
.video
.i_height
=
982 p_text
->fmt_out
.video
.i_visible_height
= 32;
984 p_text
->pf_sub_buffer_new
= spu_new_buffer
;
985 p_text
->pf_sub_buffer_del
= spu_del_buffer
;
987 vlc_object_attach( p_text
, p_spu
);
989 /* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
990 * same than these 2 calls ? */
991 char *psz_modulename
= var_CreateGetString( p_spu
, "text-renderer" );
992 if( psz_modulename
&& *psz_modulename
)
994 p_text
->p_module
= module_need( p_text
, "text renderer",
995 psz_modulename
, true );
997 free( psz_modulename
);
999 if( !p_text
->p_module
)
1000 p_text
->p_module
= module_need( p_text
, "text renderer", NULL
, false );
1002 /* Create a few variables used for enhanced text rendering */
1003 var_Create( p_text
, "spu-duration", VLC_VAR_TIME
);
1004 var_Create( p_text
, "spu-elapsed", VLC_VAR_TIME
);
1005 var_Create( p_text
, "text-rerender", VLC_VAR_BOOL
);
1006 var_Create( p_text
, "scale", VLC_VAR_INTEGER
);
1009 static filter_t
*CreateAndLoadScale( vlc_object_t
*p_obj
,
1010 vlc_fourcc_t i_src_chroma
, vlc_fourcc_t i_dst_chroma
,
1015 p_scale
= vlc_custom_create( p_obj
, sizeof(filter_t
),
1016 VLC_OBJECT_GENERIC
, "scale" );
1020 es_format_Init( &p_scale
->fmt_in
, VIDEO_ES
, 0 );
1021 p_scale
->fmt_in
.video
.i_chroma
= i_src_chroma
;
1022 p_scale
->fmt_in
.video
.i_width
=
1023 p_scale
->fmt_in
.video
.i_height
= 32;
1025 es_format_Init( &p_scale
->fmt_out
, VIDEO_ES
, 0 );
1026 p_scale
->fmt_out
.video
.i_chroma
= i_dst_chroma
;
1027 p_scale
->fmt_out
.video
.i_width
=
1028 p_scale
->fmt_out
.video
.i_height
= b_resize
? 16 : 32;
1030 p_scale
->pf_video_buffer_new
= spu_new_video_buffer
;
1031 p_scale
->pf_video_buffer_del
= spu_del_video_buffer
;
1033 vlc_object_attach( p_scale
, p_obj
);
1034 p_scale
->p_module
= module_need( p_scale
, "video filter2", NULL
, false );
1038 static void SpuRenderCreateAndLoadScale( spu_t
*p_spu
)
1040 assert( !p_spu
->p
->p_scale
);
1041 assert( !p_spu
->p
->p_scale_yuvp
);
1042 /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
1044 p_spu
->p
->p_scale
= CreateAndLoadScale( VLC_OBJECT(p_spu
),
1045 VLC_CODEC_YUVA
, VLC_CODEC_YUVA
, true );
1046 /* This one is used for YUVP to YUVA/RGBA without scaling
1047 * FIXME rename it */
1048 p_spu
->p
->p_scale_yuvp
= CreateAndLoadScale( VLC_OBJECT(p_spu
),
1049 VLC_CODEC_YUVP
, VLC_CODEC_YUVA
, false );
1052 static void SpuRenderText( spu_t
*p_spu
, bool *pb_rerender_text
,
1053 subpicture_t
*p_subpic
, subpicture_region_t
*p_region
,
1054 int i_min_scale_ratio
, mtime_t render_date
)
1056 filter_t
*p_text
= p_spu
->p
->p_text
;
1058 assert( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
);
1060 if( !p_text
|| !p_text
->p_module
)
1063 /* Setup 3 variables which can be used to render
1064 * time-dependent text (and effects). The first indicates
1065 * the total amount of time the text will be on screen,
1066 * the second the amount of time it has already been on
1067 * screen (can be a negative value as text is layed out
1068 * before it is rendered) and the third is a feedback
1069 * variable from the renderer - if the renderer sets it
1070 * then this particular text is time-dependent, eg. the
1071 * visual progress bar inside the text in karaoke and the
1072 * text needs to be rendered multiple times in order for
1073 * the effect to work - we therefore need to return the
1074 * region to its original state at the end of the loop,
1075 * instead of leaving it in YUVA or YUVP.
1076 * Any renderer which is unaware of how to render
1077 * time-dependent text can happily ignore the variables
1078 * and render the text the same as usual - it should at
1079 * least show up on screen, but the effect won't change
1080 * the text over time.
1082 var_SetTime( p_text
, "spu-duration", p_subpic
->i_stop
- p_subpic
->i_start
);
1083 var_SetTime( p_text
, "spu-elapsed", render_date
);
1084 var_SetBool( p_text
, "text-rerender", false );
1085 var_SetInteger( p_text
, "scale", i_min_scale_ratio
);
1087 if( p_text
->pf_render_html
&& p_region
->psz_html
)
1089 p_text
->pf_render_html( p_text
, p_region
, p_region
);
1091 else if( p_text
->pf_render_text
)
1093 p_text
->pf_render_text( p_text
, p_region
, p_region
);
1095 *pb_rerender_text
= var_GetBool( p_text
, "text-rerender" );
1098 p_region
->i_align
|= SUBPICTURE_RENDERED
;
1102 * A few scale functions helpers.
1104 static spu_scale_t
spu_scale_create( int w
, int h
)
1106 spu_scale_t s
= { .w
= w
, .h
= h
};
1113 static spu_scale_t
spu_scale_unit( void )
1115 return spu_scale_create( SCALE_UNIT
, SCALE_UNIT
);
1117 static spu_scale_t
spu_scale_createq( int wn
, int wd
, int hn
, int hd
)
1119 return spu_scale_create( wn
* SCALE_UNIT
/ wd
,
1120 hn
* SCALE_UNIT
/ hd
);
1122 static int spu_scale_w( int v
, const spu_scale_t s
)
1124 return v
* s
.w
/ SCALE_UNIT
;
1126 static int spu_scale_h( int v
, const spu_scale_t s
)
1128 return v
* s
.h
/ SCALE_UNIT
;
1130 static int spu_invscale_w( int v
, const spu_scale_t s
)
1132 return v
* SCALE_UNIT
/ s
.w
;
1134 static int spu_invscale_h( int v
, const spu_scale_t s
)
1136 return v
* SCALE_UNIT
/ s
.h
;
1140 * A few area functions helpers
1142 static spu_area_t
spu_area_create( int x
, int y
, int w
, int h
, spu_scale_t s
)
1144 spu_area_t a
= { .i_x
= x
, .i_y
= y
, .i_width
= w
, .i_height
= h
, .scale
= s
};
1147 static spu_area_t
spu_area_scaled( spu_area_t a
)
1149 if( a
.scale
.w
== SCALE_UNIT
&& a
.scale
.h
== SCALE_UNIT
)
1152 a
.i_x
= spu_scale_w( a
.i_x
, a
.scale
);
1153 a
.i_y
= spu_scale_h( a
.i_y
, a
.scale
);
1155 a
.i_width
= spu_scale_w( a
.i_width
, a
.scale
);
1156 a
.i_height
= spu_scale_h( a
.i_height
, a
.scale
);
1158 a
.scale
= spu_scale_unit();
1161 static spu_area_t
spu_area_unscaled( spu_area_t a
, spu_scale_t s
)
1163 if( a
.scale
.w
== s
.w
&& a
.scale
.h
== s
.h
)
1166 a
= spu_area_scaled( a
);
1168 a
.i_x
= spu_invscale_w( a
.i_x
, s
);
1169 a
.i_y
= spu_invscale_h( a
.i_y
, s
);
1171 a
.i_width
= spu_invscale_w( a
.i_width
, s
);
1172 a
.i_height
= spu_invscale_h( a
.i_height
, s
);
1177 static bool spu_area_overlap( spu_area_t a
, spu_area_t b
)
1182 a
= spu_area_scaled( a
);
1183 b
= spu_area_scaled( b
);
1185 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
) &&
1186 __MAX( a
.i_y
-i_dy
, b
.i_y
) < __MIN( a
.i_y
+a
.i_height
+i_dy
, b
.i_y
+b
.i_height
);
1190 * Avoid area overlapping
1192 static void SpuAreaFixOverlap( spu_area_t
*p_dst
,
1193 const spu_area_t
*p_sub
, int i_sub
, int i_align
)
1195 spu_area_t a
= spu_area_scaled( *p_dst
);
1196 bool b_moved
= false;
1199 /* Check for overlap
1200 * XXX It is not fast O(n^2) but we should not have a lot of region */
1204 for( int i
= 0; i
< i_sub
; i
++ )
1206 spu_area_t sub
= spu_area_scaled( p_sub
[i
] );
1208 if( !spu_area_overlap( a
, sub
) )
1211 if( i_align
& SUBPICTURE_ALIGN_TOP
)
1214 int i_y
= sub
.i_y
+ sub
.i_height
;
1218 else if( i_align
& SUBPICTURE_ALIGN_BOTTOM
)
1221 int i_y
= sub
.i_y
- a
.i_height
;
1227 /* TODO what to do in this case? */
1228 //fprintf( stderr, "Overlap with unsupported alignment\n" );
1238 *p_dst
= spu_area_unscaled( a
, p_dst
->scale
);
1242 static void SpuAreaFitInside( spu_area_t
*p_area
, const spu_area_t
*p_boundary
)
1244 spu_area_t a
= spu_area_scaled( *p_area
);
1246 const int i_error_x
= (a
.i_x
+ a
.i_width
) - p_boundary
->i_width
;
1252 const int i_error_y
= (a
.i_y
+ a
.i_height
) - p_boundary
->i_height
;
1258 *p_area
= spu_area_unscaled( a
, p_area
->scale
);
1264 static void SpuRegionPlace( int *pi_x
, int *pi_y
,
1265 const subpicture_t
*p_subpic
,
1266 const subpicture_region_t
*p_region
)
1268 const int i_delta_x
= p_region
->i_x
;
1269 const int i_delta_y
= p_region
->i_y
;
1272 assert( p_region
->i_x
!= INT_MAX
&& p_region
->i_y
!= INT_MAX
);
1273 if( p_region
->i_align
& SUBPICTURE_ALIGN_TOP
)
1277 else if( p_region
->i_align
& SUBPICTURE_ALIGN_BOTTOM
)
1279 i_y
= p_subpic
->i_original_picture_height
- p_region
->fmt
.i_height
- i_delta_y
;
1283 i_y
= p_subpic
->i_original_picture_height
/ 2 - p_region
->fmt
.i_height
/ 2;
1286 if( p_region
->i_align
& SUBPICTURE_ALIGN_LEFT
)
1290 else if( p_region
->i_align
& SUBPICTURE_ALIGN_RIGHT
)
1292 i_x
= p_subpic
->i_original_picture_width
- p_region
->fmt
.i_width
- i_delta_x
;
1296 i_x
= p_subpic
->i_original_picture_width
/ 2 - p_region
->fmt
.i_width
/ 2;
1299 if( p_subpic
->b_absolute
)
1305 /* Margin shifts all subpictures */
1306 /* NOTE We have margin only for subtitles, so we don't really need this here
1307 if( i_margin_y != 0 )
1308 i_y -= i_margin_y;*/
1310 /* Clamp offset to not go out of the screen (when possible) */
1311 /* NOTE Again, useful only for subtitles, otherwise goes against the alignment logic above
1312 const int i_error_x = (i_x + p_region->fmt.i_width) - p_subpic->i_original_picture_width;
1318 const int i_error_y = (i_y + p_region->fmt.i_height) - p_subpic->i_original_picture_height;
1329 * This function computes the current alpha value for a given region.
1331 static int SpuRegionAlpha( subpicture_t
*p_subpic
, subpicture_region_t
*p_region
)
1333 /* Compute alpha blend value */
1334 int i_fade_alpha
= 255;
1335 if( p_subpic
->b_fade
)
1337 mtime_t i_fade_start
= ( p_subpic
->i_stop
+
1338 p_subpic
->i_start
) / 2;
1339 mtime_t i_now
= mdate();
1341 if( i_now
>= i_fade_start
&& p_subpic
->i_stop
> i_fade_start
)
1343 i_fade_alpha
= 255 * ( p_subpic
->i_stop
- i_now
) /
1344 ( p_subpic
->i_stop
- i_fade_start
);
1347 return i_fade_alpha
* p_subpic
->i_alpha
* p_region
->i_alpha
/ 65025;
1351 * It will render the provided region onto p_pic_dst.
1354 static void SpuRenderRegion( spu_t
*p_spu
,
1355 picture_t
*p_pic_dst
, spu_area_t
*p_area
,
1356 subpicture_t
*p_subpic
, subpicture_region_t
*p_region
,
1357 const spu_scale_t scale_size
,
1358 const video_format_t
*p_fmt
,
1359 const spu_area_t
*p_subtitle_area
, int i_subtitle_area
,
1360 mtime_t render_date
)
1362 spu_private_t
*p_sys
= p_spu
->p
;
1364 video_format_t fmt_original
= p_region
->fmt
;
1365 bool b_rerender_text
= false;
1366 bool b_restore_format
= false;
1370 video_format_t region_fmt
;
1371 picture_t
*p_region_picture
;
1373 /* Invalidate area by default */
1374 *p_area
= spu_area_create( 0,0, 0,0, scale_size
);
1376 /* Render text region */
1377 if( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
)
1379 const int i_min_scale_ratio
= SCALE_UNIT
; /* FIXME what is the right value? (scale_size is not) */
1380 SpuRenderText( p_spu
, &b_rerender_text
, p_subpic
, p_region
,
1381 i_min_scale_ratio
, render_date
);
1382 b_restore_format
= b_rerender_text
;
1384 /* Check if the rendering has failed ... */
1385 if( p_region
->fmt
.i_chroma
== VLC_CODEC_TEXT
)
1389 /* Force palette if requested
1390 * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
1391 * instead of only the right one (being the dvd spu).
1393 const bool b_using_palette
= p_region
->fmt
.i_chroma
== VLC_CODEC_YUVP
;
1394 const bool b_force_palette
= b_using_palette
&& p_sys
->b_force_palette
;
1395 const bool b_force_crop
= b_force_palette
&& p_sys
->b_force_crop
;
1396 bool b_changed_palette
= false;
1399 /* Compute the margin which is expressed in destination pixel unit
1400 * The margin is applied only to subtitle and when no forced crop is
1401 * requested (dvd menu) */
1403 if( !b_force_crop
&& p_subpic
->b_subtitle
)
1404 i_margin_y
= spu_invscale_h( p_sys
->i_margin
, scale_size
);
1406 /* Place the picture
1407 * We compute the position in the rendered size */
1408 SpuRegionPlace( &i_x_offset
, &i_y_offset
,
1409 p_subpic
, p_region
);
1411 /* Save this position for subtitle overlap support
1412 * it is really important that there are given without scale_size applied */
1413 *p_area
= spu_area_create( i_x_offset
, i_y_offset
,
1414 p_region
->fmt
.i_width
, p_region
->fmt
.i_height
,
1417 /* Handle overlapping subtitles when possible */
1418 if( p_subpic
->b_subtitle
&& !p_subpic
->b_absolute
)
1420 SpuAreaFixOverlap( p_area
, p_subtitle_area
, i_subtitle_area
,
1421 p_region
->i_align
);
1424 /* we copy the area: for the subtitle overlap support we want
1425 * to only save the area without margin applied */
1426 spu_area_t restrained
= *p_area
;
1428 /* apply margin to subtitles and correct if they go over the picture edge */
1429 if( p_subpic
->b_subtitle
)
1431 restrained
.i_y
-= i_margin_y
;
1432 spu_area_t display
= spu_area_create( 0, 0, p_fmt
->i_width
, p_fmt
->i_height
,
1434 SpuAreaFitInside( &restrained
, &display
);
1437 /* Fix the position for the current scale_size */
1438 i_x_offset
= spu_scale_w( restrained
.i_x
, restrained
.scale
);
1439 i_y_offset
= spu_scale_h( restrained
.i_y
, restrained
.scale
);
1442 if( b_force_palette
)
1444 video_palette_t
*p_palette
= p_region
->fmt
.p_palette
;
1445 video_palette_t palette
;
1447 /* We suppose DVD palette here */
1448 palette
.i_entries
= 4;
1449 for( int i
= 0; i
< 4; i
++ )
1450 for( int j
= 0; j
< 4; j
++ )
1451 palette
.palette
[i
][j
] = p_sys
->palette
[i
][j
];
1453 if( p_palette
->i_entries
== palette
.i_entries
)
1455 for( int i
= 0; i
< p_palette
->i_entries
; i
++ )
1456 for( int j
= 0; j
< 4; j
++ )
1457 b_changed_palette
|= p_palette
->palette
[i
][j
] != palette
.palette
[i
][j
];
1461 b_changed_palette
= true;
1463 *p_palette
= palette
;
1467 region_fmt
= p_region
->fmt
;
1468 p_region_picture
= p_region
->p_picture
;
1471 /* Scale from rendered size to destination size */
1472 if( p_sys
->p_scale
&& p_sys
->p_scale
->p_module
&&
1473 ( !b_using_palette
|| ( p_sys
->p_scale_yuvp
&& p_sys
->p_scale_yuvp
->p_module
) ) &&
1474 ( scale_size
.w
!= SCALE_UNIT
|| scale_size
.h
!= SCALE_UNIT
|| b_using_palette
) )
1476 const unsigned i_dst_width
= spu_scale_w( p_region
->fmt
.i_width
, scale_size
);
1477 const unsigned i_dst_height
= spu_scale_h( p_region
->fmt
.i_height
, scale_size
);
1479 /* Destroy the cache if unusable */
1480 if( p_region
->p_private
)
1482 subpicture_region_private_t
*p_private
= p_region
->p_private
;
1483 bool b_changed
= false;
1485 /* Check resize changes */
1486 if( i_dst_width
!= p_private
->fmt
.i_width
||
1487 i_dst_height
!= p_private
->fmt
.i_height
)
1490 /* Check forced palette changes */
1491 if( b_changed_palette
)
1496 SpuRegionPrivateDelete( p_private
);
1497 p_region
->p_private
= NULL
;
1501 /* Scale if needed into cache */
1502 if( !p_region
->p_private
&& i_dst_width
> 0 && i_dst_height
> 0 )
1504 filter_t
*p_scale
= p_sys
->p_scale
;
1506 picture_t
*p_picture
= p_region
->p_picture
;
1507 picture_Hold( p_picture
);
1509 /* Convert YUVP to YUVA/RGBA first for better scaling quality */
1510 if( b_using_palette
)
1512 filter_t
*p_scale_yuvp
= p_sys
->p_scale_yuvp
;
1514 p_scale_yuvp
->fmt_in
.video
= p_region
->fmt
;
1516 /* TODO converting to RGBA for RGB video output is better */
1517 p_scale_yuvp
->fmt_out
.video
= p_region
->fmt
;
1518 p_scale_yuvp
->fmt_out
.video
.i_chroma
= VLC_CODEC_YUVA
;
1520 p_picture
= p_scale_yuvp
->pf_video_filter( p_scale_yuvp
, p_picture
);
1523 /* Well we will try conversion+scaling */
1524 msg_Warn( p_spu
, "%4.4s to %4.4s conversion failed",
1525 (const char*)&p_scale_yuvp
->fmt_in
.video
.i_chroma
,
1526 (const char*)&p_scale_yuvp
->fmt_out
.video
.i_chroma
);
1530 /* Conversion(except from YUVP)/Scaling */
1532 ( p_picture
->format
.i_width
!= i_dst_width
||
1533 p_picture
->format
.i_height
!= i_dst_height
) )
1535 p_scale
->fmt_in
.video
= p_picture
->format
;
1536 p_scale
->fmt_out
.video
= p_picture
->format
;
1538 p_scale
->fmt_out
.video
.i_width
= i_dst_width
;
1539 p_scale
->fmt_out
.video
.i_height
= i_dst_height
;
1541 p_scale
->fmt_out
.video
.i_visible_width
=
1542 spu_scale_w( p_region
->fmt
.i_visible_width
, scale_size
);
1543 p_scale
->fmt_out
.video
.i_visible_height
=
1544 spu_scale_h( p_region
->fmt
.i_visible_height
, scale_size
);
1546 p_picture
= p_scale
->pf_video_filter( p_scale
, p_picture
);
1548 msg_Err( p_spu
, "scaling failed" );
1554 p_region
->p_private
= SpuRegionPrivateNew( &p_picture
->format
);
1555 if( p_region
->p_private
)
1557 p_region
->p_private
->p_picture
= p_picture
;
1558 if( !p_region
->p_private
->p_picture
)
1560 SpuRegionPrivateDelete( p_region
->p_private
);
1561 p_region
->p_private
= NULL
;
1566 picture_Release( p_picture
);
1571 /* And use the scaled picture */
1572 if( p_region
->p_private
)
1574 region_fmt
= p_region
->p_private
->fmt
;
1575 p_region_picture
= p_region
->p_private
->p_picture
;
1579 /* Force cropping if requested */
1582 int i_crop_x
= spu_scale_w( p_sys
->i_crop_x
, scale_size
);
1583 int i_crop_y
= spu_scale_h( p_sys
->i_crop_y
, scale_size
);
1584 int i_crop_width
= spu_scale_w( p_sys
->i_crop_width
, scale_size
);
1585 int i_crop_height
= spu_scale_h( p_sys
->i_crop_height
,scale_size
);
1587 /* Find the intersection */
1588 if( i_crop_x
+ i_crop_width
<= i_x_offset
||
1589 i_x_offset
+ (int)region_fmt
.i_visible_width
< i_crop_x
||
1590 i_crop_y
+ i_crop_height
<= i_y_offset
||
1591 i_y_offset
+ (int)region_fmt
.i_visible_height
< i_crop_y
)
1593 /* No intersection */
1594 region_fmt
.i_visible_width
=
1595 region_fmt
.i_visible_height
= 0;
1599 int i_x
, i_y
, i_x_end
, i_y_end
;
1600 i_x
= __MAX( i_crop_x
, i_x_offset
);
1601 i_y
= __MAX( i_crop_y
, i_y_offset
);
1602 i_x_end
= __MIN( i_crop_x
+ i_crop_width
,
1603 i_x_offset
+ (int)region_fmt
.i_visible_width
);
1604 i_y_end
= __MIN( i_crop_y
+ i_crop_height
,
1605 i_y_offset
+ (int)region_fmt
.i_visible_height
);
1607 region_fmt
.i_x_offset
= i_x
- i_x_offset
;
1608 region_fmt
.i_y_offset
= i_y
- i_y_offset
;
1609 region_fmt
.i_visible_width
= i_x_end
- i_x
;
1610 region_fmt
.i_visible_height
= i_y_end
- i_y
;
1612 i_x_offset
= __MAX( i_x
, 0 );
1613 i_y_offset
= __MAX( i_y
, 0 );
1617 /* Update the blender */
1618 if( filter_ConfigureBlend( p_spu
->p
->p_blend
,
1619 p_fmt
->i_width
, p_fmt
->i_height
,
1621 filter_Blend( p_spu
->p
->p_blend
,
1622 p_pic_dst
, i_x_offset
, i_y_offset
,
1623 p_region_picture
, SpuRegionAlpha( p_subpic
, p_region
) ) )
1625 msg_Err( p_spu
, "blending %4.4s to %4.4s failed",
1626 (char *)&p_sys
->p_blend
->fmt_in
.video
.i_chroma
,
1627 (char *)&p_sys
->p_blend
->fmt_out
.video
.i_chroma
);
1631 if( b_rerender_text
)
1633 /* Some forms of subtitles need to be re-rendered more than
1634 * once, eg. karaoke. We therefore restore the region to its
1635 * pre-rendered state, so the next time through everything is
1638 if( p_region
->p_picture
)
1640 picture_Release( p_region
->p_picture
);
1641 p_region
->p_picture
= NULL
;
1643 if( p_region
->p_private
)
1645 SpuRegionPrivateDelete( p_region
->p_private
);
1646 p_region
->p_private
= NULL
;
1648 p_region
->i_align
&= ~SUBPICTURE_RENDERED
;
1650 if( b_restore_format
)
1651 p_region
->fmt
= fmt_original
;
1655 * This function compares two 64 bits integers.
1656 * It can be used by qsort.
1658 static int IntegerCmp( int64_t i0
, int64_t i1
)
1660 return i0
< i1
? -1 : i0
> i1
? 1 : 0;
1663 * This function compares 2 subpictures using the following properties
1664 * (ordered by priority)
1665 * 1. absolute positionning
1667 * 3. creation order (per channel)
1669 * It can be used by qsort.
1671 * XXX spu_RenderSubpictures depends heavily on this order.
1673 static int SubpictureCmp( const void *s0
, const void *s1
)
1675 subpicture_t
*p_subpic0
= *(subpicture_t
**)s0
;
1676 subpicture_t
*p_subpic1
= *(subpicture_t
**)s1
;
1679 r
= IntegerCmp( !p_subpic0
->b_absolute
, !p_subpic1
->b_absolute
);
1681 r
= IntegerCmp( p_subpic0
->i_start
, p_subpic1
->i_start
);
1683 r
= IntegerCmp( p_subpic0
->i_channel
, p_subpic1
->i_channel
);
1685 r
= IntegerCmp( p_subpic0
->i_order
, p_subpic1
->i_order
);
1689 /*****************************************************************************
1690 * SpuClearChannel: clear an spu channel
1691 *****************************************************************************
1692 * This function destroys the subpictures which belong to the spu channel
1693 * corresponding to i_channel_id.
1694 *****************************************************************************/
1695 static void SpuClearChannel( spu_t
*p_spu
, int i_channel
)
1697 spu_private_t
*p_sys
= p_spu
->p
;
1698 int i_subpic
; /* subpicture index */
1700 vlc_mutex_lock( &p_sys
->lock
);
1702 for( i_subpic
= 0; i_subpic
< VOUT_MAX_SUBPICTURES
; i_subpic
++ )
1704 spu_heap_entry_t
*p_entry
= &p_sys
->heap
.p_entry
[i_subpic
];
1705 subpicture_t
*p_subpic
= p_entry
->p_subpicture
;
1709 if( p_subpic
->i_channel
!= i_channel
&& ( i_channel
!= -1 || p_subpic
->i_channel
== DEFAULT_CHAN
) )
1712 /* You cannot delete subpicture outside of spu_SortSubpictures */
1713 p_entry
->b_reject
= true;
1716 vlc_mutex_unlock( &p_sys
->lock
);
1719 /*****************************************************************************
1720 * spu_ControlDefault: default methods for the subpicture unit control.
1721 *****************************************************************************/
1722 static int SpuControl( spu_t
*p_spu
, int i_query
, va_list args
)
1724 spu_private_t
*p_sys
= p_spu
->p
;
1729 case SPU_CHANNEL_REGISTER
:
1730 pi
= (int *)va_arg( args
, int * );
1731 vlc_mutex_lock( &p_sys
->lock
);
1733 *pi
= p_sys
->i_channel
++;
1734 vlc_mutex_unlock( &p_sys
->lock
);
1737 case SPU_CHANNEL_CLEAR
:
1738 i
= (int)va_arg( args
, int );
1739 SpuClearChannel( p_spu
, i
);
1743 msg_Dbg( p_spu
, "control query not supported" );
1744 return VLC_EGENERIC
;
1750 /*****************************************************************************
1751 * Object variables callbacks
1752 *****************************************************************************/
1754 /*****************************************************************************
1755 * UpdateSPU: update subpicture settings
1756 *****************************************************************************
1757 * This function is called from CropCallback and at initialization time, to
1758 * retrieve crop information from the input.
1759 *****************************************************************************/
1760 static void UpdateSPU( spu_t
*p_spu
, vlc_object_t
*p_object
)
1762 spu_private_t
*p_sys
= p_spu
->p
;
1765 vlc_mutex_lock( &p_sys
->lock
);
1767 p_sys
->b_force_palette
= false;
1768 p_sys
->b_force_crop
= false;
1770 if( var_Get( p_object
, "highlight", &val
) || !val
.b_bool
)
1772 vlc_mutex_unlock( &p_sys
->lock
);
1776 p_sys
->b_force_crop
= true;
1777 p_sys
->i_crop_x
= var_GetInteger( p_object
, "x-start" );
1778 p_sys
->i_crop_y
= var_GetInteger( p_object
, "y-start" );
1779 p_sys
->i_crop_width
= var_GetInteger( p_object
, "x-end" ) - p_sys
->i_crop_x
;
1780 p_sys
->i_crop_height
= var_GetInteger( p_object
, "y-end" ) - p_sys
->i_crop_y
;
1782 if( var_Get( p_object
, "menu-palette", &val
) == VLC_SUCCESS
)
1784 memcpy( p_sys
->palette
, val
.p_address
, 16 );
1785 p_sys
->b_force_palette
= true;
1787 vlc_mutex_unlock( &p_sys
->lock
);
1789 msg_Dbg( p_object
, "crop: %i,%i,%i,%i, palette forced: %i",
1790 p_sys
->i_crop_x
, p_sys
->i_crop_y
,
1791 p_sys
->i_crop_width
, p_sys
->i_crop_height
,
1792 p_sys
->b_force_palette
);
1795 /*****************************************************************************
1796 * CropCallback: called when the highlight properties are changed
1797 *****************************************************************************
1798 * This callback is called from the input thread when we need cropping
1799 *****************************************************************************/
1800 static int CropCallback( vlc_object_t
*p_object
, char const *psz_var
,
1801 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1803 VLC_UNUSED(oldval
); VLC_UNUSED(newval
); VLC_UNUSED(psz_var
);
1805 UpdateSPU( (spu_t
*)p_data
, p_object
);
1809 /*****************************************************************************
1810 * MarginCallback: called when requested subtitle position has changed *
1811 *****************************************************************************/
1813 static int MarginCallback( vlc_object_t
*p_object
, char const *psz_var
,
1814 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1816 VLC_UNUSED( psz_var
); VLC_UNUSED( oldval
); VLC_UNUSED( p_object
);
1817 spu_private_t
*p_sys
= ( spu_private_t
* ) p_data
;
1819 vlc_mutex_lock( &p_sys
->lock
);
1820 p_sys
->i_margin
= newval
.i_int
;
1821 vlc_mutex_unlock( &p_sys
->lock
);
1825 /*****************************************************************************
1826 * Buffers allocation callbacks for the filters
1827 *****************************************************************************/
1828 struct filter_owner_sys_t
1834 static subpicture_t
*sub_new_buffer( filter_t
*p_filter
)
1836 filter_owner_sys_t
*p_sys
= p_filter
->p_owner
;
1838 subpicture_t
*p_subpicture
= subpicture_New();
1840 p_subpicture
->i_channel
= p_sys
->i_channel
;
1841 return p_subpicture
;
1843 static void sub_del_buffer( filter_t
*p_filter
, subpicture_t
*p_subpic
)
1845 VLC_UNUSED( p_filter
);
1846 subpicture_Delete( p_subpic
);
1849 static subpicture_t
*spu_new_buffer( filter_t
*p_filter
)
1851 VLC_UNUSED(p_filter
);
1852 return subpicture_New();
1854 static void spu_del_buffer( filter_t
*p_filter
, subpicture_t
*p_subpic
)
1856 VLC_UNUSED(p_filter
);
1857 subpicture_Delete( p_subpic
);
1860 static picture_t
*spu_new_video_buffer( filter_t
*p_filter
)
1862 const video_format_t
*p_fmt
= &p_filter
->fmt_out
.video
;
1864 VLC_UNUSED(p_filter
);
1865 return picture_NewFromFormat( p_fmt
);
1867 static void spu_del_video_buffer( filter_t
*p_filter
, picture_t
*p_picture
)
1869 VLC_UNUSED(p_filter
);
1870 picture_Release( p_picture
);
1873 static int SubFilterAllocationInit( filter_t
*p_filter
, void *p_data
)
1875 spu_t
*p_spu
= p_data
;
1877 filter_owner_sys_t
*p_sys
= malloc( sizeof(filter_owner_sys_t
) );
1879 return VLC_EGENERIC
;
1881 p_filter
->pf_sub_buffer_new
= sub_new_buffer
;
1882 p_filter
->pf_sub_buffer_del
= sub_del_buffer
;
1884 p_filter
->p_owner
= p_sys
;
1885 spu_Control( p_spu
, SPU_CHANNEL_REGISTER
, &p_sys
->i_channel
);
1886 p_sys
->p_spu
= p_spu
;
1891 static void SubFilterAllocationClean( filter_t
*p_filter
)
1893 filter_owner_sys_t
*p_sys
= p_filter
->p_owner
;
1895 SpuClearChannel( p_sys
->p_spu
, p_sys
->i_channel
);
1896 free( p_filter
->p_owner
);
1899 static int SubFilterCallback( vlc_object_t
*p_object
, char const *psz_var
,
1900 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1902 spu_t
*p_spu
= p_data
;
1903 spu_private_t
*p_sys
= p_spu
->p
;
1905 VLC_UNUSED(p_object
); VLC_UNUSED(oldval
); VLC_UNUSED(psz_var
);
1907 vlc_mutex_lock( &p_sys
->lock
);
1909 free( p_sys
->psz_chain_update
);
1910 p_sys
->psz_chain_update
= strdup( newval
.psz_string
);
1912 vlc_mutex_unlock( &p_sys
->lock
);