• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

githubのコードからのfolk


Commit MetaInfo

Revisione586e3d36882d5854b3bfb13cceb6d5e5f190d9d (tree)
Time2011-02-25 10:08:33
Authordavidfstr <davidfstr@b64f...>
Commiterdavidfstr

Log Message

Add temporally overlapping subtitle support.
* New subtitle sync algorithm added to sync work-object ("simultaneous").

Classic algorithm preserved but disabled.

* Render work-object now supports queueing a /list/ of subtitles.
* FIFOs have been extended to support pushing/popping buffer-lists as single elements.
* Added SUBSYNC_VERBOSE_TIMING flag to debug timing issues related to subtitle display.

Observable behaviors changed in the new subtitle sync algorithm:
* Temporally overlapping subtitles are no longer trimmed to be non-overlapping.
* Subtitles less than two seconds long are no longer artificially extended. Sorry, Indochine fans.
* Subtitles that stop before they start will never be displayed. The old algorithm will display such subtitles if they begin in the future (relative to the current video frame being processed).

git-svn-id: svn://localhost/HandBrake/trunk@3804 b64f7644-9d1e-0410-96f1-a4d463321fa5

Change Summary

Incremental Difference

--- a/libhb/decssasub.c
+++ b/libhb/decssasub.c
@@ -50,6 +50,8 @@ typedef enum {
5050 sec * 1000L +\
5151 centi * 10L ) )
5252
53+#define SSA_VERBOSE_PACKETS 0
54+
5355 static StyleSet ssa_parse_style_override( uint8_t *pos, StyleSet prevStyles )
5456 {
5557 StyleSet nextStyles = prevStyles;
@@ -592,6 +594,10 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
592594 hb_buffer_t * in = *buf_in;
593595 hb_buffer_t * out_list = NULL;
594596
597+#if SSA_VERBOSE_PACKETS
598+ printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->start/90, in->stop/90, in->size, in->data);
599+#endif
600+
595601 if ( in->size > 0 ) {
596602 out_list = ssa_decode_packet(w, in);
597603 } else {
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -190,6 +190,7 @@ void hb_buffer_realloc( hb_buffer_t * b, int size )
190190 }
191191 }
192192
193+// Frees the specified buffer list.
193194 void hb_buffer_close( hb_buffer_t ** _b )
194195 {
195196 hb_buffer_t * b = *_b;
@@ -294,6 +295,8 @@ float hb_fifo_percent_full( hb_fifo_t * f )
294295 return ret;
295296 }
296297
298+// Pulls the first packet out of this FIFO, blocking until such a packet is available.
299+// Returns NULL if this FIFO has been closed or flushed.
297300 hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * f )
298301 {
299302 hb_buffer_t * b;
@@ -323,6 +326,7 @@ hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * f )
323326 return b;
324327 }
325328
329+// Pulls a packet out of this FIFO, or returns NULL if no packet is available.
326330 hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
327331 {
328332 hb_buffer_t * b;
@@ -368,6 +372,8 @@ hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * f )
368372 return b;
369373 }
370374
375+// Returns the first packet in the specified FIFO.
376+// If the FIFO is empty, returns NULL.
371377 hb_buffer_t * hb_fifo_see( hb_fifo_t * f )
372378 {
373379 hb_buffer_t * b;
@@ -400,6 +406,8 @@ hb_buffer_t * hb_fifo_see2( hb_fifo_t * f )
400406 return b;
401407 }
402408
409+// Waits until the specified FIFO is no longer full or until FIFO_TIMEOUT milliseconds have elapsed.
410+// Returns whether the FIFO is non-full upon return.
403411 int hb_fifo_full_wait( hb_fifo_t * f )
404412 {
405413 int result;
@@ -415,6 +423,8 @@ int hb_fifo_full_wait( hb_fifo_t * f )
415423 return result;
416424 }
417425
426+// Pushes the specified buffer onto the specified FIFO,
427+// blocking until the FIFO has space available.
418428 void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b )
419429 {
420430 if( !b )
@@ -451,6 +461,7 @@ void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b )
451461 hb_unlock( f->lock );
452462 }
453463
464+// Appends the specified packet list to the end of the specified FIFO.
454465 void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
455466 {
456467 if( !b )
@@ -482,6 +493,7 @@ void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
482493 hb_unlock( f->lock );
483494 }
484495
496+// Prepends the specified packet list to the start of the specified FIFO.
485497 void hb_fifo_push_head( hb_fifo_t * f, hb_buffer_t * b )
486498 {
487499 hb_buffer_t * tmp;
@@ -519,6 +531,29 @@ void hb_fifo_push_head( hb_fifo_t * f, hb_buffer_t * b )
519531 hb_unlock( f->lock );
520532 }
521533
534+// Pushes a list of packets onto the specified FIFO as a single element.
535+void hb_fifo_push_list_element( hb_fifo_t *fifo, hb_buffer_t *buffer_list )
536+{
537+ hb_buffer_t *container = hb_buffer_init( 0 );
538+ // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next')
539+ // to carry the list inside a single "container" buffer
540+ container->next_subpicture = buffer_list;
541+
542+ hb_fifo_push( fifo, container );
543+}
544+
545+// Removes a list of packets from the specified FIFO that were stored as a single element.
546+hb_buffer_t *hb_fifo_get_list_element( hb_fifo_t *fifo )
547+{
548+ hb_buffer_t *container = hb_fifo_get( fifo );
549+ // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next')
550+ // to carry the list inside a single "container" buffer
551+ hb_buffer_t *buffer_list = container->next_subpicture;
552+ hb_buffer_close( &container );
553+
554+ return buffer_list;
555+}
556+
522557 void hb_fifo_close( hb_fifo_t ** _f )
523558 {
524559 hb_fifo_t * f = *_f;
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -126,6 +126,8 @@ void hb_fifo_push( hb_fifo_t *, hb_buffer_t * );
126126 void hb_fifo_push_wait( hb_fifo_t *, hb_buffer_t * );
127127 int hb_fifo_full_wait( hb_fifo_t * f );
128128 void hb_fifo_push_head( hb_fifo_t *, hb_buffer_t * );
129+void hb_fifo_push_list_element( hb_fifo_t *fifo, hb_buffer_t *buffer_list );
130+hb_buffer_t * hb_fifo_get_list_element( hb_fifo_t *fifo );
129131 void hb_fifo_close( hb_fifo_t ** );
130132 void hb_fifo_flush( hb_fifo_t * f );
131133
--- a/libhb/render.c
+++ b/libhb/render.c
@@ -67,6 +67,8 @@ static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
6767 return(&data[(y>>1) * w2 + (x>>1) + width*height + w2*h2]);
6868 }
6969
70+// Draws the specified PICTURESUB subtitle on the specified video packet.
71+// Disposes the subtitle afterwards.
7072 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
7173 hb_buffer_t ** _sub )
7274 {
@@ -397,11 +399,11 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
397399 /* Push subtitles onto queue just in case we need to delay a frame */
398400 if( in->sub )
399401 {
400- hb_fifo_push( pv->subtitle_queue, in->sub );
402+ hb_fifo_push_list_element( pv->subtitle_queue, in->sub );
401403 }
402404 else
403405 {
404- hb_fifo_push( pv->subtitle_queue, hb_buffer_init(0) );
406+ hb_fifo_push_list_element( pv->subtitle_queue, NULL );
405407 }
406408
407409 /* If there's a chapter mark remember it in case we delay or drop its frame */
@@ -451,11 +453,15 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
451453 }
452454 else if( result == FILTER_DELAY )
453455 {
456+ // Process the current frame later
457+
454458 buf_tmp_in = NULL;
455459 break;
456460 }
457461 else if( result == FILTER_DROP )
458462 {
463+ // Drop the current frame
464+
459465 /* We need to compensate for the time lost by dropping this frame.
460466 Spread its duration out in quarters, because usually dropped frames
461467 maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones.
@@ -471,15 +477,28 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
471477 pv->total_lost_time += temp_duration;
472478 pv->dropped_frames++;
473479
474- /* Pop the frame's subtitle and dispose of it. */
475- hb_buffer_t * subpicture_list = hb_fifo_get( pv->subtitle_queue );
476- hb_buffer_t * subpicture;
477- hb_buffer_t * subpicture_next;
478- for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
480+ /* Pop the frame's subtitle list and dispose of it. */
481+ hb_buffer_t * sub_list = hb_fifo_get_list_element( pv->subtitle_queue );
482+ hb_buffer_t * sub;
483+ hb_buffer_t * sub_next;
484+ for ( sub = sub_list; sub; sub = sub_next )
479485 {
480- subpicture_next = subpicture->next_subpicture;
481- hb_buffer_close( &subpicture );
486+ sub_next = sub->next;
487+ // XXX: Prevent hb_buffer_close from killing the whole list
488+ // before we finish iterating over it
489+ sub->next = NULL;
490+
491+ hb_buffer_t * subpicture_list = sub;
492+ hb_buffer_t * subpicture;
493+ hb_buffer_t * subpicture_next;
494+ for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
495+ {
496+ subpicture_next = subpicture->next_subpicture;
497+
498+ hb_buffer_close( &subpicture );
499+ }
482500 }
501+
483502 buf_tmp_in = NULL;
484503 break;
485504 }
@@ -503,16 +522,28 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
503522 pv->last_stop[0] = pv->last_start[0] + (buf_tmp_in->stop - buf_tmp_in->start);
504523 }
505524
506- /* Apply subtitles */
525+ /* Apply subtitles and dispose them */
507526 if( buf_tmp_in )
508527 {
509- hb_buffer_t * subpicture_list = hb_fifo_get( pv->subtitle_queue );
510- hb_buffer_t * subpicture;
511- hb_buffer_t * subpicture_next;
512- for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
528+ hb_buffer_t * sub_list = hb_fifo_get_list_element( pv->subtitle_queue );
529+ hb_buffer_t * sub;
530+ hb_buffer_t * sub_next;
531+ for ( sub = sub_list; sub; sub = sub_next )
513532 {
514- subpicture_next = subpicture->next_subpicture;
515- ApplySub( job, buf_tmp_in, &subpicture );
533+ sub_next = sub->next;
534+ // XXX: Prevent hb_buffer_close inside ApplySub from killing the whole list
535+ // before we finish iterating over it
536+ sub->next = NULL;
537+
538+ hb_buffer_t * subpicture_list = sub;
539+ hb_buffer_t * subpicture;
540+ hb_buffer_t * subpicture_next;
541+ for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
542+ {
543+ subpicture_next = subpicture->next_subpicture;
544+
545+ ApplySub( job, buf_tmp_in, &subpicture );
546+ }
516547 }
517548 }
518549
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -68,6 +68,9 @@ typedef struct
6868 uint64_t st_counts[4];
6969 uint64_t st_dates[4];
7070 uint64_t st_first;
71+
72+ /* Subtitles */
73+ hb_buffer_t * sub_list; /* list of subtitles to be passed thru or rendered */
7174 } hb_sync_video_t;
7275
7376 struct hb_work_private_s
@@ -549,11 +552,204 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
549552 }
550553
551554 /*
552- * Track the video sequence number localy so that we can sync the audio
555+ * Track the video sequence number locally so that we can sync the audio
553556 * to it using the sequence number as well as the PTS.
554557 */
555558 sync->video_sequence = cur->sequence;
559+
560+ /* Process subtitles that apply to this video frame */
561+
562+ // NOTE: There is no logic in either subtitle-sync algorithm that waits for the
563+ // subtitle-decoder if it is lagging behind the video-decoder.
564+ //
565+ // Therefore there is the implicit assumption that the subtitle-decoder
566+ // is always faster than the video-decoder. This assumption is definitely
567+ // incorrect in some cases where the SSA subtitle decoder is used.
568+ // Enable the SUBSYNC_VERBOSE_TIMING flag below to debug.
569+
570+#define SUBSYNC_ALGORITHM_SIMULTANEOUS 1
571+#define SUBSYNC_ALGORITHM_CLASSIC 0
572+
573+/*
574+ * Enables logging of three kinds of events:
575+ * SUB***: Subtitle received by sync object
576+ * SUB+++: Subtitle now shown
577+ * SUB---: Subtitle now hidden and disposed
578+ *
579+ * Lead times on SUB*** events should be positive.
580+ * Negative lead times lead to lag times on SUB+++ or the complete drop of a subtitle.
581+ * Lag times on SUB+++ and SUB--- should be small positive values in the 0-40ms range.
582+ */
583+#define SUBSYNC_VERBOSE_TIMING 0
584+
585+#if SUBSYNC_ALGORITHM_SIMULTANEOUS
586+ #define sub_list sync->sub_list
587+ /*
588+ * 1. Find all subtitles that need to be burned into the current video frame
589+ * and attach them to the frame.
590+ * 2. Find all subtitles that need to be passed thru and do so immediately.
591+ */
592+ for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
593+ {
594+ subtitle = hb_list_item( job->list_subtitle, i );
595+
596+ // If this subtitle track's packets are to be passed thru, do so immediately
597+ if( subtitle->config.dest == PASSTHRUSUB )
598+ {
599+ while ( ( sub = hb_fifo_get( subtitle->fifo_raw ) ) != NULL )
600+ {
601+ if ( subtitle->source == VOBSUB )
602+ {
603+ hb_fifo_push( subtitle->fifo_sync, sub );
604+ }
605+ else
606+ {
607+ hb_fifo_push( subtitle->fifo_out, sub );
608+ }
609+ }
610+ }
611+ // If this subtitle track's packets are to be rendered, identify the
612+ // packets that need to be rendered on the current video frame
613+ else if( subtitle->config.dest == RENDERSUB )
614+ {
615+ // Migrate subtitles from 'subtitle->fifo_raw' to 'sub_list' immediately.
616+ // Note that the size of 'sub_list' is unbounded.
617+ while ( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) != NULL )
618+ {
619+ sub = hb_fifo_get( subtitle->fifo_raw ); // pop
620+
621+ #if SUBSYNC_VERBOSE_TIMING
622+ printf( "\nSUB*** (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lead by %"PRId64"ms)\n",
623+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
624+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
625+ (sub->start - cur->start)/90);
626+ if (pv->common->video_pts_slip)
627+ {
628+ printf( " VIDEO-LAG: %"PRId64"\n", pv->common->video_pts_slip );
629+ }
630+ #endif
631+
632+ // Prepend to sub_list
633+ hb_buffer_t *sub_list_next = sub_list;
634+ sub_list = sub;
635+ sub_list->next = sub_list_next;
636+ }
637+
638+ hb_buffer_t *last_sub = NULL;
639+ for ( sub = sub_list; sub != NULL; )
640+ {
641+ // NOTE: Strictly speaking this sequence check is probably unnecessary.
642+ // It is a holdover behavior inherited from the classic subsync algorithm.
643+ if ( sub->sequence > cur->sequence )
644+ {
645+ // Subtitle sequence in the future
646+
647+ // (Keep the subtitle in the stream)
648+ last_sub = sub;
649+ sub = sub->next;
650+ continue;
651+ }
652+
653+ if ( cur->start < sub->start )
654+ {
655+ // Subtitle starts in the future
656+
657+ // (Keep the subtitle in the stream)
658+ last_sub = sub;
659+ sub = sub->next;
660+ continue;
661+ }
662+ else
663+ {
664+ // Subtitle starts in the past...
665+
666+ if ( cur->start < sub->stop )
667+ {
668+ // Subtitle starts in the past and finishes in the future
669+
670+ // Attach a copy of the subtitle packet to the current video packet
671+ // to be burned in by the 'render' work-object.
672+ // (Can't just alias it because we don't know when the 'render'
673+ // work-object will dispose of it.)
674+ hb_buffer_t * old_sublist_head = cur->sub;
675+ cur->sub = copy_subtitle( sub );
676+ cur->sub->next = old_sublist_head;
677+
678+ #if SUBSYNC_VERBOSE_TIMING
679+ if (!(sub->new_chap & 0x01))
680+ {
681+ printf( "\nSUB+++ (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lag by %"PRId64"ms)\n",
682+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
683+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
684+ (cur->start - sub->start)/90 );
685+ if (pv->common->video_pts_slip)
686+ {
687+ printf( " VIDEO-LAG: %"PRId64"\n", pv->common->video_pts_slip );
688+ }
689+
690+ sub->new_chap |= 0x01;
691+ }
692+ #endif
693+
694+ // (Keep the subtitle in the stream)
695+ last_sub = sub;
696+ sub = sub->next;
697+ continue;
698+ }
699+ else
700+ {
701+ // Subtitle starts in the past and has already finished
702+
703+ #if SUBSYNC_VERBOSE_TIMING
704+ printf( "\nSUB--- (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lag by %"PRId64"ms)\n",
705+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
706+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
707+ (cur->start - sub->stop)/90 );
708+ if (pv->common->video_pts_slip)
709+ {
710+ printf( " VIDEO-LAG: %"PRId64"\n", pv->common->video_pts_slip );
711+ }
712+ #endif
713+
714+ // Remove it from the stream...
715+ if (last_sub != NULL)
716+ {
717+ last_sub->next = sub->next;
718+ }
719+ if (sub_list == sub)
720+ {
721+ sub_list = sub->next;
722+ }
723+
724+ // ...and trash it
725+ hb_buffer_t *next_sub = sub->next;
726+ // XXX: Prevent hb_buffer_close from killing the whole list
727+ // before we finish iterating over it
728+ sub->next = NULL;
729+
730+ hb_buffer_t * subpicture_list = sub;
731+ hb_buffer_t * subpicture;
732+ hb_buffer_t * subpicture_next;
733+ for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
734+ {
735+ subpicture_next = subpicture->next_subpicture;
736+
737+ hb_buffer_close( &subpicture );
738+ }
739+
740+ // (last_sub remains the same)
741+ sub = next_sub;
742+ continue;
743+ }
744+ }
745+ }
746+ }
747+ } // end subtitles
748+ #undef sub_list
556749
750+#elif SUBSYNC_ALGORITHM_CLASSIC
751+ // NOTE: This algorithm does not correctly support the simultaneous display of temporally overlapping subtitles.
752+
557753 /*
558754 * Look for a subtitle for this frame.
559755 *
@@ -651,13 +847,19 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
651847 duration = sub->stop - sub->start;
652848 sub_stop = sub_start + duration;
653849
654- /* If two subtitles overlap, make the first one stop
850+ /* If two DVD subtitles overlap, make the first one stop
655851 when the second one starts */
656- sub2 = hb_fifo_see2( subtitle->fifo_raw );
657- if( sub2 && sub->stop > sub2->start )
852+ // TODO: Consider removing this entirely. Currently retained
853+ // to preserve old DVD subtitle behavior.
854+ if ( subtitle->source == VOBSUB )
658855 {
659- sub->stop = sub2->start;
856+ sub2 = hb_fifo_see2( subtitle->fifo_raw );
857+ if( sub2 && sub->stop > sub2->start )
858+ {
859+ sub->stop = sub2->start;
860+ }
660861 }
862+
661863
662864 // hb_log("0x%x: video seq: %"PRId64" subtitle sequence: %"PRId64,
663865 // sub, cur->sequence, sub->sequence);
@@ -674,8 +876,22 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
674876 break;
675877 }
676878
879+ #if SUBSYNC_VERBOSE_TIMING
880+ if (!(sub->new_chap & 0x02))
881+ {
882+ printf( "\nSUB*** (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lead by %"PRId64"ms)\n",
883+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
884+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
885+ (sub->start - cur->start)/90);
886+
887+ sub->new_chap |= 0x02;
888+ }
889+ #endif
890+
677891 if( sub_stop > start )
678892 {
893+ // CONDITION: cur->start < sub->stop
894+
679895 /*
680896 * The stop time is in the future, so fall through
681897 * and we'll deal with it in the next block of
@@ -687,6 +903,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
687903 */
688904 if( sub_stop > sub_start)
689905 {
906+ // CONDITION: {cur->start, sub->start} < sub->stop
907+
690908 /*
691909 * Normal subtitle which ends after it starts,
692910 * check to see that the current video is between
@@ -695,6 +913,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
695913 if( start > sub_start &&
696914 start < sub_stop )
697915 {
916+ // CONDITION: sub->start < cur->start < sub->stop
917+
698918 /*
699919 * We should be playing this, so leave the
700920 * subtitle in place.
@@ -704,6 +924,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
704924 }
705925 else
706926 {
927+ // CONDITION: cur->start < sub->start < sub->stop
928+
707929 /*
708930 * Defer until the play point is within
709931 * the subtitle
@@ -713,12 +935,16 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
713935 }
714936 else
715937 {
938+ // CONDITION: cur->start < sub->stop < sub->start
939+
716940 /*
717941 * The end of the subtitle is less than the start,
718942 * this is a sign of a PTS discontinuity.
719943 */
720944 if( sub_start > start )
721945 {
946+ // CONDITION: cur->start < sub->stop < sub->start
947+
722948 /*
723949 * we haven't reached the start time yet, or
724950 * we have jumped backwards after having
@@ -726,6 +952,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
726952 */
727953 if( start < sub_stop )
728954 {
955+ // CONDITION: cur->start < sub->stop < sub->start
956+
729957 /*
730958 * We have jumped backwards and so should
731959 * continue displaying this subtitle.
@@ -735,6 +963,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
735963 }
736964 else
737965 {
966+ // CONDITION: Mathematically impossible to get here
967+
738968 /*
739969 * Defer until the play point is
740970 * within the subtitle
@@ -742,6 +972,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
742972 sub = NULL;
743973 }
744974 } else {
975+ // CONDITION: Mathematically impossible to get here
976+
745977 /*
746978 * Play this subtitle as the start is
747979 * greater than our video point.
@@ -754,6 +986,14 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
754986 }
755987 else
756988 {
989+ // CONDITION: sub->stop < cur->start
990+
991+ #if SUBSYNC_VERBOSE_TIMING
992+ printf( "\nSUB--- (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lag by %"PRId64"ms)\n",
993+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
994+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
995+ (cur->start - sub->stop)/90 );
996+ #endif
757997
758998 /*
759999 * The subtitle is older than this picture, trash it
@@ -766,24 +1006,39 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
7661006 /* If we have a subtitle for this picture, copy it */
7671007 if( sub )
7681008 {
1009+ #if SUBSYNC_VERBOSE_TIMING
1010+ if (!(sub->new_chap & 0x01))
1011+ {
1012+ printf( "\nSUB+++ (%"PRId64"/%"PRId64":%"PRId64") @ %"PRId64"/%"PRId64":%"PRId64" (lag by %"PRId64"ms)\n",
1013+ sub->start/90, sub->start/90/1000/60, sub->start/90/1000%60,
1014+ cur->start/90, cur->start/90/1000/60, cur->start/90/1000%60,
1015+ (cur->start - sub->start)/90 );
1016+
1017+ sub->new_chap |= 0x01;
1018+ }
1019+ #endif
1020+
7691021 if( sub->size > 0 )
7701022 {
7711023 if( subtitle->config.dest == RENDERSUB )
7721024 {
773- // Only allow one subtitle to be showing at once; ignore others
774- if ( cur->sub == NULL )
775- {
776- /*
777- * Tack onto the video buffer for rendering
778- */
779- /* FIXME: we should avoid this memcpy */
780- cur->sub = copy_subtitle( sub );
781- cur->sub->start = sub_start;
782- cur->sub->stop = sub_stop;
1025+ /*
1026+ * Tack onto the video buffer for rendering.
1027+ *
1028+ * Note that there may be multiple subtitles
1029+ * whose time intervals overlap which must display
1030+ * on the same frame.
1031+ */
1032+ hb_buffer_t * old_sublist_head = cur->sub;
1033+
1034+ /* FIXME: we should avoid this memcpy */
1035+ cur->sub = copy_subtitle( sub );
1036+ cur->sub->next = old_sublist_head;
1037+ cur->sub->start = sub_start;
1038+ cur->sub->stop = sub_stop;
7831039
784- // Leave the subtitle on the raw queue
785- // (until it no longer needs to be displayed)
786- }
1040+ // Leave the subtitle on the raw queue
1041+ // (until it no longer needs to be displayed)
7871042 } else {
7881043 /*
7891044 * Pass-Through, pop it off of the raw queue,
@@ -811,6 +1066,9 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
8111066 }
8121067 }
8131068 } // end subtitles
1069+#else
1070+ #error "Must select a subtitle sync algorithm."
1071+#endif
8141072
8151073 /*
8161074 * Adjust the pts of the current frame so that it's contiguous