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 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_subpicture.h>
38 /*****************************************************************************
40 *****************************************************************************/
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" ) };
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*/
136 int i_last_region_align
;
138 bool b_last_region_saved
;
141 /*****************************************************************************
142 * subsdelay_heap_t: Heap
143 *****************************************************************************/
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 */
159 /*****************************************************************************
160 * filter_sys_t: Subsdelay filter descriptor
161 *****************************************************************************/
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 */
183 /*****************************************************************************
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
,
196 /*****************************************************************************
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 /*****************************************************************************
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 /*****************************************************************************
262 *****************************************************************************/
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 )
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
;
304 /* allocate structure */
305 p_sys
= (filter_sys_t
*) malloc( sizeof(filter_sys_t
) );
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
);
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
);
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
)
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
);
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
);
417 SubsdelayHeapUnlock( p_heap
);
419 msg_Err(p_filter
, "Can't add subpicture to the heap");
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
);
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
,
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
);
485 vlc_assert_unreachable();
488 SubsdelayRecalculateDelays( (filter_t
*) p_this
);
490 SubsdelayHeapUnlock( &p_sys
->heap
);
494 /*****************************************************************************
495 * SubsdelayHeapInit: Initialize heap
496 *****************************************************************************/
497 static void SubsdelayHeapInit( subsdelay_heap_t
*p_heap
)
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
);
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 */
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;
575 p_new_entry
->p_next
= p_heap
->p_head
;
576 p_heap
->p_head
= p_new_entry
;
582 SubsdelayRebuildList( p_heap
);
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
;
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
)
609 p_prev
->p_next
= p_entry
->p_next
;
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
)
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
;
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
) );
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
);
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;
710 /*****************************************************************************
711 * SubsdelayEntryDestroy: Destroy entry
712 *****************************************************************************/
713 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t
*p_entry
)
715 SubpicDestroyClone( p_entry
->p_source
);
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
)
726 VLC_UNUSED( p_heap
);
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
)
738 if( !p_curr
->b_check_empty
) /* subtitle was checked, and it's not empty */
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
;
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) */
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) */
804 [subtitle 1 .........]
806 |<-MinStopStartInterval->|
809 [subtitle 1 ..................]
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
;
825 if( i_offset
< i_min_stop_start_interval
)
827 p_list
[i
]->i_new_stop
= p_list
[j
]->p_source
->i_start
;
834 /* step 3 - enforce min start stop interval rule (shorten delays) */
837 [subtitle 1 ............]
838 [subtitle 2 ....................]
839 |<-MinStartStopInterval->|
841 * and remove the overlapping part:
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
;
859 if( i_offset
< i_min_start_stop_interval
)
861 p_list
[i
]->i_new_stop
= p_list
[j
]->p_source
->i_start
;
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:
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
;
922 int i_result
= VLC_SUCCESS
;
924 p_entry
= p_subpic
->updater
.p_sys
;
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
)
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;
956 /* subpic update isn't necessary, so local update should be called here */
957 SubpicLocalUpdate( p_subpic
, i_ts
);
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
;
972 p_entry
= p_subpic
->updater
.p_sys
;
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
;
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
;
1034 p_entry
= p_subpic
->updater
.p_sys
;
1035 if( !p_entry
|| !p_entry
->p_filter
)
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
);
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
);
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
;
1145 /*****************************************************************************
1146 * SubpicDestroyClone: destroy cloned subpicture (shared references will not
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
)
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
)
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
)
1220 i_new_alpha
= 255 - i_overlapping
* ( 255 - i_min_alpha
);
1224 i_new_alpha
= 255 - i_overlapping
* ( 255 - i_min_alpha
) / 2;
1228 i_new_alpha
= 255 - i_overlapping
* ( 255 - i_min_alpha
) / 3;
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 };
1256 return p_rank
[i_length
- 1];
1259 /*****************************************************************************
1260 * SubsdelayGetTextRank: Calculate text rank
1261 *****************************************************************************/
1262 static int SubsdelayGetTextRank( char *psz_text
)
1268 int i
, i_word_length
, i_rank
;
1277 while ( psz_text
[i
] != '\0' )
1282 if( c
== '\\' && !b_skip_esc
)
1288 if( psz_text
[i
] == '<' )
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
);
1320 if( i_word_length
> 0 )
1322 i_rank
+= SubsdelayGetWordRank( i_word_length
);
1328 /*****************************************************************************
1329 * SubsdelayIsTextEmpty: Check if the text contains spaces only
1330 *****************************************************************************/
1331 static bool SubsdelayIsTextEmpty( const text_segment_t
*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
] )
1341 p_segment
= p_segment
->p_next
;