opengl: implement subpictures renderer
[vlc.git] / modules / spu / subsdelay.c
blob8b961e7de2432006f66d387a32339179d987f88b
1 /*****************************************************************************
2 * subsdelay.c : Subsdelay plugin for vlc
3 *****************************************************************************
4 * Copyright © 2011 VideoLAN
6 * Authors: Yuval Tze <yuvaltze@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_subpicture.h>
35 #include <vlc_es.h>
36 #include <stdlib.h>
38 /*****************************************************************************
39 * Local constants
40 *****************************************************************************/
42 /* descriptions */
44 #define SUBSDELAY_HELP N_("Change subtitle delay")
46 #define MODE_TEXT N_( "Delay calculation mode" )
47 #define MODE_LONGTEXT N_( \
48 "Absolute delay - add absolute delay to each subtitle. " \
49 "Relative to source delay - multiply subtitle delay. " \
50 "Relative to source content - determine subtitle delay from its content (text)." )
52 #define FACTOR_TEXT N_( "Calculation factor" )
53 #define FACTOR_LONGTEXT N_( "Calculation factor. " \
54 "In Absolute delay mode the factor represents seconds.")
56 #define OVERLAP_TEXT N_( "Maximum overlapping subtitles" )
57 #define OVERLAP_LONGTEXT N_( "Maximum number of subtitles allowed at the same time." )
59 #define MIN_ALPHA_TEXT N_( "Minimum alpha value" )
60 #define MIN_ALPHA_LONGTEXT N_( \
61 "Alpha value of the earliest subtitle, where 0 is fully transparent and 255 is fully opaque." )
63 #define MIN_STOPS_INTERVAL_TEXT N_( "Interval between two disappearances" )
64 #define MIN_STOPS_INTERVAL_LONGTEXT N_( \
65 "Minimum time (in milliseconds) that subtitle should stay after its predecessor has disappeared " \
66 "(subtitle delay will be extended to meet this requirement)." )
68 #define MIN_STOP_START_INTERVAL_TEXT N_( "Interval between disappearance and appearance" )
69 #define MIN_STOP_START_INTERVAL_LONGTEXT N_( \
70 "Minimum time (in milliseconds) between subtitle disappearance and newer subtitle appearance " \
71 "(earlier subtitle delay will be extended to fill the gap)." )
73 #define MIN_START_STOP_INTERVAL_TEXT N_( "Interval between appearance and disappearance" )
74 #define MIN_START_STOP_INTERVAL_LONGTEXT N_( \
75 "Minimum time (in milliseconds) that subtitle should stay after newer subtitle has appeared " \
76 "(earlier subtitle delay will be shortened to avoid the overlap)." )
78 static const int pi_mode_values[] = { 0, 1, 2 };
79 static const char * const ppsz_mode_descriptions[] = { N_( "Absolute delay" ), N_( "Relative to source delay" ), N_(
80 "Relative to source content" ) };
82 /* parameters */
84 #define CFG_PREFIX "subsdelay-"
86 #define CFG_MODE CFG_PREFIX "mode"
87 #define CFG_FACTOR CFG_PREFIX "factor"
88 #define CFG_OVERLAP CFG_PREFIX "overlap"
90 #define CFG_MIN_ALPHA CFG_PREFIX "min-alpha"
91 #define CFG_MIN_STOPS_INTERVAL CFG_PREFIX "min-stops"
92 #define CFG_MIN_STOP_START_INTERVAL CFG_PREFIX "min-stop-start"
93 #define CFG_MIN_START_STOP_INTERVAL CFG_PREFIX "min-start-stop"
96 /* max subtitles handled on the heap */
97 #define SUBSDELAY_MAX_ENTRIES 16
99 #define SUBSDELAY_MODE_ABSOLUTE 0
100 #define SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY 1
101 #define SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT 2
104 /*****************************************************************************
105 * subsdelay_heap_entry_t: Heap entry
106 *****************************************************************************/
108 typedef struct subsdelay_heap_entry_t subsdelay_heap_entry_t;
110 struct subsdelay_heap_entry_t
112 subpicture_t *p_subpic; /* local subtitle */
114 subpicture_t *p_source; /* subtitle source */
116 filter_t *p_filter; /* assigned subsdelay filter */
118 subsdelay_heap_entry_t *p_next; /* next entry */
120 bool b_update_stop; /* new stop value should be calculated */
122 bool b_update_ephemer; /* actual stop value is unknown */
124 bool b_update_position; /* subtitle position should be updated */
126 bool b_check_empty; /* subtitle content should be checked */
128 vlc_tick_t i_new_stop; /* new stop value */
130 /* last region data*/
132 int i_last_region_x;
134 int i_last_region_y;
136 int i_last_region_align;
138 bool b_last_region_saved;
141 /*****************************************************************************
142 * subsdelay_heap_t: Heap
143 *****************************************************************************/
145 typedef struct
147 vlc_mutex_t lock; /* heap global lock */
149 subsdelay_heap_entry_t *p_list[SUBSDELAY_MAX_ENTRIES]; /* subtitles entries array */
151 subsdelay_heap_entry_t *p_head; /* subtitles entries linked list */
153 int i_count; /* subtitles count */
155 } subsdelay_heap_t;
159 /*****************************************************************************
160 * filter_sys_t: Subsdelay filter descriptor
161 *****************************************************************************/
163 typedef struct
165 int i_mode; /* delay calculation mode */
167 float f_factor; /* calculation factor */
169 int i_overlap; /* max overlap */
171 int i_min_alpha; /* oldest subtitle alpha value */
173 vlc_tick_t i_min_stops_interval;
175 vlc_tick_t i_min_stop_start_interval;
177 vlc_tick_t i_min_start_stop_interval;
179 subsdelay_heap_t heap; /* subpictures list */
180 } filter_sys_t;
183 /*****************************************************************************
184 * Filter functions
185 *****************************************************************************/
187 static int SubsdelayCreate( vlc_object_t * );
189 static void SubsdelayDestroy( vlc_object_t * );
191 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic );
193 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
194 void *p_data );
196 /*****************************************************************************
197 * Helper functions
198 *****************************************************************************/
200 static void SubsdelayEnforceDelayRules( filter_t *p_filter );
202 static vlc_tick_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry );
204 static void SubsdelayRecalculateDelays( filter_t *p_filter );
206 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha );
208 static int SubsdelayGetTextRank( char *psz_text );
210 static bool SubsdelayIsTextEmpty( const text_segment_t* p_segment );
212 /*****************************************************************************
213 * Subpicture functions
214 *****************************************************************************/
216 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
217 bool has_dst_changed, const video_format_t *p_fmt_dst, vlc_tick_t i_ts );
219 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
220 const video_format_t *p_fmt_dst, vlc_tick_t i_ts );
222 static void SubpicDestroyWrapper( subpicture_t *p_subpic );
224 static void SubpicLocalUpdate( subpicture_t* p_subpic, vlc_tick_t i_ts );
226 static bool SubpicIsEmpty( subpicture_t* p_subpic );
228 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater );
230 static void SubpicDestroyClone( subpicture_t *p_subpic );
232 /*****************************************************************************
233 * Heap functions
234 *****************************************************************************/
236 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap );
238 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap );
240 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter );
242 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry );
244 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap );
246 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap );
248 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap );
250 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_subpic, filter_t *p_filter );
252 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry );
254 /* heap / entries special functionality */
256 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, vlc_tick_t i_date );
258 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry );
260 /*****************************************************************************
261 * Module descriptor
262 *****************************************************************************/
264 vlc_module_begin()
265 set_shortname( N_("Subsdelay") )
266 set_description( N_("Subtitle delay") )
267 set_help( SUBSDELAY_HELP )
268 set_capability( "sub filter", 0 )
269 set_callbacks( SubsdelayCreate, SubsdelayDestroy )
270 set_category( CAT_VIDEO )
271 set_subcategory( SUBCAT_VIDEO_SUBPIC )
273 add_integer( CFG_MODE, 1, MODE_TEXT, MODE_LONGTEXT, false )
274 change_integer_list( pi_mode_values, ppsz_mode_descriptions )
276 add_float_with_range( CFG_FACTOR, 2, 0, 20, FACTOR_TEXT, FACTOR_LONGTEXT, false )
278 add_integer_with_range( CFG_OVERLAP, 3, 1, 4, OVERLAP_TEXT, OVERLAP_LONGTEXT, false )
280 add_integer_with_range( CFG_MIN_ALPHA, 70, 0, 255, MIN_ALPHA_TEXT, MIN_ALPHA_LONGTEXT, false )
282 set_section( N_("Overlap fix"), NULL )
284 add_integer( CFG_MIN_STOPS_INTERVAL, 1000, MIN_STOPS_INTERVAL_TEXT, MIN_STOPS_INTERVAL_LONGTEXT, false )
286 add_integer( CFG_MIN_START_STOP_INTERVAL, 1000, MIN_START_STOP_INTERVAL_TEXT,
287 MIN_START_STOP_INTERVAL_LONGTEXT, false )
289 add_integer( CFG_MIN_STOP_START_INTERVAL, 1000, MIN_STOP_START_INTERVAL_TEXT,
290 MIN_STOP_START_INTERVAL_LONGTEXT, false )
292 vlc_module_end ()
294 static const char * const ppsz_filter_options[] = { "mode", "factor", "overlap", NULL };
296 /*****************************************************************************
297 * SubsdelayCreate: Create subsdelay filter
298 *****************************************************************************/
299 static int SubsdelayCreate( vlc_object_t *p_this )
301 filter_t *p_filter = (filter_t *) p_this;
302 filter_sys_t *p_sys;
304 /* allocate structure */
305 p_sys = (filter_sys_t*) malloc( sizeof(filter_sys_t) );
307 if( !p_sys )
309 return VLC_ENOMEM;
312 /* init parameters */
314 p_sys->i_mode = var_CreateGetIntegerCommand( p_filter, CFG_MODE );
315 var_AddCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
317 p_sys->f_factor = var_CreateGetFloatCommand( p_filter, CFG_FACTOR );
318 var_AddCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
320 p_sys->i_overlap = var_CreateGetIntegerCommand( p_filter, CFG_OVERLAP );
321 var_AddCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
323 p_sys->i_min_alpha = var_CreateGetIntegerCommand( p_filter, CFG_MIN_ALPHA );
324 var_AddCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
326 p_sys->i_min_stops_interval
327 = VLC_TICK_FROM_MS( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOPS_INTERVAL ) );
328 var_AddCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
330 p_sys->i_min_stop_start_interval
331 = VLC_TICK_FROM_MS( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOP_START_INTERVAL ) );
332 var_AddCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
334 p_sys->i_min_start_stop_interval
335 = VLC_TICK_FROM_MS( var_CreateGetIntegerCommand( p_filter, CFG_MIN_START_STOP_INTERVAL ) );
336 var_AddCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
338 p_filter->p_sys = p_sys;
339 p_filter->pf_sub_filter = SubsdelayFilter;
341 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg );
343 SubsdelayHeapInit( &p_sys->heap );
345 return VLC_SUCCESS;
348 /*****************************************************************************
349 * SubsdelayDestroy: Destroy subsdelay filter
350 *****************************************************************************/
351 static void SubsdelayDestroy( vlc_object_t *p_this )
353 filter_t *p_filter = (filter_t *) p_this;
354 filter_sys_t *p_sys = p_filter->p_sys;
356 SubsdelayHeapDestroy( &p_sys->heap );
358 /* destroy parameters */
360 var_DelCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
361 var_Destroy( p_filter, CFG_MODE );
363 var_DelCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
364 var_Destroy( p_filter, CFG_FACTOR );
366 var_DelCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
367 var_Destroy( p_filter, CFG_OVERLAP );
369 var_DelCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
370 var_Destroy( p_filter, CFG_MIN_ALPHA );
372 var_DelCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
373 var_Destroy( p_filter, CFG_MIN_STOPS_INTERVAL );
375 var_DelCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
376 var_Destroy( p_filter, CFG_MIN_STOP_START_INTERVAL );
378 var_DelCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
379 var_Destroy( p_filter, CFG_MIN_START_STOP_INTERVAL );
381 free( p_sys );
384 /*****************************************************************************
385 * SubsdelayFilter: Filter new subpicture
386 *****************************************************************************/
387 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic )
389 subsdelay_heap_t *p_heap;
390 subsdelay_heap_entry_t *p_entry;
392 if( !p_subpic->b_subtitle )
394 return p_subpic;
397 if( SubpicIsEmpty( p_subpic ) )
399 /* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway,
400 so this subtitle can be dropped */
402 subpicture_Delete( p_subpic );
404 return NULL;
407 filter_sys_t *p_sys = p_filter->p_sys;
408 p_heap = &p_sys->heap;
410 /* add subpicture to the heap */
412 SubsdelayHeapLock( p_heap );
414 p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter );
415 if( !p_entry )
417 SubsdelayHeapUnlock( p_heap );
419 msg_Err(p_filter, "Can't add subpicture to the heap");
421 return p_subpic;
424 p_subpic = p_entry->p_subpic; /* get the local subpic */
426 if( p_subpic->b_ephemer )
428 /* set a relativly long delay in hope that the next subtitle
429 will arrive in this time and the real delay could be determined */
431 p_subpic->i_stop = p_subpic->i_start + VLC_TICK_FROM_SEC(20); /* start + 20 sec */
432 p_subpic->b_ephemer = false;
436 SubsdelayEnforceDelayRules( p_filter );
438 SubsdelayHeapUnlock( p_heap );
440 return p_subpic;
443 /*****************************************************************************
444 * SubsdelayCallback: Subsdelay parameters callback
445 *****************************************************************************/
446 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
447 void *p_data )
449 filter_sys_t *p_sys = (filter_sys_t *) p_data;
451 VLC_UNUSED( oldval );
453 SubsdelayHeapLock( &p_sys->heap );
455 if( !strcmp( psz_var, CFG_MODE ) )
457 p_sys->i_mode = newval.i_int;
459 else if( !strcmp( psz_var, CFG_FACTOR ) )
461 p_sys->f_factor = newval.f_float;
463 else if( !strcmp( psz_var, CFG_OVERLAP ) )
465 p_sys->i_overlap = newval.i_int;
467 else if( !strcmp( psz_var, CFG_MIN_ALPHA ) )
469 p_sys->i_min_alpha = newval.i_int;
471 else if( !strcmp( psz_var, CFG_MIN_STOPS_INTERVAL ) )
473 p_sys->i_min_stops_interval = VLC_TICK_FROM_MS( newval.i_int );
475 else if( !strcmp( psz_var, CFG_MIN_STOP_START_INTERVAL ) )
477 p_sys->i_min_stop_start_interval = VLC_TICK_FROM_MS( newval.i_int );
479 else if( !strcmp( psz_var, CFG_MIN_START_STOP_INTERVAL ) )
481 p_sys->i_min_start_stop_interval = VLC_TICK_FROM_MS( newval.i_int );
483 else
485 vlc_assert_unreachable();
488 SubsdelayRecalculateDelays( (filter_t *) p_this );
490 SubsdelayHeapUnlock( &p_sys->heap );
491 return VLC_SUCCESS;
494 /*****************************************************************************
495 * SubsdelayHeapInit: Initialize heap
496 *****************************************************************************/
497 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap )
499 p_heap->i_count = 0;
500 p_heap->p_head = NULL;
502 vlc_mutex_init( &p_heap->lock );
505 /*****************************************************************************
506 * SubsdelayHeapDestroy: Destroy the heap and remove its entries
507 *****************************************************************************/
508 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap )
510 SubsdelayHeapLock( p_heap );
512 for( subsdelay_heap_entry_t *p_entry = p_heap->p_head;
513 p_entry != NULL; p_entry = p_entry->p_next )
515 p_entry->p_subpic->i_stop = p_entry->p_source->i_stop;
517 p_entry->p_filter = NULL;
520 SubsdelayHeapUnlock( p_heap );
522 vlc_mutex_destroy( &p_heap->lock );
525 /*****************************************************************************
526 * SubsdelayHeapPush: Add new subpicture to the heap
527 *****************************************************************************/
528 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter )
530 subsdelay_heap_entry_t *p_last, *p_new_entry;
532 if( p_heap->i_count >= SUBSDELAY_MAX_ENTRIES )
534 return NULL; /* the heap is full */
537 p_new_entry = SubsdelayEntryCreate( p_subpic, p_filter );
539 if( !p_new_entry )
541 return NULL;
545 p_last = NULL;
547 for( subsdelay_heap_entry_t *p_entry = p_heap->p_head; p_entry != NULL;
548 p_entry = p_entry->p_next )
550 if( p_entry->p_source->i_start > p_subpic->i_start )
552 /* the new entry should be inserted before p_entry */
553 break;
556 p_last = p_entry;
559 if( p_last )
561 p_new_entry->p_next = p_last->p_next;
562 p_last->p_next = p_new_entry;
565 if( p_last->b_update_ephemer )
567 /* the correct stop value can be determined */
569 p_last->p_source->i_stop = p_new_entry->p_source->i_start;
570 p_last->b_update_ephemer = false;
573 else
575 p_new_entry->p_next = p_heap->p_head;
576 p_heap->p_head = p_new_entry;
580 /* rebuild list */
582 SubsdelayRebuildList( p_heap );
584 return p_new_entry;
587 /*****************************************************************************
588 * SubsdelayHeapRemove: Remove entry
589 *****************************************************************************/
590 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry )
592 subsdelay_heap_entry_t *p_prev;
594 p_prev = NULL;
596 for( subsdelay_heap_entry_t *p_curr = p_heap->p_head; p_curr != NULL;
597 p_curr = p_curr->p_next )
599 if( p_curr == p_entry )
601 break;
604 p_prev = p_curr;
607 if( p_prev )
609 p_prev->p_next = p_entry->p_next;
611 else
613 p_heap->p_head = p_entry->p_next;
616 p_entry->p_filter = NULL;
618 SubsdelayRebuildList( p_heap );
622 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap )
624 int i_index;
626 i_index = 0;
627 for( subsdelay_heap_entry_t *p_curr = p_heap->p_head; p_curr != NULL;
628 p_curr = p_curr->p_next )
630 p_heap->p_list[i_index] = p_curr;
631 i_index++;
634 p_heap->i_count = i_index;
637 /*****************************************************************************
638 * SubsdelayHeapLock: Lock the heap
639 *****************************************************************************/
640 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap )
642 vlc_mutex_lock( &p_heap->lock );
645 /*****************************************************************************
646 * SubsdelayHeapUnlock: Unlock the heap
647 *****************************************************************************/
648 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap )
650 vlc_mutex_unlock( &p_heap->lock );
654 /*****************************************************************************
655 * SubsdelayHeapCreateEntry: Create new entry
656 *****************************************************************************/
657 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_source, filter_t *p_filter )
659 subsdelay_heap_entry_t *p_entry;
661 subpicture_t *p_new_subpic;
663 subpicture_updater_t updater;
665 /* allocate structure */
667 p_entry = (subsdelay_heap_entry_t *) malloc( sizeof( subsdelay_heap_entry_t ) );
669 if( !p_entry )
671 return NULL;
674 /* initialize local updater */
676 updater.p_sys = p_entry;
677 updater.pf_validate = SubpicValidateWrapper;
678 updater.pf_update = SubpicUpdateWrapper;
679 updater.pf_destroy = SubpicDestroyWrapper;
681 /* create new subpic */
683 p_new_subpic = SubpicClone( p_source, &updater );
685 if( !p_new_subpic )
687 free( p_entry );
688 return NULL;
691 /* initialize entry */
693 p_entry->p_subpic = p_new_subpic;
694 p_entry->p_source = p_source;
695 p_entry->p_filter = p_filter;
696 p_entry->p_next = NULL;
697 p_entry->b_update_stop = true;
698 p_entry->b_update_ephemer = p_source->b_ephemer;
699 p_entry->b_update_position = true;
700 p_entry->b_check_empty = true;
701 p_entry->i_new_stop = p_source->i_stop;
702 p_entry->b_last_region_saved = false;
703 p_entry->i_last_region_x = 0;
704 p_entry->i_last_region_y = 0;
705 p_entry->i_last_region_align = 0;
707 return p_entry;
710 /*****************************************************************************
711 * SubsdelayEntryDestroy: Destroy entry
712 *****************************************************************************/
713 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry )
715 SubpicDestroyClone( p_entry->p_source );
716 free( p_entry );
719 /*****************************************************************************
720 * SubsdelayHeapCountOverlap: Count overlapping subtitles at a given time
721 *****************************************************************************/
722 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, vlc_tick_t i_date )
724 int i_overlaps;
726 VLC_UNUSED( p_heap );
728 i_overlaps = 0;
730 for( subsdelay_heap_entry_t *p_curr = p_entry->p_next; p_curr != NULL;
731 p_curr = p_curr->p_next )
733 if( p_curr->p_source->i_start > i_date )
735 break;
738 if( !p_curr->b_check_empty ) /* subtitle was checked, and it's not empty */
740 i_overlaps++;
744 return i_overlaps;
747 /*****************************************************************************
748 * SubsdelayEntryNewStopValueUpdated: Update source stop value after the new
749 * stop value was changed
750 *****************************************************************************/
751 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry )
753 if( !p_entry->b_update_stop )
755 p_entry->p_subpic->i_stop = p_entry->i_new_stop - VLC_TICK_FROM_MS(100); /* 0.1 sec less */
759 /*****************************************************************************
760 * SubsdelayEnforceDelayRules: Update subtitles delay after adding new
761 * subtitle or changing subtitle stop value
762 *****************************************************************************/
763 static void SubsdelayEnforceDelayRules( filter_t *p_filter )
765 subsdelay_heap_entry_t ** p_list;
766 int i_count, i_overlap;
767 vlc_tick_t i_offset;
768 vlc_tick_t i_min_stops_interval;
769 vlc_tick_t i_min_stop_start_interval;
770 vlc_tick_t i_min_start_stop_interval;
772 filter_sys_t *p_sys = p_filter->p_sys;
774 p_list = p_sys->heap.p_list;
775 i_count = p_sys->heap.i_count;
777 i_overlap = p_sys->i_overlap;
778 i_min_stops_interval = p_sys->i_min_stops_interval;
779 i_min_stop_start_interval = p_sys->i_min_stop_start_interval;
780 i_min_start_stop_interval = p_sys->i_min_start_stop_interval;
782 /* step 1 - enforce min stops interval rule (extend delays) */
784 /* look for:
785 [subtitle 1 ..............]
786 [subtitle 2 ..............]
787 |<-MinStopsInterval->|
789 * and extend newer subtitle:
790 [subtitle 1 ..............]
791 [subtitle 2 ............................]
792 |<-MinStopsInterval->|
795 for( int i = 0; i < i_count - 1; i++ )
797 p_list[i + 1]->i_new_stop = __MAX( p_list[i + 1]->i_new_stop,
798 p_list[i]->i_new_stop + i_min_stops_interval );
801 /* step 2 - enforce min stop start interval rule (extend delays) */
803 /* look for:
804 [subtitle 1 .........]
805 [subtitle 2 ....]
806 |<-MinStopStartInterval->|
808 * and fill the gap:
809 [subtitle 1 ..................]
810 [subtitle 2 ....]
811 |<-MinStopStartInterval->|
814 for( int i = 0; i < i_count; i++ )
816 for( int j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
818 i_offset = p_list[j]->p_source->i_start - p_list[i]->i_new_stop;
820 if( i_offset <= 0 )
822 continue;
825 if( i_offset < i_min_stop_start_interval )
827 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
830 break;
834 /* step 3 - enforce min start stop interval rule (shorten delays) */
836 /* look for:
837 [subtitle 1 ............]
838 [subtitle 2 ....................]
839 |<-MinStartStopInterval->|
841 * and remove the overlapping part:
842 [subtitle 1 ...]
843 [subtitle 2 ....................]
844 |<-MinStartStopInterval->|
848 for( int i = 0; i < i_count; i++ )
850 for( int j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
852 i_offset = p_list[i]->i_new_stop - p_list[j]->p_source->i_start;
854 if( i_offset <= 0 )
856 break;
859 if( i_offset < i_min_start_stop_interval )
861 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
862 break;
867 /* step 4 - enforce max overlapping rule (shorten delays)*/
869 /* look for: (overlap = 2)
870 [subtitle 1 ..............]
871 [subtitle 2 ..............]
872 [subtitle 3 ..............]
875 * and cut older subtitle:
876 [subtitle 1 .....]
877 [subtitle 2 ..............]
878 [subtitle 3 ..............]
881 for( int i = 0; i < i_count - i_overlap; i++ )
883 p_list[i]->i_new_stop = __MIN(p_list[i]->i_new_stop, p_list[i + i_overlap]->p_source->i_start);
886 /* finally - update all */
888 for( int i = 0; i < i_count; i++ )
890 SubsdelayEntryNewStopValueUpdated( p_list[i] );
894 /*****************************************************************************
895 * SubsdelayRecalculateDelays: Recalculate subtitles delay after changing
896 * one of the filter's parameters
897 *****************************************************************************/
898 static void SubsdelayRecalculateDelays( filter_t *p_filter )
900 filter_sys_t *p_sys = p_filter->p_sys;
901 for( subsdelay_heap_entry_t *p_curr = p_sys->heap.p_head;
902 p_curr != NULL; p_curr = p_curr->p_next )
904 if( !p_curr->b_update_ephemer )
906 p_curr->i_new_stop = p_curr->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_curr );
907 p_curr->b_update_stop = false;
911 SubsdelayEnforceDelayRules( p_filter );
914 /*****************************************************************************
915 * SubpicValidateWrapper: Subpicture validate callback wrapper
916 *****************************************************************************/
917 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
918 bool has_dst_changed, const video_format_t *p_fmt_dst, vlc_tick_t i_ts )
920 subsdelay_heap_entry_t *p_entry;
921 vlc_tick_t i_new_ts;
922 int i_result = VLC_SUCCESS;
924 p_entry = p_subpic->updater.p_sys;
925 if( !p_entry )
927 return VLC_SUCCESS;
930 /* call source validate */
931 if( p_entry->p_source->updater.pf_validate )
933 i_new_ts = p_entry->p_source->i_start +
934 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
935 ( p_entry->i_new_stop - p_entry->p_source->i_start );
937 i_result = p_entry->p_source->updater.pf_validate( p_entry->p_source, has_src_changed, p_fmt_src,
938 has_dst_changed, p_fmt_dst, i_new_ts );
942 p_entry->b_last_region_saved = false;
944 if( p_subpic->p_region )
946 /* save copy */
947 p_entry->i_last_region_x = p_subpic->p_region->i_x;
948 p_entry->i_last_region_y = p_subpic->p_region->i_y;
949 p_entry->i_last_region_align = p_subpic->p_region->i_align;
951 p_entry->b_last_region_saved = true;
954 if( !i_result )
956 /* subpic update isn't necessary, so local update should be called here */
957 SubpicLocalUpdate( p_subpic, i_ts );
960 return i_result;
963 /*****************************************************************************
964 * SubpicUpdateWrapper: Subpicture update callback wrapper
965 *****************************************************************************/
966 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
967 const video_format_t *p_fmt_dst, vlc_tick_t i_ts )
969 subsdelay_heap_entry_t *p_entry;
970 vlc_tick_t i_new_ts;
972 p_entry = p_subpic->updater.p_sys;
973 if( !p_entry )
975 return;
978 /* call source update */
979 if( p_entry->p_source->updater.pf_update )
981 i_new_ts = p_entry->p_source->i_start +
982 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
983 ( p_entry->i_new_stop - p_entry->p_source->i_start );
985 p_entry->p_source->p_region = p_entry->p_subpic->p_region;
987 p_entry->p_source->updater.pf_update( p_entry->p_source, p_fmt_src, p_fmt_dst, i_new_ts );
989 p_entry->p_subpic->p_region = p_entry->p_source->p_region;
992 SubpicLocalUpdate( p_subpic, i_ts );
995 /*****************************************************************************
996 * SubpicDestroyCallback: Subpicture destroy callback
997 *****************************************************************************/
998 static void SubpicDestroyWrapper( subpicture_t *p_subpic )
1000 subsdelay_heap_entry_t *p_entry;
1001 subsdelay_heap_t *p_heap;
1003 p_entry = p_subpic->updater.p_sys;
1005 if( !p_entry )
1007 return;
1010 if( p_entry->p_filter )
1012 filter_sys_t *p_sys = p_entry->p_filter->p_sys;
1013 p_heap = &p_sys->heap;
1015 SubsdelayHeapLock( p_heap );
1016 SubsdelayHeapRemove( p_heap, p_entry );
1017 SubsdelayHeapUnlock( p_heap );
1020 SubsdelayEntryDestroy( p_entry );
1023 /*****************************************************************************
1024 * SubpicLocalUpdate: rewrite some of the subpicture parameters
1025 *****************************************************************************/
1026 static void SubpicLocalUpdate( subpicture_t* p_subpic, vlc_tick_t i_ts )
1028 subsdelay_heap_entry_t *p_entry;
1029 subsdelay_heap_t *p_heap;
1030 filter_t *p_filter;
1032 int i_overlapping;
1034 p_entry = p_subpic->updater.p_sys;
1035 if( !p_entry || !p_entry->p_filter )
1037 return;
1040 p_filter = p_entry->p_filter;
1041 filter_sys_t *p_sys = p_filter->p_sys;
1042 p_heap = &p_sys->heap;
1044 SubsdelayHeapLock( p_heap );
1046 if( p_entry->b_check_empty && p_subpic->p_region )
1048 //FIXME: What if there is more than one segment?
1049 if( SubsdelayIsTextEmpty( p_subpic->p_region->p_text ) )
1051 /* remove empty subtitle */
1053 p_subpic->b_ephemer = false;
1054 p_subpic->i_stop = p_subpic->i_start;
1056 SubsdelayHeapRemove( p_heap, p_entry );
1058 SubsdelayHeapUnlock( p_heap );
1060 return;
1063 p_entry->b_check_empty = false;
1066 if( p_entry->b_update_stop && !p_entry->b_update_ephemer )
1068 p_entry->i_new_stop = p_entry->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_entry );
1069 p_entry->b_update_stop = false;
1071 SubsdelayEnforceDelayRules( p_filter );
1074 i_overlapping = SubsdelayHeapCountOverlap( p_heap, p_entry, i_ts );
1076 p_subpic->i_alpha = SubsdelayCalculateAlpha( p_filter, i_overlapping, p_entry->p_source->i_alpha );
1078 if( p_entry->b_update_position )
1080 p_subpic->b_absolute = false;
1082 if( p_subpic->p_region )
1084 p_subpic->p_region->i_x = 0;
1085 p_subpic->p_region->i_y = 10;
1086 p_subpic->p_region->i_align = ( p_subpic->p_region->i_align & ( ~SUBPICTURE_ALIGN_MASK ) )
1087 | SUBPICTURE_ALIGN_BOTTOM;
1090 p_entry->b_update_position = false;
1092 else if( p_entry->b_last_region_saved )
1094 p_subpic->b_absolute = true;
1096 if( p_subpic->p_region )
1098 p_subpic->p_region->i_x = p_entry->i_last_region_x;
1099 p_subpic->p_region->i_y = p_entry->i_last_region_y;
1100 p_subpic->p_region->i_align = p_entry->i_last_region_align;
1104 SubsdelayHeapUnlock( p_heap );
1107 /*****************************************************************************
1108 * SubpicIsEmpty: subpic region contains empty string
1109 *****************************************************************************/
1110 static bool SubpicIsEmpty( subpicture_t* p_subpic )
1112 return ( p_subpic->p_region && ( SubsdelayIsTextEmpty( p_subpic->p_region->p_text ) ) );
1115 /*****************************************************************************
1116 * SubpicClone: Clone subpicture (shallow copy)
1117 *****************************************************************************/
1118 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater )
1120 subpicture_t *p_subpic;
1121 subpicture_updater_t subpic_updater;
1122 subpicture_private_t *p_subpic_private;
1124 p_subpic = subpicture_New( updater );
1126 if( !p_subpic )
1128 return NULL;
1131 /* save private members */
1132 subpic_updater = p_subpic->updater;
1133 p_subpic_private = p_subpic->p_private;
1135 /* copy the entire struct */
1136 memcpy( p_subpic, p_source, sizeof( subpicture_t ) );
1138 /* restore private members */
1139 p_subpic->updater = subpic_updater;
1140 p_subpic->p_private = p_subpic_private;
1142 return p_subpic;
1145 /*****************************************************************************
1146 * SubpicDestroyClone: destroy cloned subpicture (shared references will not
1147 * be destroyed)
1148 *****************************************************************************/
1149 static void SubpicDestroyClone( subpicture_t *p_subpic )
1151 p_subpic->p_region = NULL; /* don't destroy region */
1152 subpicture_Delete( p_subpic );
1155 /*****************************************************************************
1156 * SubsdelayEstimateDelay: Calculate new subtitle delay according to its
1157 * content and the calculation mode
1158 *****************************************************************************/
1159 static vlc_tick_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry )
1161 int i_mode;
1162 int i_rank;
1164 filter_sys_t *p_sys = p_filter->p_sys;
1166 i_mode = p_sys->i_mode;
1168 if( i_mode == SUBSDELAY_MODE_ABSOLUTE )
1170 return ( p_entry->p_source->i_stop - p_entry->p_source->i_start + vlc_tick_from_sec( p_sys->f_factor ) );
1173 if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT )
1175 if( p_entry->p_subpic && p_entry->p_subpic->p_region && ( p_entry->p_subpic->p_region->p_text ) )
1177 //FIXME: We only use a single segment here
1178 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->p_text->psz_text );
1180 return vlc_tick_from_sec( p_sys->f_factor * i_rank );
1183 /* content is unavailable, calculation mode should be based on source delay */
1184 i_mode = SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY;
1187 if( likely(i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY) )
1189 return (vlc_tick_t)( p_sys->f_factor * ( p_entry->p_source->i_stop - p_entry->p_source->i_start ) );
1192 return VLC_TICK_FROM_SEC(10); /* 10 sec */
1195 /*****************************************************************************
1196 * SubsdelayCalculateAlpha: Calculate subtitle alpha according to source alpha
1197 * value and number of overlapping subtitles
1198 *****************************************************************************/
1199 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha )
1201 int i_new_alpha;
1202 int i_min_alpha;
1204 filter_sys_t *p_sys = p_filter->p_sys;
1206 i_min_alpha = p_sys->i_min_alpha;
1208 if( i_overlapping > p_sys->i_overlap - 1)
1210 i_overlapping = p_sys->i_overlap - 1;
1213 switch ( p_sys->i_overlap )
1215 case 1:
1216 i_new_alpha = 255;
1217 break;
1219 case 2:
1220 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha );
1221 break;
1223 case 3:
1224 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 2;
1225 break;
1227 default:
1228 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 3;
1229 break;
1232 return ( i_source_alpha * i_new_alpha ) / 255;
1235 /*****************************************************************************
1236 * SubsdelayGetWordRank: Calculate single word rank according to its length
1237 *****************************************************************************/
1238 static int SubsdelayGetWordRank( int i_length )
1240 /* p_rank[0] = p_rank[1] = p_rank[2] = 300;
1241 for( i = 3; i < 20; i++ ) p_rank[i] = (int) ( 1.1 * p_rank[i - 1] ); */
1243 static const int p_rank[20] = { 300, 300, 300, 330, 363, 399, 438, 481, 529, 581,
1244 639, 702, 772, 849, 933, 1026, 1128, 1240, 1364, 1500 };
1246 if( i_length < 1 )
1248 return 0;
1251 if( i_length > 20 )
1253 i_length = 20;
1256 return p_rank[i_length - 1];
1259 /*****************************************************************************
1260 * SubsdelayGetTextRank: Calculate text rank
1261 *****************************************************************************/
1262 static int SubsdelayGetTextRank( char *psz_text )
1264 bool b_skip_esc;
1265 bool b_skip_tag;
1266 char c;
1268 int i, i_word_length, i_rank;
1270 i = 0;
1271 i_word_length = 0;
1272 i_rank = 0;
1274 b_skip_esc = false;
1275 b_skip_tag = false;
1277 while ( psz_text[i] != '\0' )
1279 c = psz_text[i];
1280 i++;
1282 if( c == '\\' && !b_skip_esc )
1284 b_skip_esc = true;
1285 continue;
1288 if( psz_text[i] == '<' )
1290 b_skip_tag = true;
1291 continue;
1294 if( !b_skip_esc && !b_skip_tag )
1296 if( c == ' ' || c == ',' || c == '.' || c == '-' || c == '?' || c == '!' ) /* common delimiters */
1298 if( i_word_length > 0 )
1300 i_rank += SubsdelayGetWordRank( i_word_length );
1303 i_word_length = 0;
1305 else
1307 i_word_length++;
1311 b_skip_esc = false;
1313 if( c == '>' )
1315 b_skip_tag = false;
1320 if( i_word_length > 0 )
1322 i_rank += SubsdelayGetWordRank( i_word_length );
1325 return i_rank;
1328 /*****************************************************************************
1329 * SubsdelayIsTextEmpty: Check if the text contains spaces only
1330 *****************************************************************************/
1331 static bool SubsdelayIsTextEmpty( const text_segment_t *p_segment )
1333 while ( p_segment )
1335 if ( strlen( p_segment->psz_text ) > 0 )
1337 size_t offset = strspn( p_segment->psz_text, " " );
1338 if ( p_segment->psz_text[offset] )
1339 return false;
1341 p_segment = p_segment->p_next;
1344 return true;