DVDnav: fix .ifo files opening
[vlc/vlc-skelet.git] / modules / video_filter / subsdelay.c
blobf286980ef366e7b02126896c4a41a325c4d455b0
1 /*****************************************************************************
2 * sepia.c : Sepia video plugin for vlc
3 *****************************************************************************
4 * Copyright © 2011 VideoLAN
5 * $Id$
7 * Authors: Yuval Tze <yuvaltze@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35 #include <vlc_subpicture.h>
36 #include <vlc_osd.h>
37 #include <vlc_es.h>
38 #include <stdlib.h>
40 /*****************************************************************************
41 * Local constants
42 *****************************************************************************/
44 /* descriptions */
46 #define SUBSDELAY_HELP N_("Change subtitles delay")
48 #define MODE_TEXT N_( "Delay calculation mode" )
49 #define MODE_LONGTEXT N_( \
50 "Absolute delay - add absolute delay to each subtitle. " \
51 "Relative to source delay - multiply subtitle delay. " \
52 "Relative to source content - determine subtitle delay from its content (text)." )
54 #define FACTOR_TEXT N_( "Calculation factor" )
55 #define FACTOR_LONGTEXT N_( "Calculation factor. " \
56 "In Absolute delay mode the factor represents seconds.")
58 #define OVERLAP_TEXT N_( "Maximum overlapping subtitles" )
59 #define OVERLAP_LONGTEXT N_( "Maximum number of subtitles allowed at the same time." )
61 #define MIN_ALPHA_TEXT N_( "Minimum alpha value" )
62 #define MIN_ALPHA_LONGTEXT N_( \
63 "Alpha value of the earliest subtitle, where 0 is fully transparent and 255 is fully opaque." )
65 #define MIN_STOPS_INTERVAL_TEXT N_( "Interval between two disappearances" )
66 #define MIN_STOPS_INTERVAL_LONGTEXT N_( \
67 "Minimum time (in milliseconds) that subtitle should stay after its predecessor has disappeared " \
68 "(subtitle delay will be extended to meet this requirement)." )
70 #define MIN_STOP_START_INTERVAL_TEXT N_( "Interval between disappearance and appearance" )
71 #define MIN_STOP_START_INTERVAL_LONGTEXT N_( \
72 "Minimum time (in milliseconds) between subtitle disappearance and newer subtitle appearance " \
73 "(earlier subtitle delay will be extended to fill the gap)." )
75 #define MIN_START_STOP_INTERVAL_TEXT N_( "Interval between appearance and disappearance" )
76 #define MIN_START_STOP_INTERVAL_LONGTEXT N_( \
77 "Minimum time (in milliseconds) that subtitle should stay after newer subtitle has appeared " \
78 "(earlier subtitle delay will be shortened to avoid the overlap)." )
80 static const int pi_mode_values[] = { 0, 1, 2 };
81 static const char * const ppsz_mode_descriptions[] = { N_( "Absolute delay" ), N_( "Relative to source delay" ), N_(
82 "Relative to source content" ) };
84 /* parameters */
86 #define CFG_PREFIX "subsdelay-"
88 #define CFG_MODE CFG_PREFIX "mode"
89 #define CFG_FACTOR CFG_PREFIX "factor"
90 #define CFG_OVERLAP CFG_PREFIX "overlap"
92 #define CFG_MIN_ALPHA CFG_PREFIX "min-alpha"
93 #define CFG_MIN_STOPS_INTERVAL CFG_PREFIX "min-stops"
94 #define CFG_MIN_STOP_START_INTERVAL CFG_PREFIX "min-stop-start"
95 #define CFG_MIN_START_STOP_INTERVAL CFG_PREFIX "min-start-stop"
98 /* max subtitles handled on the heap */
99 #define SUBSDELAY_MAX_ENTRIES 16
101 /* factor convert macros */
102 #define INT_FACTOR_BASE 1000
103 #define FLOAT_FACTOR_TO_INT_FACTOR( x ) (int)( ( x ) * INT_FACTOR_BASE )
104 #define INT_FACTOR_TO_MICROSEC( x ) ( ( x ) * ( 1000000 / INT_FACTOR_BASE ) )
105 #define INT_FACTOR_TO_RANK_FACTOR( x ) ( x )
106 #define MILLISEC_TO_MICROSEC( x ) ( ( x ) * 1000 )
109 #define SUBSDELAY_MODE_ABSOLUTE 0
110 #define SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY 1
111 #define SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT 2
114 /*****************************************************************************
115 * subsdelay_heap_entry_t: Heap entry
116 *****************************************************************************/
118 typedef subpicture_updater_sys_t subsdelay_heap_entry_t;
120 struct subpicture_updater_sys_t
122 subpicture_t *p_subpic; /* local subtitle */
124 subpicture_t *p_source; /* subtitle source */
126 filter_t *p_filter; /* assigned subsdelay filter */
128 subsdelay_heap_entry_t *p_next; /* next entry */
130 bool b_update_stop; /* new stop value should be calculated */
132 bool b_update_ephemer; /* actual stop value is unknown */
134 bool b_update_position; /* subtitle position should be updated */
136 bool b_check_empty; /* subtitle content should be checked */
138 mtime_t i_new_stop; /* new stop value */
140 /* last region data*/
142 int i_last_region_x;
144 int i_last_region_y;
146 int i_last_region_align;
148 bool b_last_region_saved;
151 /*****************************************************************************
152 * subsdelay_heap_t: Heap
153 *****************************************************************************/
155 typedef struct
157 vlc_mutex_t lock; /* heap global lock */
159 subsdelay_heap_entry_t *p_list[SUBSDELAY_MAX_ENTRIES]; /* subtitles entries array */
161 subsdelay_heap_entry_t *p_head; /* subtitles entries linked list */
163 int i_count; /* subtitles count */
165 } subsdelay_heap_t;
169 /*****************************************************************************
170 * filter_sys_t: Subsdelay filter descriptor
171 *****************************************************************************/
173 struct filter_sys_t
175 int i_mode; /* delay calculation mode */
177 int i_factor; /* calculation factor */
179 int i_overlap; /* max overlap */
181 int i_min_alpha; /* oldest subtitle alpha value */
183 int64_t i_min_stops_interval;
185 int64_t i_min_stop_start_interval;
187 int64_t i_min_start_stop_interval;
189 subsdelay_heap_t heap; /* subpictures list */
193 /*****************************************************************************
194 * Filter functions
195 *****************************************************************************/
197 static int SubsdelayCreate( vlc_object_t * );
199 static void SubsdelayDestroy( vlc_object_t * );
201 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic );
203 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
204 void *p_data );
206 /*****************************************************************************
207 * Helper functions
208 *****************************************************************************/
210 static void SubsdelayEnforceDelayRules( filter_t *p_filter );
212 static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry );
214 static void SubsdelayRecalculateDelays( filter_t *p_filter );
216 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha );
218 static int SubsdelayGetTextRank( char *psz_text );
220 static bool SubsdelayIsTextEmpty( char *psz_text );
222 /*****************************************************************************
223 * Subpicture functions
224 *****************************************************************************/
226 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
227 bool has_dst_changed, const video_format_t *p_fmt_dst, mtime_t i_ts );
229 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
230 const video_format_t *p_fmt_dst, mtime_t i_ts );
232 static void SubpicDestroyWrapper( subpicture_t *p_subpic );
234 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts );
236 static bool SubpicIsEmpty( subpicture_t* p_subpic );
238 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater );
240 static void SubpicDestroyClone( subpicture_t *p_subpic );
242 /*****************************************************************************
243 * Heap functions
244 *****************************************************************************/
246 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap );
248 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap );
250 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter );
252 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry );
254 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap );
256 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap );
258 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap );
260 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_subpic, filter_t *p_filter );
262 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry );
264 /* heap / entries special functionality */
266 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date );
268 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry );
270 /*****************************************************************************
271 * Module descriptor
272 *****************************************************************************/
274 vlc_module_begin()
275 set_shortname( _("Subsdelay") )
276 set_description( _("Subtitles delay") )
277 set_help( SUBSDELAY_HELP )
278 set_capability( "sub filter", 0 )
279 set_callbacks( SubsdelayCreate, SubsdelayDestroy )
280 set_category( CAT_VIDEO )
281 set_subcategory( SUBCAT_VIDEO_SUBPIC )
283 add_integer( CFG_MODE, 1, MODE_TEXT, MODE_LONGTEXT, false )
284 change_integer_list( pi_mode_values, ppsz_mode_descriptions )
286 add_float_with_range( CFG_FACTOR, 2, 0, 20, NULL, FACTOR_TEXT, FACTOR_LONGTEXT, false )
288 add_integer_with_range( CFG_OVERLAP, 3, 1, 4, NULL, OVERLAP_TEXT, OVERLAP_LONGTEXT, false )
290 add_integer_with_range( CFG_MIN_ALPHA, 125, 0, 255, NULL, MIN_ALPHA_TEXT, MIN_ALPHA_LONGTEXT, false )
292 set_section( N_("Overlap fix"), NULL )
294 add_integer( CFG_MIN_STOPS_INTERVAL, 1000, MIN_STOPS_INTERVAL_TEXT, MIN_STOPS_INTERVAL_LONGTEXT, false )
296 add_integer( CFG_MIN_START_STOP_INTERVAL, 1000, MIN_START_STOP_INTERVAL_TEXT,
297 MIN_START_STOP_INTERVAL_LONGTEXT, false )
299 add_integer( CFG_MIN_STOP_START_INTERVAL, 1000, MIN_STOP_START_INTERVAL_TEXT,
300 MIN_STOP_START_INTERVAL_LONGTEXT, false )
302 vlc_module_end ()
304 static const char * const ppsz_filter_options[] = { "mode", "factor", "overlap", NULL };
306 /*****************************************************************************
307 * SubsdelayCreate: Create subsdelay filter
308 *****************************************************************************/
309 static int SubsdelayCreate( vlc_object_t *p_this )
311 filter_t *p_filter = (filter_t *) p_this;
312 filter_sys_t *p_sys;
314 /* allocate structure */
315 p_sys = (filter_sys_t*) malloc( sizeof(filter_sys_t) );
317 if( !p_sys )
319 return VLC_ENOMEM;
322 /* init parameters */
324 p_sys->i_mode = var_CreateGetIntegerCommand( p_filter, CFG_MODE );
325 var_AddCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
327 p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( var_CreateGetFloatCommand( p_filter, CFG_FACTOR ) );
328 var_AddCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
330 p_sys->i_overlap = var_CreateGetIntegerCommand( p_filter, CFG_OVERLAP );
331 var_AddCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
333 p_sys->i_min_alpha = var_CreateGetIntegerCommand( p_filter, CFG_MIN_ALPHA );
334 var_AddCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
336 p_sys->i_min_stops_interval
337 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOPS_INTERVAL ) );
338 var_AddCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
340 p_sys->i_min_stop_start_interval
341 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOP_START_INTERVAL ) );
342 var_AddCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
344 p_sys->i_min_start_stop_interval
345 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_START_STOP_INTERVAL ) );
346 var_AddCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
348 p_filter->p_sys = p_sys;
349 p_filter->pf_sub_filter = SubsdelayFilter;
351 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg );
353 SubsdelayHeapInit( &p_sys->heap );
355 return VLC_SUCCESS;
358 /*****************************************************************************
359 * SubsdelayDestroy: Destroy subsdelay filter
360 *****************************************************************************/
361 static void SubsdelayDestroy( vlc_object_t *p_this )
363 filter_t *p_filter = (filter_t *) p_this;
364 filter_sys_t *p_sys = p_filter->p_sys;
366 SubsdelayHeapDestroy( &p_sys->heap );
368 /* destroy parameters */
370 var_DelCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
371 var_Destroy( p_filter, CFG_MODE );
373 var_DelCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
374 var_Destroy( p_filter, CFG_FACTOR );
376 var_DelCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
377 var_Destroy( p_filter, CFG_OVERLAP );
379 var_DelCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
380 var_Destroy( p_filter, CFG_MIN_ALPHA );
382 var_DelCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
383 var_Destroy( p_filter, CFG_MIN_STOPS_INTERVAL );
385 var_DelCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
386 var_Destroy( p_filter, CFG_MIN_STOP_START_INTERVAL );
388 var_DelCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
389 var_Destroy( p_filter, CFG_MIN_START_STOP_INTERVAL );
391 free( p_sys );
394 /*****************************************************************************
395 * SubsdelayFilter: Filter new subpicture
396 *****************************************************************************/
397 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic )
399 subsdelay_heap_t *p_heap;
400 subsdelay_heap_entry_t *p_entry;
402 if( !p_subpic->b_subtitle )
404 return p_subpic;
407 if( SubpicIsEmpty( p_subpic ) )
409 /* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway,
410 so this subtitle can be dropped */
412 subpicture_Delete( p_subpic );
414 return NULL;
417 p_heap = &p_filter->p_sys->heap;
419 /* add subpicture to the heap */
421 SubsdelayHeapLock( p_heap );
423 p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter );
424 if( !p_entry )
426 SubsdelayHeapUnlock( p_heap );
428 msg_Err(p_filter, "Can't add subpicture to the heap");
430 return p_subpic;
433 p_subpic = p_entry->p_subpic; /* get the local subpic */
435 if( p_subpic->b_ephemer )
437 /* set a relativly long delay in hope that the next subtitle
438 will arrive in this time and the real delay could be determined */
440 p_subpic->i_stop = p_subpic->i_start + 20000000; /* start + 20 sec */
441 p_subpic->b_ephemer = false;
445 SubsdelayEnforceDelayRules( p_filter );
447 SubsdelayHeapUnlock( p_heap );
449 return p_subpic;
452 /*****************************************************************************
453 * SubsdelayCallback: Subsdelay parameters callback
454 *****************************************************************************/
455 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
456 void *p_data )
458 filter_sys_t *p_sys = (filter_sys_t *) p_data;
460 VLC_UNUSED( oldval );
462 SubsdelayHeapLock( &p_sys->heap );
464 if( !strcmp( psz_var, CFG_MODE ) )
466 p_sys->i_mode = newval.i_int;
468 else if( !strcmp( psz_var, CFG_FACTOR ) )
470 p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( newval.f_float );
472 else if( !strcmp( psz_var, CFG_OVERLAP ) )
474 p_sys->i_overlap = newval.i_int;
476 else if( !strcmp( psz_var, CFG_MIN_ALPHA ) )
478 p_sys->i_min_alpha = newval.i_int;
480 else if( !strcmp( psz_var, CFG_MIN_STOPS_INTERVAL ) )
482 p_sys->i_min_stops_interval = MILLISEC_TO_MICROSEC( newval.i_int );
484 else if( !strcmp( psz_var, CFG_MIN_STOP_START_INTERVAL ) )
486 p_sys->i_min_stop_start_interval = MILLISEC_TO_MICROSEC( newval.i_int );
488 else if( !strcmp( psz_var, CFG_MIN_START_STOP_INTERVAL ) )
490 p_sys->i_min_start_stop_interval = MILLISEC_TO_MICROSEC( newval.i_int );
492 else
494 SubsdelayHeapUnlock( &p_sys->heap );
495 return VLC_ENOVAR;
498 SubsdelayRecalculateDelays( (filter_t *) p_this );
500 SubsdelayHeapUnlock( &p_sys->heap );
501 return VLC_SUCCESS;
504 /*****************************************************************************
505 * SubsdelayHeapInit: Initialize heap
506 *****************************************************************************/
507 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap )
509 p_heap->i_count = 0;
510 p_heap->p_head = NULL;
512 vlc_mutex_init( &p_heap->lock );
515 /*****************************************************************************
516 * SubsdelayHeapDestroy: Destroy the heap and remove its entries
517 *****************************************************************************/
518 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap )
520 subsdelay_heap_entry_t *p_entry;
522 SubsdelayHeapLock( p_heap );
524 for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
526 p_entry->p_subpic->i_stop = p_entry->p_source->i_stop;
528 p_entry->p_filter = NULL;
531 SubsdelayHeapUnlock( p_heap );
533 vlc_mutex_destroy( &p_heap->lock );
536 /*****************************************************************************
537 * SubsdelayHeapPush: Add new subpicture to the heap
538 *****************************************************************************/
539 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter )
541 subsdelay_heap_entry_t *p_entry, *p_last, *p_new_entry;
543 if( p_heap->i_count >= SUBSDELAY_MAX_ENTRIES )
545 return NULL; /* the heap is full */
548 p_new_entry = SubsdelayEntryCreate( p_subpic, p_filter );
550 if( !p_new_entry )
552 return NULL;
556 p_last = NULL;
558 for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
560 if( p_entry->p_source->i_start > p_subpic->i_start )
562 /* the new entry should be inserted before p_entry */
563 break;
566 p_last = p_entry;
569 if( p_last )
571 p_new_entry->p_next = p_last->p_next;
572 p_last->p_next = p_new_entry;
575 if( p_last->b_update_ephemer )
577 /* the correct stop value can be determined */
579 p_last->p_source->i_stop = p_new_entry->p_source->i_start;
580 p_last->b_update_ephemer = false;
583 else
585 p_new_entry->p_next = p_heap->p_head;
586 p_heap->p_head = p_new_entry;
590 /* rebuild list */
592 SubsdelayRebuildList( p_heap );
594 return p_new_entry;
597 /*****************************************************************************
598 * SubsdelayHeapRemove: Remove entry
599 *****************************************************************************/
600 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry )
602 subsdelay_heap_entry_t *p_curr, *p_prev;
604 p_prev = NULL;
606 for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
608 if( p_curr == p_entry )
610 break;
613 p_prev = p_curr;
616 if( p_prev )
618 p_prev->p_next = p_entry->p_next;
620 else
622 p_heap->p_head = p_entry->p_next;
625 p_entry->p_filter = NULL;
627 SubsdelayRebuildList( p_heap );
631 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap )
633 subsdelay_heap_entry_t *p_curr;
634 int i_index;
636 i_index = 0;
637 for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
639 p_heap->p_list[i_index] = p_curr;
640 i_index++;
643 p_heap->i_count = i_index;
646 /*****************************************************************************
647 * SubsdelayHeapLock: Lock the heap
648 *****************************************************************************/
649 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap )
651 vlc_mutex_lock( &p_heap->lock );
654 /*****************************************************************************
655 * SubsdelayHeapUnlock: Unlock the heap
656 *****************************************************************************/
657 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap )
659 vlc_mutex_unlock( &p_heap->lock );
663 /*****************************************************************************
664 * SubsdelayHeapCreateEntry: Create new entry
665 *****************************************************************************/
666 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_source, filter_t *p_filter )
668 subsdelay_heap_entry_t *p_entry;
670 subpicture_t *p_new_subpic;
672 subpicture_updater_t updater;
674 /* allocate structure */
676 p_entry = (subsdelay_heap_entry_t *) malloc( sizeof( subsdelay_heap_entry_t ) );
678 if( !p_entry )
680 return NULL;
683 /* initialize local updater */
685 updater.p_sys = p_entry;
686 updater.pf_validate = SubpicValidateWrapper;
687 updater.pf_update = SubpicUpdateWrapper;
688 updater.pf_destroy = SubpicDestroyWrapper;
690 /* create new subpic */
692 p_new_subpic = SubpicClone( p_source, &updater );
694 if( !p_new_subpic )
696 free( p_entry );
697 return NULL;
700 /* initialize entry */
702 p_entry->p_subpic = p_new_subpic;
703 p_entry->p_source = p_source;
704 p_entry->p_filter = p_filter;
705 p_entry->p_next = NULL;
706 p_entry->b_update_stop = true;
707 p_entry->b_update_ephemer = p_source->b_ephemer;
708 p_entry->b_update_position = true;
709 p_entry->b_check_empty = true;
710 p_entry->i_new_stop = p_source->i_stop;
711 p_entry->b_last_region_saved = false;
712 p_entry->i_last_region_x = 0;
713 p_entry->i_last_region_y = 0;
714 p_entry->i_last_region_align = 0;
716 return p_entry;
719 /*****************************************************************************
720 * SubsdelayEntryDestroy: Destroy entry
721 *****************************************************************************/
722 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry )
724 SubpicDestroyClone( p_entry->p_source );
725 free( p_entry );
728 /*****************************************************************************
729 * SubsdelayHeapCountOverlap: Count overlapping subtitles at a given time
730 *****************************************************************************/
731 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date )
733 subsdelay_heap_entry_t *p_curr;
734 int i_overlaps;
736 VLC_UNUSED( p_heap );
738 i_overlaps = 0;
740 for( p_curr = p_entry->p_next; p_curr != NULL; p_curr = p_curr->p_next )
742 if( p_curr->p_source->i_start > i_date )
744 break;
747 if( !p_curr->b_check_empty ) /* subtitle was checked, and it's not empty */
749 i_overlaps++;
753 return i_overlaps;
756 /*****************************************************************************
757 * SubsdelayEntryNewStopValueUpdated: Update source stop value after the new
758 * stop value was changed
759 *****************************************************************************/
760 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry )
762 if( !p_entry->b_update_stop )
764 p_entry->p_subpic->i_stop = p_entry->i_new_stop - 100000; /* 0.1 sec less */
768 /*****************************************************************************
769 * SubsdelayEnforceDelayRules: Update subtitles delay after adding new
770 * subtitle or changing subtitle stop value
771 *****************************************************************************/
772 static void SubsdelayEnforceDelayRules( filter_t *p_filter )
774 subsdelay_heap_entry_t ** p_list;
775 int i, j, i_count, i_overlap;
776 int64_t i_offset;
777 int64_t i_min_stops_interval;
778 int64_t i_min_stop_start_interval;
779 int64_t i_min_start_stop_interval;
781 p_list = p_filter->p_sys->heap.p_list;
782 i_count = p_filter->p_sys->heap.i_count;
784 i_overlap = p_filter->p_sys->i_overlap;
785 i_min_stops_interval = p_filter->p_sys->i_min_stops_interval;
786 i_min_stop_start_interval = p_filter->p_sys->i_min_stop_start_interval;
787 i_min_start_stop_interval = p_filter->p_sys->i_min_start_stop_interval;
789 /* step 1 - enforce min stops interval rule (extend delays) */
791 /* look for:
792 [subtitle 1 ..............]
793 [subtitle 2 ..............]
794 |<-MinStopsInterval->|
796 * and extend newer subtitle:
797 [subtitle 1 ..............]
798 [subtitle 2 ............................]
799 |<-MinStopsInterval->|
802 for( i = 0; i < i_count - 1; i++ )
804 p_list[i + 1]->i_new_stop = __MAX( p_list[i + 1]->i_new_stop,
805 p_list[i]->i_new_stop + i_min_stops_interval );
808 /* step 2 - enforce min stop start interval rule (extend delays) */
810 /* look for:
811 [subtitle 1 .........]
812 [subtitle 2 ....]
813 |<-MinStopStartInterval->|
815 * and fill the gap:
816 [subtitle 1 ..................]
817 [subtitle 2 ....]
818 |<-MinStopStartInterval->|
821 for( i = 0; i < i_count; i++ )
823 for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
825 i_offset = p_list[j]->p_source->i_start - p_list[i]->i_new_stop;
827 if( i_offset <= 0 )
829 continue;
832 if( i_offset < i_min_stop_start_interval )
834 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
837 break;
841 /* step 3 - enforce min start stop interval rule (shorten delays) */
843 /* look for:
844 [subtitle 1 ............]
845 [subtitle 2 ....................]
846 |<-MinStartStopInterval->|
848 * and remove the overlapping part:
849 [subtitle 1 ...]
850 [subtitle 2 ....................]
851 |<-MinStartStopInterval->|
855 for( i = 0; i < i_count; i++ )
857 for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
859 i_offset = p_list[i]->i_new_stop - p_list[j]->p_source->i_start;
861 if( i_offset <= 0 )
863 break;
866 if( i_offset < i_min_start_stop_interval )
868 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
869 break;
874 /* step 4 - enforce max overlapping rule (shorten delays)*/
876 /* look for: (overlap = 2)
877 [subtitle 1 ..............]
878 [subtitle 2 ..............]
879 [subtitle 3 ..............]
882 * and cut older subtitle:
883 [subtitle 1 .....]
884 [subtitle 2 ..............]
885 [subtitle 3 ..............]
888 for( i = 0; i < i_count - i_overlap; i++ )
890 if( p_list[i]->i_new_stop > p_list[i + i_overlap]->p_source->i_start )
892 p_list[i]->i_new_stop = p_list[i + i_overlap]->p_source->i_start;
896 /* finally - update all */
898 for( i = 0; i < i_count; i++ )
900 SubsdelayEntryNewStopValueUpdated( p_list[i] );
904 /*****************************************************************************
905 * SubsdelayRecalculateDelays: Recalculate subtitles delay after changing
906 * one of the filter's parameters
907 *****************************************************************************/
908 static void SubsdelayRecalculateDelays( filter_t *p_filter )
910 subsdelay_heap_entry_t *p_curr;
912 for( p_curr = p_filter->p_sys->heap.p_head; p_curr != NULL; p_curr = p_curr->p_next )
914 if( !p_curr->b_update_ephemer )
916 p_curr->i_new_stop = p_curr->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_curr );
917 p_curr->b_update_stop = false;
921 SubsdelayEnforceDelayRules( p_filter );
924 /*****************************************************************************
925 * SubpicValidateWrapper: Subpicture validate callback wrapper
926 *****************************************************************************/
927 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
928 bool has_dst_changed, const video_format_t *p_fmt_dst, mtime_t i_ts )
930 subsdelay_heap_entry_t *p_entry;
931 mtime_t i_new_ts;
932 int i_result = VLC_SUCCESS;
934 p_entry = p_subpic->updater.p_sys;
935 if( !p_entry )
937 return VLC_SUCCESS;
940 /* call source validate */
941 if( p_entry->p_source->updater.pf_validate )
943 i_new_ts = p_entry->p_source->i_start +
944 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
945 ( p_entry->i_new_stop - p_entry->p_source->i_start );
947 i_result = p_entry->p_source->updater.pf_validate( p_entry->p_source, has_src_changed, p_fmt_src,
948 has_dst_changed, p_fmt_dst, i_new_ts );
952 p_entry->b_last_region_saved = false;
954 if( p_subpic->p_region )
956 /* save copy */
957 p_entry->i_last_region_x = p_subpic->p_region->i_x;
958 p_entry->i_last_region_y = p_subpic->p_region->i_y;
959 p_entry->i_last_region_align = p_subpic->p_region->i_align;
961 p_entry->b_last_region_saved = true;
964 if( !i_result )
966 /* subpic update isn't necessary, so local update should be called here */
967 SubpicLocalUpdate( p_subpic, i_ts );
970 return i_result;
973 /*****************************************************************************
974 * SubpicUpdateWrapper: Subpicture update callback wrapper
975 *****************************************************************************/
976 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
977 const video_format_t *p_fmt_dst, mtime_t i_ts )
979 subsdelay_heap_entry_t *p_entry;
980 mtime_t i_new_ts;
982 p_entry = p_subpic->updater.p_sys;
983 if( !p_entry )
985 return;
988 /* call source update */
989 if( p_entry->p_source->updater.pf_update )
991 i_new_ts = p_entry->p_source->i_start +
992 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
993 ( p_entry->i_new_stop - p_entry->p_source->i_start );
995 p_entry->p_source->p_region = p_entry->p_subpic->p_region;
997 p_entry->p_source->updater.pf_update( p_entry->p_source, p_fmt_src, p_fmt_dst, i_new_ts );
999 p_entry->p_subpic->p_region = p_entry->p_source->p_region;
1002 SubpicLocalUpdate( p_subpic, i_ts );
1005 /*****************************************************************************
1006 * SubpicDestroyCallback: Subpicture destroy callback
1007 *****************************************************************************/
1008 static void SubpicDestroyWrapper( subpicture_t *p_subpic )
1010 subsdelay_heap_entry_t *p_entry;
1011 subsdelay_heap_t *p_heap;
1013 p_entry = p_subpic->updater.p_sys;
1015 if( !p_entry )
1017 return;
1020 if( p_entry->p_filter )
1022 p_heap = &p_entry->p_filter->p_sys->heap;
1024 SubsdelayHeapLock( p_heap );
1025 SubsdelayHeapRemove( p_heap, p_entry );
1026 SubsdelayHeapUnlock( p_heap );
1029 SubsdelayEntryDestroy( p_entry );
1032 /*****************************************************************************
1033 * SubpicLocalUpdate: rewrite some of the subpicture parameters
1034 *****************************************************************************/
1035 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts )
1037 subsdelay_heap_entry_t *p_entry;
1038 subsdelay_heap_t *p_heap;
1039 filter_t *p_filter;
1041 int i_overlapping;
1043 p_entry = p_subpic->updater.p_sys;
1044 if( !p_entry || !p_entry->p_filter )
1046 return;
1049 p_filter = p_entry->p_filter;
1050 p_heap = &p_filter->p_sys->heap;
1052 SubsdelayHeapLock( p_heap );
1054 if( p_entry->b_check_empty && p_subpic->p_region )
1056 if( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1057 SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) )
1059 /* remove empty subtitle */
1061 p_subpic->b_ephemer = false;
1062 p_subpic->i_stop = p_subpic->i_start;
1064 SubsdelayHeapRemove( p_heap, p_entry );
1066 SubsdelayHeapUnlock( p_heap );
1068 return;
1071 p_entry->b_check_empty = false;
1074 if( p_entry->b_update_stop && !p_entry->b_update_ephemer )
1076 p_entry->i_new_stop = p_entry->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_entry );
1077 p_entry->b_update_stop = false;
1079 SubsdelayEnforceDelayRules( p_filter );
1082 i_overlapping = SubsdelayHeapCountOverlap( p_heap, p_entry, i_ts );
1084 p_subpic->i_alpha = SubsdelayCalculateAlpha( p_filter, i_overlapping, p_entry->p_source->i_alpha );
1086 if( p_entry->b_update_position )
1088 p_subpic->b_absolute = false;
1090 if( p_subpic->p_region )
1092 p_subpic->p_region->i_x = 0;
1093 p_subpic->p_region->i_y = 10;
1094 p_subpic->p_region->i_align = ( p_subpic->p_region->i_align & ( ~SUBPICTURE_ALIGN_MASK ) )
1095 | SUBPICTURE_ALIGN_BOTTOM;
1098 p_entry->b_update_position = false;
1100 else if( p_entry->b_last_region_saved )
1102 p_subpic->b_absolute = true;
1104 if( p_subpic->p_region )
1106 p_subpic->p_region->i_x = p_entry->i_last_region_x;
1107 p_subpic->p_region->i_y = p_entry->i_last_region_y;
1108 p_subpic->p_region->i_align = p_entry->i_last_region_align;
1112 SubsdelayHeapUnlock( p_heap );
1115 /*****************************************************************************
1116 * SubpicIsEmpty: subpic region contains empty string
1117 *****************************************************************************/
1118 static bool SubpicIsEmpty( subpicture_t* p_subpic )
1120 return ( p_subpic->p_region && ( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1121 SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) ) );
1124 /*****************************************************************************
1125 * SubpicClone: Clone subpicture (shallow copy)
1126 *****************************************************************************/
1127 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater )
1129 subpicture_t *p_subpic;
1130 subpicture_updater_t subpic_updater;
1131 subpicture_private_t *p_subpic_private;
1133 p_subpic = subpicture_New( updater );
1135 if( !p_subpic )
1137 return NULL;
1140 /* save private members */
1141 subpic_updater = p_subpic->updater;
1142 p_subpic_private = p_subpic->p_private;
1144 /* copy the entire struct */
1145 memcpy( p_subpic, p_source, sizeof( subpicture_t ) );
1147 /* restore private members */
1148 p_subpic->updater = subpic_updater;
1149 p_subpic->p_private = p_subpic_private;
1151 return p_subpic;
1154 /*****************************************************************************
1155 * SubpicDestroyClone: destroy cloned subpicture (shared references will not
1156 * be destroyed)
1157 *****************************************************************************/
1158 static void SubpicDestroyClone( subpicture_t *p_subpic )
1160 p_subpic->p_region = NULL; /* don't destroy region */
1161 subpicture_Delete( p_subpic );
1164 /*****************************************************************************
1165 * SubsdelayEstimateDelay: Calculate new subtitle delay according to its
1166 * content and the calculation mode
1167 *****************************************************************************/
1168 static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry )
1170 int i_mode;
1171 int i_factor;
1172 int i_rank;
1174 i_mode = p_filter->p_sys->i_mode;
1175 i_factor = p_filter->p_sys->i_factor;
1177 if( i_mode == SUBSDELAY_MODE_ABSOLUTE )
1179 return ( p_entry->p_source->i_stop - p_entry->p_source->i_start + INT_FACTOR_TO_MICROSEC( i_factor ) );
1182 if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT )
1184 if( p_entry->p_subpic && p_entry->p_subpic->p_region && ( p_entry->p_subpic->p_region->psz_text
1185 || p_entry->p_subpic->p_region->psz_html ) )
1187 if( p_entry->p_subpic->p_region->psz_text )
1189 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_text );
1191 else
1193 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_html );
1196 return ( i_rank * INT_FACTOR_TO_RANK_FACTOR( i_factor ) );
1199 /* content is unavailable, calculation mode should be based on source delay */
1200 i_mode = SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY;
1203 if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY )
1205 return ( ( i_factor * ( p_entry->p_source->i_stop - p_entry->p_source->i_start ) ) / INT_FACTOR_BASE );
1208 return 10000000; /* 10 sec */
1211 /*****************************************************************************
1212 * SubsdelayCalculateAlpha: Calculate subtitle alpha according to source alpha
1213 * value and number of overlapping subtitles
1214 *****************************************************************************/
1215 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha )
1217 int i_new_alpha;
1218 int i_min_alpha;
1220 i_min_alpha = p_filter->p_sys->i_min_alpha;
1222 if( i_overlapping > p_filter->p_sys->i_overlap - 1)
1224 i_overlapping = p_filter->p_sys->i_overlap - 1;
1227 switch ( p_filter->p_sys->i_overlap )
1229 case 1:
1230 i_new_alpha = 255;
1231 break;
1233 case 2:
1234 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha );
1235 break;
1237 case 3:
1238 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 2;
1239 break;
1241 default:
1242 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 3;
1243 break;
1246 return ( i_source_alpha * i_new_alpha ) / 255;
1249 /*****************************************************************************
1250 * SubsdelayGetWordRank: Calculate single word rank according to its length
1251 *****************************************************************************/
1252 static int SubsdelayGetWordRank( int i_length )
1254 /* p_rank[0] = p_rank[1] = p_rank[2] = 300;
1255 for( i = 3; i < 20; i++ ) p_rank[i] = (int) ( 1.1 * p_rank[i - 1] ); */
1257 static const int p_rank[20] = { 300, 300, 300, 330, 363, 399, 438, 481, 529, 581,
1258 639, 702, 772, 849, 933, 1026, 1128, 1240, 1364, 1500 };
1260 if( i_length < 1 )
1262 return 0;
1265 if( i_length > 20 )
1267 i_length = 20;
1270 return p_rank[i_length - 1];
1273 /*****************************************************************************
1274 * SubsdelayGetTextRank: Calculate text rank
1275 *****************************************************************************/
1276 static int SubsdelayGetTextRank( char *psz_text )
1278 bool b_skip_esc;
1279 bool b_skip_tag;
1280 char c;
1282 int i, i_word_length, i_rank;
1284 i = 0;
1285 i_word_length = 0;
1286 i_rank = 0;
1288 b_skip_esc = false;
1289 b_skip_tag = false;
1291 while ( psz_text[i] != '\0' )
1293 c = psz_text[i];
1294 i++;
1296 if( c == '\\' && !b_skip_esc )
1298 b_skip_esc = true;
1299 continue;
1302 if( psz_text[i] == '<' )
1304 b_skip_tag = true;
1305 continue;
1308 if( !b_skip_esc && !b_skip_tag )
1310 if( c == ' ' || c == ',' || c == '.' || c == '-' || c == '?' || c == '!' ) /* common delimiters */
1312 if( i_word_length > 0 )
1314 i_rank += SubsdelayGetWordRank( i_word_length );
1317 i_word_length = 0;
1319 else
1321 i_word_length++;
1325 b_skip_esc = false;
1327 if( c == '>' )
1329 b_skip_tag = false;
1334 if( i_word_length > 0 )
1336 i_rank += SubsdelayGetWordRank( i_word_length );
1339 return i_rank;
1342 /*****************************************************************************
1343 * SubsdelayIsTextEmpty: Check if the text contains spaces only
1344 *****************************************************************************/
1345 static bool SubsdelayIsTextEmpty( char *psz_text )
1347 if( !psz_text )
1349 return false;
1352 psz_text += strspn( psz_text, " " );
1353 return !( *psz_text );