1 /*****************************************************************************
3 *****************************************************************************/
9 #include <vlc_common.h>
10 #include <vlc_plugin.h>
11 #include <vlc_filter.h>
12 #include <vlc_subpicture.h>
17 /*****************************************************************************
19 *****************************************************************************/
23 #define SUBSDELAY_HELP N_("Change subtitles delay")
25 #define MODE_TEXT N_( "Delay calculation mode" )
26 #define MODE_LONGTEXT N_( \
27 "Absolute delay - add absolute delay to each subtitle. " \
28 "Relative to source delay - multiply subtitle delay. " \
29 "Relative to source content - determine subtitle delay from its content (text)." )
31 #define FACTOR_TEXT N_( "Calculation factor" )
32 #define FACTOR_LONGTEXT N_( "Calculation factor. " \
33 "In Absolute delay mode the factor represents seconds.")
35 #define OVERLAP_TEXT N_( "Maximum overlapping subtitles" )
36 #define OVERLAP_LONGTEXT N_( "Maximum number of subtitles allowed at the same time." )
38 #define MIN_ALPHA_TEXT N_( "Minimum alpha value" )
39 #define MIN_ALPHA_LONGTEXT N_( \
40 "Alpha value of the earliest subtitle, where 0 is fully transparent and 255 is fully opaque." )
42 #define MIN_STOPS_INTERVAL_TEXT N_( "Interval between two disappearances" )
43 #define MIN_STOPS_INTERVAL_LONGTEXT N_( \
44 "Minimum time (in milliseconds) that subtitle should stay after its predecessor has disappeared " \
45 "(subtitle delay will be extended to meet this requirement)." )
47 #define MIN_STOP_START_INTERVAL_TEXT N_( "Interval between disappearance and appearance" )
48 #define MIN_STOP_START_INTERVAL_LONGTEXT N_( \
49 "Minimum time (in milliseconds) between subtitle disappearance and newer subtitle appearance " \
50 "(earlier subtitle delay will be extended to fill the gap)." )
52 #define MIN_START_STOP_INTERVAL_TEXT N_( "Interval between appearance and disappearance" )
53 #define MIN_START_STOP_INTERVAL_LONGTEXT N_( \
54 "Minimum time (in milliseconds) that subtitle should stay after newer subtitle has appeared " \
55 "(earlier subtitle delay will be shortened to avoid the overlap)." )
57 static const int pi_mode_values[] = { 0, 1, 2 };
58 static const char * const ppsz_mode_descriptions[] = { N_( "Absolute delay" ), N_( "Relative to source delay" ), N_(
59 "Relative to source content" ) };
63 #define CFG_PREFIX "subsdelay-"
65 #define CFG_MODE CFG_PREFIX "mode"
66 #define CFG_FACTOR CFG_PREFIX "factor"
67 #define CFG_OVERLAP CFG_PREFIX "overlap"
69 #define CFG_MIN_ALPHA CFG_PREFIX "min-alpha"
70 #define CFG_MIN_STOPS_INTERVAL CFG_PREFIX "min-stops"
71 #define CFG_MIN_STOP_START_INTERVAL CFG_PREFIX "min-stop-start"
72 #define CFG_MIN_START_STOP_INTERVAL CFG_PREFIX "min-start-stop"
75 /* max subtitles handled on the heap */
76 #define SUBSDELAY_MAX_ENTRIES 16
78 /* factor convert macros */
79 #define INT_FACTOR_BASE 1000
80 #define FLOAT_FACTOR_TO_INT_FACTOR( x ) (int)( ( x ) * INT_FACTOR_BASE )
81 #define INT_FACTOR_TO_MICROSEC( x ) ( ( x ) * ( 1000000 / INT_FACTOR_BASE ) )
82 #define INT_FACTOR_TO_RANK_FACTOR( x ) ( x )
83 #define MILLISEC_TO_MICROSEC( x ) ( ( x ) * 1000 )
86 #define SUBSDELAY_MODE_ABSOLUTE 0
87 #define SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY 1
88 #define SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT 2
91 /*****************************************************************************
92 * subsdelay_heap_entry_t: Heap entry
93 *****************************************************************************/
95 typedef subpicture_updater_sys_t subsdelay_heap_entry_t;
97 struct subpicture_updater_sys_t
99 subpicture_t *p_subpic; /* local subtitle */
101 subpicture_t *p_source; /* subtitle source */
103 filter_t *p_filter; /* assigned subsdelay filter */
105 subsdelay_heap_entry_t *p_next; /* next entry */
107 bool b_update_stop; /* new stop value should be calculated */
109 bool b_update_ephemer; /* actual stop value is unknown */
111 bool b_update_position; /* subtitle position should be updated */
113 bool b_check_empty; /* subtitle content should be checked */
115 mtime_t i_new_stop; /* new stop value */
117 /* last region data*/
123 int i_last_region_align;
125 bool b_last_region_saved;
128 /*****************************************************************************
129 * subsdelay_heap_t: Heap
130 *****************************************************************************/
134 vlc_mutex_t lock; /* heap global lock */
136 subsdelay_heap_entry_t *p_list[SUBSDELAY_MAX_ENTRIES]; /* subtitles entries array */
138 subsdelay_heap_entry_t *p_head; /* subtitles entries linked list */
140 int i_count; /* subtitles count */
146 /*****************************************************************************
147 * filter_sys_t: Subsdelay filter descriptor
148 *****************************************************************************/
152 int i_mode; /* delay calculation mode */
154 int i_factor; /* calculation factor */
156 int i_overlap; /* max overlap */
158 int i_min_alpha; /* oldest subtitle alpha value */
160 int64_t i_min_stops_interval;
162 int64_t i_min_stop_start_interval;
164 int64_t i_min_start_stop_interval;
166 subsdelay_heap_t heap; /* subpictures list */
170 /*****************************************************************************
172 *****************************************************************************/
174 static int SubsdelayCreate( vlc_object_t * );
176 static void SubsdelayDestroy( vlc_object_t * );
178 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic );
180 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
183 /*****************************************************************************
185 *****************************************************************************/
187 static void SubsdelayEnforceDelayRules( filter_t *p_filter );
189 static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry );
191 static void SubsdelayRecalculateDelays( filter_t *p_filter );
193 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha );
195 static int SubsdelayGetTextRank( char *psz_text );
197 static bool SubsdelayIsTextEmpty( char *psz_text );
199 /*****************************************************************************
200 * Subpicture functions
201 *****************************************************************************/
203 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
204 bool has_dst_changed, const video_format_t *p_fmt_dst, mtime_t i_ts );
206 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
207 const video_format_t *p_fmt_dst, mtime_t i_ts );
209 static void SubpicDestroyWrapper( subpicture_t *p_subpic );
211 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts );
213 static bool SubpicIsEmpty( subpicture_t* p_subpic );
215 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater );
217 static void SubpicDestroyClone( subpicture_t *p_subpic );
219 /*****************************************************************************
221 *****************************************************************************/
223 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap );
225 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap );
227 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter );
229 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry );
231 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap );
233 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap );
235 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap );
237 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_subpic, filter_t *p_filter );
239 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry );
241 /* heap / entries special functionality */
243 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date );
245 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry );
247 /*****************************************************************************
249 *****************************************************************************/
252 set_shortname( _("Subsdelay") )
253 set_description( _("Subtitles delay") )
254 set_help( SUBSDELAY_HELP )
255 set_capability( "sub filter", 0 )
256 set_callbacks( SubsdelayCreate, SubsdelayDestroy )
257 set_category( CAT_VIDEO )
258 set_subcategory( SUBCAT_VIDEO_SUBPIC )
260 add_integer( CFG_MODE, 1, MODE_TEXT, MODE_LONGTEXT, false )
261 change_integer_list( pi_mode_values, ppsz_mode_descriptions )
263 add_float_with_range( CFG_FACTOR, 2, 0, 20, NULL, FACTOR_TEXT, FACTOR_LONGTEXT, false )
265 add_integer_with_range( CFG_OVERLAP, 3, 1, 4, NULL, OVERLAP_TEXT, OVERLAP_LONGTEXT, false )
267 add_integer_with_range( CFG_MIN_ALPHA, 125, 0, 255, NULL, MIN_ALPHA_TEXT, MIN_ALPHA_LONGTEXT, false )
269 set_section( N_("Overlap fix"), NULL )
271 add_integer( CFG_MIN_STOPS_INTERVAL, 1000, MIN_STOPS_INTERVAL_TEXT, MIN_STOPS_INTERVAL_LONGTEXT, false )
273 add_integer( CFG_MIN_START_STOP_INTERVAL, 1000, MIN_START_STOP_INTERVAL_TEXT,
274 MIN_START_STOP_INTERVAL_LONGTEXT, false )
276 add_integer( CFG_MIN_STOP_START_INTERVAL, 1000, MIN_STOP_START_INTERVAL_TEXT,
277 MIN_STOP_START_INTERVAL_LONGTEXT, false )
281 static const char * const ppsz_filter_options[] = { "mode", "factor", "overlap", NULL };
283 /*****************************************************************************
284 * SubsdelayCreate: Create subsdelay filter
285 *****************************************************************************/
286 static int SubsdelayCreate( vlc_object_t *p_this )
288 filter_t *p_filter = (filter_t *) p_this;
291 /* allocate structure */
292 p_sys = (filter_sys_t*) malloc( sizeof(filter_sys_t) );
299 /* init parameters */
301 p_sys->i_mode = var_CreateGetIntegerCommand( p_filter, CFG_MODE );
302 var_AddCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
304 p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( var_CreateGetFloatCommand( p_filter, CFG_FACTOR ) );
305 var_AddCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
307 p_sys->i_overlap = var_CreateGetIntegerCommand( p_filter, CFG_OVERLAP );
308 var_AddCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
310 p_sys->i_min_alpha = var_CreateGetIntegerCommand( p_filter, CFG_MIN_ALPHA );
311 var_AddCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
313 p_sys->i_min_stops_interval
314 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOPS_INTERVAL ) );
315 var_AddCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
317 p_sys->i_min_stop_start_interval
318 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOP_START_INTERVAL ) );
319 var_AddCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
321 p_sys->i_min_start_stop_interval
322 = MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_START_STOP_INTERVAL ) );
323 var_AddCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
325 p_filter->p_sys = p_sys;
326 p_filter->pf_sub_filter = SubsdelayFilter;
328 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg );
330 SubsdelayHeapInit( &p_sys->heap );
335 /*****************************************************************************
336 * SubsdelayDestroy: Destroy subsdelay filter
337 *****************************************************************************/
338 static void SubsdelayDestroy( vlc_object_t *p_this )
340 filter_t *p_filter = (filter_t *) p_this;
341 filter_sys_t *p_sys = p_filter->p_sys;
343 SubsdelayHeapDestroy( &p_sys->heap );
345 /* destroy parameters */
347 var_DelCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
348 var_Destroy( p_filter, CFG_MODE );
350 var_DelCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
351 var_Destroy( p_filter, CFG_FACTOR );
353 var_DelCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
354 var_Destroy( p_filter, CFG_OVERLAP );
356 var_DelCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
357 var_Destroy( p_filter, CFG_MIN_ALPHA );
359 var_DelCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
360 var_Destroy( p_filter, CFG_MIN_STOPS_INTERVAL );
362 var_DelCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
363 var_Destroy( p_filter, CFG_MIN_STOP_START_INTERVAL );
365 var_DelCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
366 var_Destroy( p_filter, CFG_MIN_START_STOP_INTERVAL );
371 /*****************************************************************************
372 * SubsdelayFilter: Filter new subpicture
373 *****************************************************************************/
374 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic )
376 subsdelay_heap_t *p_heap;
377 subsdelay_heap_entry_t *p_entry;
379 if( !p_subpic->b_subtitle )
384 if( SubpicIsEmpty( p_subpic ) )
386 /* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway,
387 so this subtitle can be dropped */
389 subpicture_Delete( p_subpic );
394 p_heap = &p_filter->p_sys->heap;
396 /* add subpicture to the heap */
398 SubsdelayHeapLock( p_heap );
400 p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter );
403 SubsdelayHeapUnlock( p_heap );
405 msg_Err(p_filter, "Can't add subpicture to the heap");
410 p_subpic = p_entry->p_subpic; /* get the local subpic */
412 if( p_subpic->b_ephemer )
414 /* set a relativly long delay in hope that the next subtitle
415 will arrive in this time and the real delay could be determined */
417 p_subpic->i_stop = p_subpic->i_start + 20000000; /* start + 20 sec */
418 p_subpic->b_ephemer = false;
422 SubsdelayEnforceDelayRules( p_filter );
424 SubsdelayHeapUnlock( p_heap );
429 /*****************************************************************************
430 * SubsdelayCallback: Subsdelay parameters callback
431 *****************************************************************************/
432 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
435 filter_sys_t *p_sys = (filter_sys_t *) p_data;
437 VLC_UNUSED( oldval );
439 SubsdelayHeapLock( &p_sys->heap );
441 if( !strcmp( psz_var, CFG_MODE ) )
443 p_sys->i_mode = newval.i_int;
445 else if( !strcmp( psz_var, CFG_FACTOR ) )
447 p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( newval.f_float );
449 else if( !strcmp( psz_var, CFG_OVERLAP ) )
451 p_sys->i_overlap = newval.i_int;
453 else if( !strcmp( psz_var, CFG_MIN_ALPHA ) )
455 p_sys->i_min_alpha = newval.i_int;
457 else if( !strcmp( psz_var, CFG_MIN_STOPS_INTERVAL ) )
459 p_sys->i_min_stops_interval = MILLISEC_TO_MICROSEC( newval.i_int );
461 else if( !strcmp( psz_var, CFG_MIN_STOP_START_INTERVAL ) )
463 p_sys->i_min_stop_start_interval = MILLISEC_TO_MICROSEC( newval.i_int );
465 else if( !strcmp( psz_var, CFG_MIN_START_STOP_INTERVAL ) )
467 p_sys->i_min_start_stop_interval = MILLISEC_TO_MICROSEC( newval.i_int );
471 SubsdelayHeapUnlock( &p_sys->heap );
475 SubsdelayRecalculateDelays( (filter_t *) p_this );
477 SubsdelayHeapUnlock( &p_sys->heap );
481 /*****************************************************************************
482 * SubsdelayHeapInit: Initialize heap
483 *****************************************************************************/
484 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap )
487 p_heap->p_head = NULL;
489 vlc_mutex_init( &p_heap->lock );
492 /*****************************************************************************
493 * SubsdelayHeapDestroy: Destroy the heap and remove its entries
494 *****************************************************************************/
495 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap )
497 subsdelay_heap_entry_t *p_entry;
499 SubsdelayHeapLock( p_heap );
501 for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
503 p_entry->p_subpic->i_stop = p_entry->p_source->i_stop;
505 p_entry->p_filter = NULL;
508 SubsdelayHeapUnlock( p_heap );
510 vlc_mutex_destroy( &p_heap->lock );
513 /*****************************************************************************
514 * SubsdelayHeapPush: Add new subpicture to the heap
515 *****************************************************************************/
516 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter )
518 subsdelay_heap_entry_t *p_entry, *p_last, *p_new_entry;
520 if( p_heap->i_count >= SUBSDELAY_MAX_ENTRIES )
522 return NULL; /* the heap is full */
525 p_new_entry = SubsdelayEntryCreate( p_subpic, p_filter );
535 for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
537 if( p_entry->p_source->i_start > p_subpic->i_start )
539 /* the new entry should be inserted before p_entry */
548 p_new_entry->p_next = p_last->p_next;
549 p_last->p_next = p_new_entry;
552 if( p_last->b_update_ephemer )
554 /* the correct stop value can be determined */
556 p_last->p_source->i_stop = p_new_entry->p_source->i_start;
557 p_last->b_update_ephemer = false;
562 p_new_entry->p_next = p_heap->p_head;
563 p_heap->p_head = p_new_entry;
569 SubsdelayRebuildList( p_heap );
574 /*****************************************************************************
575 * SubsdelayHeapRemove: Remove entry
576 *****************************************************************************/
577 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry )
579 subsdelay_heap_entry_t *p_curr, *p_prev;
583 for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
585 if( p_curr == p_entry )
595 p_prev->p_next = p_entry->p_next;
599 p_heap->p_head = p_entry->p_next;
602 p_entry->p_filter = NULL;
604 SubsdelayRebuildList( p_heap );
608 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap )
610 subsdelay_heap_entry_t *p_curr;
614 for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
616 p_heap->p_list[i_index] = p_curr;
620 p_heap->i_count = i_index;
623 /*****************************************************************************
624 * SubsdelayHeapLock: Lock the heap
625 *****************************************************************************/
626 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap )
628 vlc_mutex_lock( &p_heap->lock );
631 /*****************************************************************************
632 * SubsdelayHeapUnlock: Unlock the heap
633 *****************************************************************************/
634 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap )
636 vlc_mutex_unlock( &p_heap->lock );
640 /*****************************************************************************
641 * SubsdelayHeapCreateEntry: Create new entry
642 *****************************************************************************/
643 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_source, filter_t *p_filter )
645 subsdelay_heap_entry_t *p_entry;
647 subpicture_t *p_new_subpic;
649 subpicture_updater_t updater;
651 /* allocate structure */
653 p_entry = (subsdelay_heap_entry_t *) malloc( sizeof( subsdelay_heap_entry_t ) );
660 /* initialize local updater */
662 updater.p_sys = p_entry;
663 updater.pf_validate = SubpicValidateWrapper;
664 updater.pf_update = SubpicUpdateWrapper;
665 updater.pf_destroy = SubpicDestroyWrapper;
667 /* create new subpic */
669 p_new_subpic = SubpicClone( p_source, &updater );
677 /* initialize entry */
679 p_entry->p_subpic = p_new_subpic;
680 p_entry->p_source = p_source;
681 p_entry->p_filter = p_filter;
682 p_entry->p_next = NULL;
683 p_entry->b_update_stop = true;
684 p_entry->b_update_ephemer = p_source->b_ephemer;
685 p_entry->b_update_position = true;
686 p_entry->b_check_empty = true;
687 p_entry->i_new_stop = p_source->i_stop;
688 p_entry->b_last_region_saved = false;
689 p_entry->i_last_region_x = 0;
690 p_entry->i_last_region_y = 0;
691 p_entry->i_last_region_align = 0;
696 /*****************************************************************************
697 * SubsdelayEntryDestroy: Destroy entry
698 *****************************************************************************/
699 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry )
701 SubpicDestroyClone( p_entry->p_source );
705 /*****************************************************************************
706 * SubsdelayHeapCountOverlap: Count overlapping subtitles at a given time
707 *****************************************************************************/
708 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date )
710 subsdelay_heap_entry_t *p_curr;
713 VLC_UNUSED( p_heap );
717 for( p_curr = p_entry->p_next; p_curr != NULL; p_curr = p_curr->p_next )
719 if( p_curr->p_source->i_start > i_date )
724 if( !p_curr->b_check_empty ) /* subtitle was checked, and it's not empty */
733 /*****************************************************************************
734 * SubsdelayEntryNewStopValueUpdated: Update source stop value after the new
735 * stop value was changed
736 *****************************************************************************/
737 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry )
739 if( !p_entry->b_update_stop )
741 p_entry->p_subpic->i_stop = p_entry->i_new_stop - 100000; /* 0.1 sec less */
745 /*****************************************************************************
746 * SubsdelayEnforceDelayRules: Update subtitles delay after adding new
747 * subtitle or changing subtitle stop value
748 *****************************************************************************/
749 static void SubsdelayEnforceDelayRules( filter_t *p_filter )
751 subsdelay_heap_entry_t ** p_list;
752 int i, j, i_count, i_overlap;
754 int64_t i_min_stops_interval;
755 int64_t i_min_stop_start_interval;
756 int64_t i_min_start_stop_interval;
758 p_list = p_filter->p_sys->heap.p_list;
759 i_count = p_filter->p_sys->heap.i_count;
761 i_overlap = p_filter->p_sys->i_overlap;
762 i_min_stops_interval = p_filter->p_sys->i_min_stops_interval;
763 i_min_stop_start_interval = p_filter->p_sys->i_min_stop_start_interval;
764 i_min_start_stop_interval = p_filter->p_sys->i_min_start_stop_interval;
766 /* step 1 - enforce min stops interval rule (extend delays) */
769 [subtitle 1 ..............]
770 [subtitle 2 ..............]
771 |<-MinStopsInterval->|
773 * and extend newer subtitle:
774 [subtitle 1 ..............]
775 [subtitle 2 ............................]
776 |<-MinStopsInterval->|
779 for( i = 0; i < i_count - 1; i++ )
781 p_list[i + 1]->i_new_stop = __MAX( p_list[i + 1]->i_new_stop,
782 p_list[i]->i_new_stop + i_min_stops_interval );
785 /* step 2 - enforce min stop start interval rule (extend delays) */
788 [subtitle 1 .........]
790 |<-MinStopStartInterval->|
793 [subtitle 1 ..................]
795 |<-MinStopStartInterval->|
798 for( i = 0; i < i_count; i++ )
800 for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
802 i_offset = p_list[j]->p_source->i_start - p_list[i]->i_new_stop;
809 if( i_offset < i_min_stop_start_interval )
811 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
818 /* step 3 - enforce min start stop interval rule (shorten delays) */
821 [subtitle 1 ............]
822 [subtitle 2 ....................]
823 |<-MinStartStopInterval->|
825 * and remove the overlapping part:
827 [subtitle 2 ....................]
828 |<-MinStartStopInterval->|
832 for( i = 0; i < i_count; i++ )
834 for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
836 i_offset = p_list[i]->i_new_stop - p_list[j]->p_source->i_start;
843 if( i_offset < i_min_start_stop_interval )
845 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
851 /* step 4 - enforce max overlapping rule (shorten delays)*/
853 /* look for: (overlap = 2)
854 [subtitle 1 ..............]
855 [subtitle 2 ..............]
856 [subtitle 3 ..............]
859 * and cut older subtitle:
861 [subtitle 2 ..............]
862 [subtitle 3 ..............]
865 for( i = 0; i < i_count - i_overlap; i++ )
867 if( p_list[i]->i_new_stop > p_list[i + i_overlap]->p_source->i_start )
869 p_list[i]->i_new_stop = p_list[i + i_overlap]->p_source->i_start;
873 /* finally - update all */
875 for( i = 0; i < i_count; i++ )
877 SubsdelayEntryNewStopValueUpdated( p_list[i] );
881 /*****************************************************************************
882 * SubsdelayRecalculateDelays: Recalculate subtitles delay after changing
883 * one of the filter's parameters
884 *****************************************************************************/
885 static void SubsdelayRecalculateDelays( filter_t *p_filter )
887 subsdelay_heap_entry_t *p_curr;
889 for( p_curr = p_filter->p_sys->heap.p_head; p_curr != NULL; p_curr = p_curr->p_next )
891 if( !p_curr->b_update_ephemer )
893 p_curr->i_new_stop = p_curr->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_curr );
894 p_curr->b_update_stop = false;
898 SubsdelayEnforceDelayRules( p_filter );
901 /*****************************************************************************
902 * SubpicValidateWrapper: Subpicture validate callback wrapper
903 *****************************************************************************/
904 static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
905 bool has_dst_changed, const video_format_t *p_fmt_dst, mtime_t i_ts )
907 subsdelay_heap_entry_t *p_entry;
911 p_entry = p_subpic->updater.p_sys;
917 /* call source validate */
918 if( p_entry->p_source->updater.pf_validate )
920 i_new_ts = p_entry->p_source->i_start +
921 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
922 ( p_entry->i_new_stop - p_entry->p_source->i_start );
924 i_result = p_entry->p_source->updater.pf_validate( p_entry->p_source, has_src_changed, p_fmt_src,
925 has_dst_changed, p_fmt_dst, i_new_ts );
929 p_entry->b_last_region_saved = false;
931 if( p_subpic->p_region )
934 p_entry->i_last_region_x = p_subpic->p_region->i_x;
935 p_entry->i_last_region_y = p_subpic->p_region->i_y;
936 p_entry->i_last_region_align = p_subpic->p_region->i_align;
938 p_entry->b_last_region_saved = true;
943 /* subpic update isn't necessary, so local update should be called here */
944 SubpicLocalUpdate( p_subpic, i_ts );
950 /*****************************************************************************
951 * SubpicUpdateWrapper: Subpicture update callback wrapper
952 *****************************************************************************/
953 static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
954 const video_format_t *p_fmt_dst, mtime_t i_ts )
956 subsdelay_heap_entry_t *p_entry;
959 p_entry = p_subpic->updater.p_sys;
965 /* call source update */
966 if( p_entry->p_source->updater.pf_update )
968 i_new_ts = p_entry->p_source->i_start +
969 ( (double)( p_entry->p_source->i_stop - p_entry->p_source->i_start ) * ( i_ts - p_entry->p_source->i_start ) ) /
970 ( p_entry->i_new_stop - p_entry->p_source->i_start );
972 p_entry->p_source->p_region = p_entry->p_subpic->p_region;
974 p_entry->p_source->updater.pf_update( p_entry->p_source, p_fmt_src, p_fmt_dst, i_new_ts );
976 p_entry->p_subpic->p_region = p_entry->p_source->p_region;
979 SubpicLocalUpdate( p_subpic, i_ts );
982 /*****************************************************************************
983 * SubpicDestroyCallback: Subpicture destroy callback
984 *****************************************************************************/
985 static void SubpicDestroyWrapper( subpicture_t *p_subpic )
987 subsdelay_heap_entry_t *p_entry;
988 subsdelay_heap_t *p_heap;
990 p_entry = p_subpic->updater.p_sys;
997 if( p_entry->p_filter )
999 p_heap = &p_entry->p_filter->p_sys->heap;
1001 SubsdelayHeapLock( p_heap );
1002 SubsdelayHeapRemove( p_heap, p_entry );
1003 SubsdelayHeapUnlock( p_heap );
1006 SubsdelayEntryDestroy( p_entry );
1009 /*****************************************************************************
1010 * SubpicLocalUpdate: rewrite some of the subpicture parameters
1011 *****************************************************************************/
1012 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts )
1014 subsdelay_heap_entry_t *p_entry;
1015 subsdelay_heap_t *p_heap;
1020 p_entry = p_subpic->updater.p_sys;
1021 if( !p_entry || !p_entry->p_filter )
1026 p_filter = p_entry->p_filter;
1027 p_heap = &p_filter->p_sys->heap;
1029 SubsdelayHeapLock( p_heap );
1031 if( p_entry->b_check_empty && p_subpic->p_region )
1033 if( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1034 SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) )
1036 /* remove empty subtitle */
1038 p_subpic->b_ephemer = false;
1039 p_subpic->i_stop = p_subpic->i_start;
1041 SubsdelayHeapRemove( p_heap, p_entry );
1043 SubsdelayHeapUnlock( p_heap );
1048 p_entry->b_check_empty = false;
1051 if( p_entry->b_update_stop && !p_entry->b_update_ephemer )
1053 p_entry->i_new_stop = p_entry->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_entry );
1054 p_entry->b_update_stop = false;
1056 SubsdelayEnforceDelayRules( p_filter );
1059 i_overlapping = SubsdelayHeapCountOverlap( p_heap, p_entry, i_ts );
1061 p_subpic->i_alpha = SubsdelayCalculateAlpha( p_filter, i_overlapping, p_entry->p_source->i_alpha );
1063 if( p_entry->b_update_position )
1065 p_subpic->b_absolute = false;
1067 if( p_subpic->p_region )
1069 p_subpic->p_region->i_x = 0;
1070 p_subpic->p_region->i_y = 10;
1071 p_subpic->p_region->i_align = ( p_subpic->p_region->i_align & ( ~SUBPICTURE_ALIGN_MASK ) )
1072 | SUBPICTURE_ALIGN_BOTTOM;
1075 p_entry->b_update_position = false;
1077 else if( p_entry->b_last_region_saved )
1079 p_subpic->b_absolute = true;
1081 if( p_subpic->p_region )
1083 p_subpic->p_region->i_x = p_entry->i_last_region_x;
1084 p_subpic->p_region->i_y = p_entry->i_last_region_y;
1085 p_subpic->p_region->i_align = p_entry->i_last_region_align;
1089 SubsdelayHeapUnlock( p_heap );
1092 /*****************************************************************************
1093 * SubpicIsEmpty: subpic region contains empty string
1094 *****************************************************************************/
1095 static bool SubpicIsEmpty( subpicture_t* p_subpic )
1097 return ( p_subpic->p_region && ( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1098 SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) ) );
1101 /*****************************************************************************
1102 * SubpicClone: Clone subpicture (shallow copy)
1103 *****************************************************************************/
1104 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater )
1106 subpicture_t *p_subpic;
1107 subpicture_updater_t subpic_updater;
1108 subpicture_private_t *p_subpic_private;
1110 p_subpic = subpicture_New( updater );
1117 /* save private members */
1118 subpic_updater = p_subpic->updater;
1119 p_subpic_private = p_subpic->p_private;
1121 /* copy the entire struct */
1122 memcpy( p_subpic, p_source, sizeof( subpicture_t ) );
1124 /* restore private members */
1125 p_subpic->updater = subpic_updater;
1126 p_subpic->p_private = p_subpic_private;
1131 /*****************************************************************************
1132 * SubpicDestroyClone: destroy cloned subpicture (shared references will not
1134 *****************************************************************************/
1135 static void SubpicDestroyClone( subpicture_t *p_subpic )
1137 p_subpic->p_region = NULL; /* don't destroy region */
1138 subpicture_Delete( p_subpic );
1141 /*****************************************************************************
1142 * SubsdelayEstimateDelay: Calculate new subtitle delay according to its
1143 * content and the calculation mode
1144 *****************************************************************************/
1145 static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry )
1151 i_mode = p_filter->p_sys->i_mode;
1152 i_factor = p_filter->p_sys->i_factor;
1154 if( i_mode == SUBSDELAY_MODE_ABSOLUTE )
1156 return ( p_entry->p_source->i_stop - p_entry->p_source->i_start + INT_FACTOR_TO_MICROSEC( i_factor ) );
1159 if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT )
1161 if( p_entry->p_subpic && p_entry->p_subpic->p_region && ( p_entry->p_subpic->p_region->psz_text
1162 || p_entry->p_subpic->p_region->psz_html ) )
1164 if( p_entry->p_subpic->p_region->psz_text )
1166 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_text );
1170 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_html );
1173 return ( i_rank * INT_FACTOR_TO_RANK_FACTOR( i_factor ) );
1176 /* content is unavailable, calculation mode should be based on source delay */
1177 i_mode = SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY;
1180 if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY )
1182 return ( ( i_factor * ( p_entry->p_source->i_stop - p_entry->p_source->i_start ) ) / INT_FACTOR_BASE );
1185 return 10000000; /* 10 sec */
1188 /*****************************************************************************
1189 * SubsdelayCalculateAlpha: Calculate subtitle alpha according to source alpha
1190 * value and number of overlapping subtitles
1191 *****************************************************************************/
1192 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha )
1197 i_min_alpha = p_filter->p_sys->i_min_alpha;
1199 if( i_overlapping > p_filter->p_sys->i_overlap - 1)
1201 i_overlapping = p_filter->p_sys->i_overlap - 1;
1204 switch ( p_filter->p_sys->i_overlap )
1211 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha );
1215 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 2;
1219 i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 3;
1223 return ( i_source_alpha * i_new_alpha ) / 255;
1226 /*****************************************************************************
1227 * SubsdelayGetWordRank: Calculate single word rank according to its length
1228 *****************************************************************************/
1229 static int SubsdelayGetWordRank( int i_length )
1231 /* p_rank[0] = p_rank[1] = p_rank[2] = 300;
1232 for( i = 3; i < 20; i++ ) p_rank[i] = (int) ( 1.1 * p_rank[i - 1] ); */
1234 static const int p_rank[20] = { 300, 300, 300, 330, 363, 399, 438, 481, 529, 581,
1235 639, 702, 772, 849, 933, 1026, 1128, 1240, 1364, 1500 };
1247 return p_rank[i_length - 1];
1250 /*****************************************************************************
1251 * SubsdelayGetTextRank: Calculate text rank
1252 *****************************************************************************/
1253 static int SubsdelayGetTextRank( char *psz_text )
1259 int i, i_word_length, i_rank;
1268 while ( psz_text[i] != '\0' )
1273 if( c == '\\' && !b_skip_esc )
1279 if( psz_text[i] == '<' )
1285 if( !b_skip_esc && !b_skip_tag )
1287 if( c == ' ' || c == ',' || c == '.' || c == '-' || c == '?' || c == '!' ) /* common delimiters */
1289 if( i_word_length > 0 )
1291 i_rank += SubsdelayGetWordRank( i_word_length );
1311 if( i_word_length > 0 )
1313 i_rank += SubsdelayGetWordRank( i_word_length );
1319 /*****************************************************************************
1320 * SubsdelayIsTextEmpty: Check if the text contains spaces only
1321 *****************************************************************************/
1322 static bool SubsdelayIsTextEmpty( char *psz_text )
1329 psz_text += strspn( psz_text, " " );
1330 return !( *psz_text );