]> git.sesse.net Git - vlc/blob - modules/video_filter/subsdelay.c
12cbe22e9c1adf168b957b8860901a411c2d3e86
[vlc] / modules / video_filter / subsdelay.c
1 /*****************************************************************************
2  * Preamble
3  *****************************************************************************/
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif
8
9 #include <vlc_common.h>
10 #include <vlc_plugin.h>
11 #include <vlc_filter.h>
12 #include <vlc_subpicture.h>
13 #include <vlc_osd.h>
14 #include <vlc_es.h>
15 #include <stdlib.h>
16
17 /*****************************************************************************
18  * Local constants
19  *****************************************************************************/
20
21 /* descriptions */
22
23 #define SUBSDELAY_HELP N_("Change subtitles delay")
24
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)." )
30
31 #define FACTOR_TEXT N_( "Calculation factor" )
32 #define FACTOR_LONGTEXT N_( "Calculation factor. " \
33     "In Absolute delay mode the factor represents seconds.")
34
35 #define OVERLAP_TEXT N_( "Maximum overlapping subtitles" )
36 #define OVERLAP_LONGTEXT N_( "Maximum number of subtitles allowed at the same time." )
37
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." )
41
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)." )
46
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)." )
51
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)." )
56
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" ) };
60
61 /* parameters */
62
63 #define CFG_PREFIX "subsdelay-"
64
65 #define CFG_MODE                    CFG_PREFIX "mode"
66 #define CFG_FACTOR                  CFG_PREFIX "factor"
67 #define CFG_OVERLAP                 CFG_PREFIX "overlap"
68
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"
73
74
75 /* max subtitles handled on the heap */
76 #define SUBSDELAY_MAX_ENTRIES 16
77
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 )
84
85
86 #define SUBSDELAY_MODE_ABSOLUTE                0
87 #define SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY   1
88 #define SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT 2
89
90
91 /*****************************************************************************
92  * subsdelay_heap_entry_t: Heap entry
93  *****************************************************************************/
94
95 typedef subpicture_updater_sys_t subsdelay_heap_entry_t;
96
97 struct subpicture_updater_sys_t
98 {
99     subpicture_t *p_subpic; /* local subtitle */
100
101     subpicture_t *p_source; /* subtitle source */
102
103     filter_t *p_filter; /* assigned subsdelay filter */
104
105     subsdelay_heap_entry_t *p_next; /* next entry */
106
107     bool b_update_stop; /* new stop value should be calculated */
108
109     bool b_update_ephemer; /* actual stop value is unknown */
110
111     bool b_update_position; /* subtitle position should be updated */
112
113     bool b_check_empty; /* subtitle content should be checked */
114
115     mtime_t i_new_stop; /* new stop value */
116
117     /* last region data*/
118
119     int i_last_region_x;
120
121     int i_last_region_y;
122
123     int i_last_region_align;
124
125     bool b_last_region_saved;
126 };
127
128 /*****************************************************************************
129  * subsdelay_heap_t: Heap
130  *****************************************************************************/
131
132 typedef struct
133 {
134     vlc_mutex_t lock; /* heap global lock */
135
136     subsdelay_heap_entry_t *p_list[SUBSDELAY_MAX_ENTRIES]; /* subtitles entries array */
137
138     subsdelay_heap_entry_t *p_head; /* subtitles entries linked list */
139
140     int i_count; /* subtitles count */
141
142 } subsdelay_heap_t;
143
144
145
146 /*****************************************************************************
147 * filter_sys_t: Subsdelay filter descriptor
148  *****************************************************************************/
149
150 struct filter_sys_t
151 {
152     int i_mode; /* delay calculation mode */
153
154     int i_factor; /* calculation factor */
155
156     int i_overlap; /* max overlap */
157
158     int i_min_alpha; /* oldest subtitle alpha value */
159
160     int64_t i_min_stops_interval;
161
162     int64_t i_min_stop_start_interval;
163
164     int64_t i_min_start_stop_interval;
165
166     subsdelay_heap_t heap; /* subpictures list */
167 };
168
169
170 /*****************************************************************************
171  * Filter functions
172  *****************************************************************************/
173
174 static int SubsdelayCreate( vlc_object_t * );
175
176 static void SubsdelayDestroy( vlc_object_t * );
177
178 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic );
179
180 static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
181         void *p_data );
182
183 /*****************************************************************************
184  * Helper functions
185  *****************************************************************************/
186
187 static void SubsdelayEnforceDelayRules( filter_t *p_filter );
188
189 static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry );
190
191 static void SubsdelayRecalculateDelays( filter_t *p_filter );
192
193 static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha );
194
195 static int SubsdelayGetTextRank( char *psz_text );
196
197 static bool SubsdelayIsTextEmpty( char *psz_text );
198
199 /*****************************************************************************
200  * Subpicture functions
201  *****************************************************************************/
202
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 );
205
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 );
208
209 static void SubpicDestroyWrapper( subpicture_t *p_subpic );
210
211 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts );
212
213 static bool SubpicIsEmpty( subpicture_t* p_subpic );
214
215 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater );
216
217 static void SubpicDestroyClone( subpicture_t *p_subpic );
218
219 /*****************************************************************************
220  * Heap functions
221  *****************************************************************************/
222
223 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap );
224
225 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap );
226
227 static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter );
228
229 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry );
230
231 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap );
232
233 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap );
234
235 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap );
236
237 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_subpic, filter_t *p_filter );
238
239 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry );
240
241 /* heap / entries special functionality */
242
243 static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date );
244
245 static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry );
246
247 /*****************************************************************************
248  * Module descriptor
249  *****************************************************************************/
250
251 vlc_module_begin()
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 )
259
260         add_integer( CFG_MODE, 1, MODE_TEXT, MODE_LONGTEXT, false )
261         change_integer_list( pi_mode_values, ppsz_mode_descriptions )
262
263         add_float_with_range( CFG_FACTOR, 2, 0, 20, NULL, FACTOR_TEXT, FACTOR_LONGTEXT, false )
264
265         add_integer_with_range( CFG_OVERLAP, 3, 1, 4, NULL, OVERLAP_TEXT, OVERLAP_LONGTEXT, false )
266
267         add_integer_with_range( CFG_MIN_ALPHA, 125, 0, 255, NULL, MIN_ALPHA_TEXT, MIN_ALPHA_LONGTEXT, false )
268
269         set_section( N_("Overlap fix"), NULL )
270
271         add_integer( CFG_MIN_STOPS_INTERVAL, 1000, MIN_STOPS_INTERVAL_TEXT, MIN_STOPS_INTERVAL_LONGTEXT, false )
272
273         add_integer( CFG_MIN_START_STOP_INTERVAL, 1000, MIN_START_STOP_INTERVAL_TEXT,
274                      MIN_START_STOP_INTERVAL_LONGTEXT, false )
275
276         add_integer( CFG_MIN_STOP_START_INTERVAL, 1000, MIN_STOP_START_INTERVAL_TEXT,
277                      MIN_STOP_START_INTERVAL_LONGTEXT, false )
278
279     vlc_module_end ()
280
281 static const char * const ppsz_filter_options[] = { "mode", "factor", "overlap", NULL };
282
283 /*****************************************************************************
284  * SubsdelayCreate: Create subsdelay filter
285  *****************************************************************************/
286 static int SubsdelayCreate( vlc_object_t *p_this )
287 {
288     filter_t *p_filter = (filter_t *) p_this;
289     filter_sys_t *p_sys;
290
291     /* allocate structure */
292     p_sys = (filter_sys_t*) malloc( sizeof(filter_sys_t) );
293
294     if( !p_sys )
295     {
296         return VLC_ENOMEM;
297     }
298
299     /* init parameters */
300
301     p_sys->i_mode = var_CreateGetIntegerCommand( p_filter, CFG_MODE );
302     var_AddCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
303
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 );
306
307     p_sys->i_overlap = var_CreateGetIntegerCommand( p_filter, CFG_OVERLAP );
308     var_AddCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
309
310     p_sys->i_min_alpha = var_CreateGetIntegerCommand( p_filter, CFG_MIN_ALPHA );
311     var_AddCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
312
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 );
316
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 );
320
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 );
324
325     p_filter->p_sys = p_sys;
326     p_filter->pf_sub_filter = SubsdelayFilter;
327
328     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg );
329
330     SubsdelayHeapInit( &p_sys->heap );
331
332     return VLC_SUCCESS;
333 }
334
335 /*****************************************************************************
336  * SubsdelayDestroy: Destroy subsdelay filter
337  *****************************************************************************/
338 static void SubsdelayDestroy( vlc_object_t *p_this )
339 {
340     filter_t *p_filter = (filter_t *) p_this;
341     filter_sys_t *p_sys = p_filter->p_sys;
342
343     SubsdelayHeapDestroy( &p_sys->heap );
344
345     /* destroy parameters */
346
347     var_DelCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
348     var_Destroy( p_filter, CFG_MODE );
349
350     var_DelCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
351     var_Destroy( p_filter, CFG_FACTOR );
352
353     var_DelCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
354     var_Destroy( p_filter, CFG_OVERLAP );
355
356     var_DelCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
357     var_Destroy( p_filter, CFG_MIN_ALPHA );
358
359     var_DelCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
360     var_Destroy( p_filter, CFG_MIN_STOPS_INTERVAL );
361
362     var_DelCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
363     var_Destroy( p_filter, CFG_MIN_STOP_START_INTERVAL );
364
365     var_DelCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
366     var_Destroy( p_filter, CFG_MIN_START_STOP_INTERVAL );
367
368     free( p_sys );
369 }
370
371 /*****************************************************************************
372  * SubsdelayFilter: Filter new subpicture
373  *****************************************************************************/
374 static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic )
375 {
376     subsdelay_heap_t *p_heap;
377     subsdelay_heap_entry_t *p_entry;
378
379     if( !p_subpic->b_subtitle )
380     {
381         return p_subpic;
382     }
383
384     if( SubpicIsEmpty( p_subpic ) )
385     {
386         /* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway,
387            so this subtitle can be dropped */
388
389         subpicture_Delete( p_subpic );
390
391         return NULL;
392     }
393
394     p_heap = &p_filter->p_sys->heap;
395
396     /* add subpicture to the heap */
397
398     SubsdelayHeapLock( p_heap );
399
400     p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter );
401     if( !p_entry )
402     {
403         SubsdelayHeapUnlock( p_heap );
404
405         msg_Err(p_filter, "Can't add subpicture to the heap");
406
407         return p_subpic;
408     }
409
410     p_subpic = p_entry->p_subpic; /* get the local subpic */
411
412     if( p_subpic->b_ephemer )
413     {
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 */
416
417         p_subpic->i_stop = p_subpic->i_start + 20000000; /* start + 20 sec */
418         p_subpic->b_ephemer = false;
419     }
420
421
422     SubsdelayEnforceDelayRules( p_filter );
423
424     SubsdelayHeapUnlock( p_heap );
425
426     return p_subpic;
427 }
428
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,
433         void *p_data )
434 {
435     filter_sys_t *p_sys = (filter_sys_t *) p_data;
436
437     VLC_UNUSED( oldval );
438
439     SubsdelayHeapLock( &p_sys->heap );
440
441     if( !strcmp( psz_var, CFG_MODE ) )
442     {
443         p_sys->i_mode = newval.i_int;
444     }
445     else if( !strcmp( psz_var, CFG_FACTOR ) )
446     {
447         p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( newval.f_float );
448     }
449     else if( !strcmp( psz_var, CFG_OVERLAP ) )
450     {
451         p_sys->i_overlap = newval.i_int;
452     }
453     else if( !strcmp( psz_var, CFG_MIN_ALPHA ) )
454     {
455         p_sys->i_min_alpha = newval.i_int;
456     }
457     else if( !strcmp( psz_var, CFG_MIN_STOPS_INTERVAL ) )
458     {
459         p_sys->i_min_stops_interval = MILLISEC_TO_MICROSEC( newval.i_int );
460     }
461     else if( !strcmp( psz_var, CFG_MIN_STOP_START_INTERVAL ) )
462     {
463         p_sys->i_min_stop_start_interval = MILLISEC_TO_MICROSEC( newval.i_int );
464     }
465     else if( !strcmp( psz_var, CFG_MIN_START_STOP_INTERVAL ) )
466     {
467         p_sys->i_min_start_stop_interval = MILLISEC_TO_MICROSEC( newval.i_int );
468     }
469     else
470     {
471         SubsdelayHeapUnlock( &p_sys->heap );
472         return VLC_ENOVAR;
473     }
474
475     SubsdelayRecalculateDelays( (filter_t *) p_this );
476
477     SubsdelayHeapUnlock( &p_sys->heap );
478     return VLC_SUCCESS;
479 }
480
481 /*****************************************************************************
482  * SubsdelayHeapInit: Initialize heap
483  *****************************************************************************/
484 static void SubsdelayHeapInit( subsdelay_heap_t *p_heap )
485 {
486     p_heap->i_count = 0;
487     p_heap->p_head = NULL;
488
489     vlc_mutex_init( &p_heap->lock );
490 }
491
492 /*****************************************************************************
493  * SubsdelayHeapDestroy: Destroy the heap and remove its entries
494  *****************************************************************************/
495 static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap )
496 {
497     subsdelay_heap_entry_t *p_entry;
498
499     SubsdelayHeapLock( p_heap );
500
501     for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
502     {
503         p_entry->p_subpic->i_stop = p_entry->p_source->i_stop;
504
505         p_entry->p_filter = NULL;
506     }
507
508     SubsdelayHeapUnlock( p_heap );
509
510     vlc_mutex_destroy( &p_heap->lock );
511 }
512
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 )
517 {
518     subsdelay_heap_entry_t *p_entry, *p_last, *p_new_entry;
519
520     if( p_heap->i_count >= SUBSDELAY_MAX_ENTRIES )
521     {
522         return NULL; /* the heap is full */
523     }
524
525     p_new_entry = SubsdelayEntryCreate( p_subpic, p_filter );
526
527     if( !p_new_entry )
528     {
529         return NULL;
530     }
531
532
533     p_last = NULL;
534
535     for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
536     {
537         if( p_entry->p_source->i_start > p_subpic->i_start )
538         {
539             /* the new entry should be inserted before p_entry */
540             break;
541         }
542
543         p_last = p_entry;
544     }
545
546     if( p_last )
547     {
548         p_new_entry->p_next = p_last->p_next;
549         p_last->p_next = p_new_entry;
550
551
552         if( p_last->b_update_ephemer )
553         {
554             /* the correct stop value can be determined */
555
556             p_last->p_source->i_stop = p_new_entry->p_source->i_start;
557             p_last->b_update_ephemer = false;
558         }
559     }
560     else
561     {
562         p_new_entry->p_next = p_heap->p_head;
563         p_heap->p_head = p_new_entry;
564     }
565
566
567     /* rebuild list */
568
569     SubsdelayRebuildList( p_heap );
570
571     return p_new_entry;
572 }
573
574 /*****************************************************************************
575  * SubsdelayHeapRemove: Remove entry
576  *****************************************************************************/
577 static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry )
578 {
579     subsdelay_heap_entry_t *p_curr, *p_prev;
580
581     p_prev = NULL;
582
583     for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
584     {
585         if( p_curr == p_entry )
586         {
587             break;
588         }
589
590         p_prev = p_curr;
591     }
592
593     if( p_prev )
594     {
595         p_prev->p_next = p_entry->p_next;
596     }
597     else
598     {
599         p_heap->p_head = p_entry->p_next;
600     }
601
602     p_entry->p_filter = NULL;
603
604     SubsdelayRebuildList( p_heap );
605 }
606
607
608 static void SubsdelayRebuildList( subsdelay_heap_t *p_heap )
609 {
610     subsdelay_heap_entry_t *p_curr;
611     int i_index;
612
613     i_index = 0;
614     for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
615     {
616         p_heap->p_list[i_index] = p_curr;
617         i_index++;
618     }
619
620     p_heap->i_count = i_index;
621 }
622
623 /*****************************************************************************
624  * SubsdelayHeapLock: Lock the heap
625  *****************************************************************************/
626 static void SubsdelayHeapLock( subsdelay_heap_t *p_heap )
627 {
628     vlc_mutex_lock( &p_heap->lock );
629 }
630
631 /*****************************************************************************
632  * SubsdelayHeapUnlock: Unlock the heap
633  *****************************************************************************/
634 static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap )
635 {
636     vlc_mutex_unlock( &p_heap->lock );
637 }
638
639
640 /*****************************************************************************
641  * SubsdelayHeapCreateEntry: Create new entry
642  *****************************************************************************/
643 static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_source, filter_t *p_filter )
644 {
645     subsdelay_heap_entry_t *p_entry;
646
647     subpicture_t *p_new_subpic;
648
649     subpicture_updater_t updater;
650
651     /* allocate structure */
652
653     p_entry = (subsdelay_heap_entry_t *) malloc( sizeof( subsdelay_heap_entry_t ) );
654
655     if( !p_entry )
656     {
657         return NULL;
658     }
659
660     /* initialize local updater */
661
662     updater.p_sys = p_entry;
663     updater.pf_validate = SubpicValidateWrapper;
664     updater.pf_update = SubpicUpdateWrapper;
665     updater.pf_destroy = SubpicDestroyWrapper;
666
667     /* create new subpic */
668
669     p_new_subpic = SubpicClone( p_source,  &updater );
670
671     if( !p_new_subpic )
672     {
673         free( p_entry );
674         return NULL;
675     }
676
677     /* initialize entry */
678
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;
692
693     return p_entry;
694 }
695
696 /*****************************************************************************
697  * SubsdelayEntryDestroy: Destroy entry
698  *****************************************************************************/
699 static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry )
700 {
701     SubpicDestroyClone( p_entry->p_source );
702     free( p_entry );
703 }
704
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 )
709 {
710     subsdelay_heap_entry_t *p_curr;
711     int i_overlaps;
712
713     VLC_UNUSED( p_heap );
714
715     i_overlaps = 0;
716
717     for( p_curr = p_entry->p_next; p_curr != NULL; p_curr = p_curr->p_next )
718     {
719         if( p_curr->p_source->i_start > i_date )
720         {
721             break;
722         }
723
724         if( !p_curr->b_check_empty ) /* subtitle was checked, and it's not empty */
725         {
726             i_overlaps++;
727         }
728     }
729
730     return i_overlaps;
731 }
732
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 )
738 {
739     if( !p_entry->b_update_stop )
740     {
741         p_entry->p_subpic->i_stop = p_entry->i_new_stop - 100000; /* 0.1 sec less */
742     }
743 }
744
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 )
750 {
751     subsdelay_heap_entry_t ** p_list;
752     int i, j, i_count, i_overlap;
753     int64_t i_offset;
754     int64_t i_min_stops_interval;
755     int64_t i_min_stop_start_interval;
756     int64_t i_min_start_stop_interval;
757
758     p_list = p_filter->p_sys->heap.p_list;
759     i_count = p_filter->p_sys->heap.i_count;
760
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;
765
766     /* step 1 - enforce min stops interval rule (extend delays) */
767
768     /* look for:
769     [subtitle 1 ..............]
770            [subtitle 2 ..............]
771                               |<-MinStopsInterval->|
772
773      * and extend newer subtitle:
774     [subtitle 1 ..............]
775            [subtitle 2 ............................]
776                               |<-MinStopsInterval->|
777     */
778
779     for( i = 0; i < i_count - 1; i++ )
780     {
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 );
783     }
784
785     /* step 2 - enforce min stop start interval rule (extend delays) */
786
787     /* look for:
788     [subtitle 1 .........]
789                                    [subtitle 2 ....]
790           |<-MinStopStartInterval->|
791
792      * and fill the gap:
793     [subtitle 1 ..................]
794                                    [subtitle 2 ....]
795           |<-MinStopStartInterval->|
796     */
797
798     for( i = 0; i < i_count; i++ )
799     {
800         for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
801         {
802             i_offset = p_list[j]->p_source->i_start - p_list[i]->i_new_stop;
803
804             if( i_offset <= 0 )
805             {
806                 continue;
807             }
808
809             if( i_offset < i_min_stop_start_interval )
810             {
811                 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
812             }
813
814             break;
815         }
816     }
817
818     /* step 3 - enforce min start stop interval rule (shorten delays) */
819
820     /* look for:
821     [subtitle 1 ............]
822                     [subtitle 2 ....................]
823                     |<-MinStartStopInterval->|
824
825      * and remove the overlapping part:
826     [subtitle 1 ...]
827                     [subtitle 2 ....................]
828                     |<-MinStartStopInterval->|
829     */
830
831
832     for( i = 0; i < i_count; i++ )
833     {
834         for( j = i + 1; j < __MIN( i_count, i + 1 + i_overlap ); j++ )
835         {
836             i_offset = p_list[i]->i_new_stop - p_list[j]->p_source->i_start;
837
838             if( i_offset <= 0 )
839             {
840                 break;
841             }
842
843             if( i_offset < i_min_start_stop_interval )
844             {
845                 p_list[i]->i_new_stop = p_list[j]->p_source->i_start;
846                 break;
847             }
848         }
849     }
850
851     /* step 4 - enforce max overlapping rule (shorten delays)*/
852
853     /* look for: (overlap = 2)
854     [subtitle 1 ..............]
855              [subtitle 2 ..............]
856                       [subtitle 3 ..............]
857
858
859      * and cut older subtitle:
860     [subtitle 1 .....]
861              [subtitle 2 ..............]
862                       [subtitle 3 ..............]
863     */
864
865     for( i = 0; i < i_count - i_overlap; i++ )
866     {
867         if( p_list[i]->i_new_stop > p_list[i + i_overlap]->p_source->i_start )
868         {
869             p_list[i]->i_new_stop = p_list[i + i_overlap]->p_source->i_start;
870         }
871     }
872
873     /* finally - update all */
874
875     for( i = 0; i < i_count; i++ )
876     {
877         SubsdelayEntryNewStopValueUpdated( p_list[i] );
878     }
879 }
880
881 /*****************************************************************************
882  * SubsdelayRecalculateDelays: Recalculate subtitles delay after changing
883  *     one of the filter's parameters
884  *****************************************************************************/
885 static void SubsdelayRecalculateDelays( filter_t *p_filter )
886 {
887     subsdelay_heap_entry_t *p_curr;
888
889     for( p_curr = p_filter->p_sys->heap.p_head; p_curr != NULL; p_curr = p_curr->p_next )
890     {
891         if( !p_curr->b_update_ephemer )
892         {
893             p_curr->i_new_stop = p_curr->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_curr );
894             p_curr->b_update_stop = false;
895         }
896     }
897
898     SubsdelayEnforceDelayRules( p_filter );
899 }
900
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 )
906 {
907     subsdelay_heap_entry_t *p_entry;
908     mtime_t i_new_ts;
909     int i_result;
910
911     p_entry = p_subpic->updater.p_sys;
912     if( !p_entry )
913     {
914         return VLC_SUCCESS;
915     }
916
917     /* call source validate */
918     if( p_entry->p_source->updater.pf_validate )
919     {
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 );
923
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 );
926     }
927
928
929     p_entry->b_last_region_saved = false;
930
931     if( p_subpic->p_region )
932     {
933         /* save copy */
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;
937
938         p_entry->b_last_region_saved = true;
939     }
940
941     if( !i_result )
942     {
943         /* subpic update isn't necessary, so local update should be called here */
944         SubpicLocalUpdate( p_subpic, i_ts );
945     }
946
947     return i_result;
948 }
949
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 )
955 {
956     subsdelay_heap_entry_t *p_entry;
957     mtime_t i_new_ts;
958
959     p_entry = p_subpic->updater.p_sys;
960     if( !p_entry )
961     {
962         return;
963     }
964
965     /* call source update */
966     if( p_entry->p_source->updater.pf_update )
967     {
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 );
971
972         p_entry->p_source->p_region = p_entry->p_subpic->p_region;
973
974         p_entry->p_source->updater.pf_update( p_entry->p_source, p_fmt_src, p_fmt_dst, i_new_ts );
975
976         p_entry->p_subpic->p_region = p_entry->p_source->p_region;
977     }
978
979     SubpicLocalUpdate( p_subpic, i_ts );
980 }
981
982 /*****************************************************************************
983  * SubpicDestroyCallback: Subpicture destroy callback
984  *****************************************************************************/
985 static void SubpicDestroyWrapper( subpicture_t *p_subpic )
986 {
987     subsdelay_heap_entry_t *p_entry;
988     subsdelay_heap_t *p_heap;
989
990     p_entry = p_subpic->updater.p_sys;
991
992     if( !p_entry )
993     {
994         return;
995     }
996
997     if( p_entry->p_filter )
998     {
999         p_heap = &p_entry->p_filter->p_sys->heap;
1000
1001         SubsdelayHeapLock( p_heap );
1002         SubsdelayHeapRemove( p_heap, p_entry );
1003         SubsdelayHeapUnlock( p_heap );
1004     }
1005
1006     SubsdelayEntryDestroy( p_entry );
1007 }
1008
1009 /*****************************************************************************
1010  * SubpicLocalUpdate: rewrite some of the subpicture parameters
1011  *****************************************************************************/
1012 static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts )
1013 {
1014     subsdelay_heap_entry_t *p_entry;
1015     subsdelay_heap_t *p_heap;
1016     filter_t *p_filter;
1017
1018     int i_overlapping;
1019
1020     p_entry = p_subpic->updater.p_sys;
1021     if( !p_entry || !p_entry->p_filter )
1022     {
1023         return;
1024     }
1025
1026     p_filter = p_entry->p_filter;
1027     p_heap = &p_filter->p_sys->heap;
1028
1029     SubsdelayHeapLock( p_heap );
1030
1031     if( p_entry->b_check_empty && p_subpic->p_region )
1032     {
1033         if( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1034             SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) )
1035         {
1036             /* remove empty subtitle */
1037
1038             p_subpic->b_ephemer = false;
1039             p_subpic->i_stop = p_subpic->i_start;
1040
1041             SubsdelayHeapRemove( p_heap, p_entry );
1042
1043             SubsdelayHeapUnlock( p_heap );
1044
1045             return;
1046         }
1047
1048         p_entry->b_check_empty = false;
1049     }
1050
1051     if( p_entry->b_update_stop && !p_entry->b_update_ephemer )
1052     {
1053         p_entry->i_new_stop = p_entry->p_source->i_start + SubsdelayEstimateDelay( p_filter, p_entry );
1054         p_entry->b_update_stop = false;
1055
1056         SubsdelayEnforceDelayRules( p_filter );
1057     }
1058
1059     i_overlapping = SubsdelayHeapCountOverlap( p_heap, p_entry, i_ts );
1060
1061     p_subpic->i_alpha = SubsdelayCalculateAlpha( p_filter, i_overlapping, p_entry->p_source->i_alpha );
1062
1063     if( p_entry->b_update_position )
1064     {
1065         p_subpic->b_absolute = false;
1066
1067         if( p_subpic->p_region )
1068         {
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;
1073         }
1074
1075         p_entry->b_update_position = false;
1076     }
1077     else if( p_entry->b_last_region_saved )
1078     {
1079         p_subpic->b_absolute = true;
1080
1081         if( p_subpic->p_region )
1082         {
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;
1086         }
1087     }
1088
1089     SubsdelayHeapUnlock( p_heap );
1090 }
1091
1092 /*****************************************************************************
1093  * SubpicIsEmpty: subpic region contains empty string
1094  *****************************************************************************/
1095 static bool SubpicIsEmpty( subpicture_t* p_subpic )
1096 {
1097     return ( p_subpic->p_region && ( SubsdelayIsTextEmpty( p_subpic->p_region->psz_html ) ||
1098                                      SubsdelayIsTextEmpty( p_subpic->p_region->psz_text ) ) );
1099 }
1100
1101 /*****************************************************************************
1102  * SubpicClone: Clone subpicture (shallow copy)
1103  *****************************************************************************/
1104 static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater )
1105 {
1106     subpicture_t *p_subpic;
1107     subpicture_updater_t subpic_updater;
1108     subpicture_private_t *p_subpic_private;
1109
1110     p_subpic = subpicture_New( updater );
1111
1112     if( !p_subpic )
1113     {
1114         return NULL;
1115     }
1116
1117     /* save private members */
1118     subpic_updater = p_subpic->updater;
1119     p_subpic_private = p_subpic->p_private;
1120
1121     /* copy the entire struct */
1122     memcpy( p_subpic, p_source, sizeof( subpicture_t ) );
1123
1124     /* restore private members */
1125     p_subpic->updater = subpic_updater;
1126     p_subpic->p_private = p_subpic_private;
1127
1128     return p_subpic;
1129 }
1130
1131 /*****************************************************************************
1132  * SubpicDestroyClone: destroy cloned subpicture (shared references will not
1133  *     be destroyed)
1134  *****************************************************************************/
1135 static void SubpicDestroyClone( subpicture_t *p_subpic )
1136 {
1137     p_subpic->p_region = NULL; /* don't destroy region */
1138     subpicture_Delete( p_subpic );
1139 }
1140
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 )
1146 {
1147     int i_mode;
1148     int i_factor;
1149     int i_rank;
1150
1151     i_mode = p_filter->p_sys->i_mode;
1152     i_factor = p_filter->p_sys->i_factor;
1153
1154     if( i_mode == SUBSDELAY_MODE_ABSOLUTE )
1155     {
1156         return ( p_entry->p_source->i_stop - p_entry->p_source->i_start + INT_FACTOR_TO_MICROSEC( i_factor ) );
1157     }
1158
1159     if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT )
1160     {
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 ) )
1163         {
1164             if( p_entry->p_subpic->p_region->psz_text )
1165             {
1166                 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_text );
1167             }
1168             else
1169             {
1170                 i_rank = SubsdelayGetTextRank( p_entry->p_subpic->p_region->psz_html );
1171             }
1172
1173             return ( i_rank * INT_FACTOR_TO_RANK_FACTOR( i_factor ) );
1174         }
1175
1176         /* content is unavailable, calculation mode should be based on source delay */
1177         i_mode = SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY;
1178     }
1179
1180     if( i_mode == SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY )
1181     {
1182         return ( ( i_factor * ( p_entry->p_source->i_stop - p_entry->p_source->i_start ) ) / INT_FACTOR_BASE );
1183     }
1184
1185     return 10000000; /* 10 sec */
1186 }
1187
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 )
1193 {
1194     int i_new_alpha;
1195     int i_min_alpha;
1196
1197     i_min_alpha = p_filter->p_sys->i_min_alpha;
1198
1199     if( i_overlapping > p_filter->p_sys->i_overlap - 1)
1200     {
1201         i_overlapping = p_filter->p_sys->i_overlap - 1;
1202     }
1203
1204     switch ( p_filter->p_sys->i_overlap )
1205     {
1206     case 1:
1207         i_new_alpha = 255;
1208         break;
1209
1210     case 2:
1211         i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha );
1212         break;
1213
1214     case 3:
1215         i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 2;
1216         break;
1217
1218     default:
1219         i_new_alpha = 255 - i_overlapping * ( 255 - i_min_alpha ) / 3;
1220         break;
1221     }
1222
1223     return ( i_source_alpha * i_new_alpha ) / 255;
1224 }
1225
1226 /*****************************************************************************
1227  * SubsdelayGetWordRank: Calculate single word rank according to its length
1228  *****************************************************************************/
1229 static int SubsdelayGetWordRank( int i_length )
1230 {
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] ); */
1233
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 };
1236
1237     if( i_length < 1 )
1238     {
1239         return 0;
1240     }
1241
1242     if( i_length > 20 )
1243     {
1244         i_length = 20;
1245     }
1246
1247     return p_rank[i_length - 1];
1248 }
1249
1250 /*****************************************************************************
1251  * SubsdelayGetTextRank: Calculate text rank
1252  *****************************************************************************/
1253 static int SubsdelayGetTextRank( char *psz_text )
1254 {
1255     bool b_skip_esc;
1256     bool b_skip_tag;
1257     char c;
1258
1259     int i, i_word_length, i_rank;
1260
1261     i = 0;
1262     i_word_length = 0;
1263     i_rank = 0;
1264
1265     b_skip_esc = false;
1266     b_skip_tag = false;
1267
1268     while ( psz_text[i] != '\0' )
1269     {
1270         c = psz_text[i];
1271         i++;
1272
1273         if( c == '\\' && !b_skip_esc )
1274         {
1275             b_skip_esc = true;
1276             continue;
1277         }
1278
1279         if( psz_text[i] == '<' )
1280         {
1281             b_skip_tag = true;
1282             continue;
1283         }
1284
1285         if( !b_skip_esc && !b_skip_tag )
1286         {
1287             if( c == ' ' || c == ',' || c == '.' || c == '-' || c == '?' || c == '!' ) /* common delimiters */
1288             {
1289                 if( i_word_length > 0 )
1290                 {
1291                     i_rank += SubsdelayGetWordRank( i_word_length );
1292                 }
1293
1294                 i_word_length = 0;
1295             }
1296             else
1297             {
1298                 i_word_length++;
1299             }
1300         }
1301
1302         b_skip_esc = false;
1303
1304         if( c == '>' )
1305         {
1306             b_skip_tag = false;
1307         }
1308
1309     }
1310
1311     if( i_word_length > 0 )
1312     {
1313         i_rank += SubsdelayGetWordRank( i_word_length );
1314     }
1315
1316     return i_rank;
1317 }
1318
1319 /*****************************************************************************
1320  * SubsdelayIsTextEmpty: Check if the text contains spaces only
1321  *****************************************************************************/
1322 static bool SubsdelayIsTextEmpty( char *psz_text )
1323 {
1324     if( !psz_text )
1325     {
1326         return false;
1327     }
1328
1329     psz_text += strspn( psz_text, " " );
1330     return !( *psz_text );
1331 }