summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2013-07-01 20:29:33 +0000
committerjstebbins <[email protected]>2013-07-01 20:29:33 +0000
commita579c35bea16bd79838aa6f71b4a655b9459e244 (patch)
tree82200903182458e82eb43a4a600f527a95283592
parentecc7a189f081ddc05761dc74a0fd6297dcc02fd2 (diff)
libhb: improve handling of overlapping text subtitles
mp4 timed text does not support subtitles that overlap (in time). So this patch detects these overlaps and merges subtitles when appropriate. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@5632 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--libhb/sync.c230
1 files changed, 177 insertions, 53 deletions
diff --git a/libhb/sync.c b/libhb/sync.c
index e3d44694b..581276684 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -55,6 +55,14 @@ typedef struct
typedef struct
{
+ int link;
+ int merge;
+ hb_buffer_t * list_current;
+ hb_buffer_t * last;
+} subtitle_sanitizer_t;
+
+typedef struct
+{
/* Video */
int first_frame;
int64_t pts_skip;
@@ -68,6 +76,8 @@ typedef struct
int chap_mark; /* to propagate chapter mark across a drop */
hb_buffer_t * cur; /* The next picture to process */
+ subtitle_sanitizer_t *subtitle_sanitizer;
+
/* Statistics */
uint64_t st_counts[4];
uint64_t st_dates[4];
@@ -91,6 +101,7 @@ struct hb_work_private_s
static void getPtsOffset( hb_work_object_t * w );
static int checkPtsOffset( hb_work_object_t * w );
static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i );
+static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int i );
static void InsertSilence( hb_work_object_t * w, int64_t d );
static void UpdateState( hb_work_object_t * w );
static void UpdateSearchState( hb_work_object_t * w, int64_t start );
@@ -188,9 +199,34 @@ hb_work_object_t * hb_sync_init( hb_job_t * job )
for ( i = 0; i < pv->common->pts_count; i++ )
pv->common->first_pts[i] = INT64_MAX;
+ int count = hb_list_count(job->list_subtitle);
+ sync->subtitle_sanitizer = calloc(count, sizeof(subtitle_sanitizer_t));
+ for( i = 0; i < count; i++ )
+ {
+ InitSubtitle(job, sync, i);
+ }
return ret;
}
+static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int i )
+{
+ hb_subtitle_t * subtitle;
+
+ subtitle = hb_list_item( job->list_subtitle, i );
+ if (subtitle->format == TEXTSUB &&
+ subtitle->config.dest == PASSTHRUSUB &&
+ (job->mux & HB_MUX_MASK_MP4))
+ {
+ // Merge overlapping subtitles since mpv tx3g does not support them
+ sync->subtitle_sanitizer[i].merge = 1;
+ }
+ if (subtitle->config.dest == PASSTHRUSUB)
+ {
+ // Fill in stop time when it is missing
+ sync->subtitle_sanitizer[i].link = 1;
+ }
+}
+
/***********************************************************************
* Close Video
***********************************************************************
@@ -248,6 +284,137 @@ void syncVideoClose( hb_work_object_t * w )
w->private_data = NULL;
}
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+
+static hb_buffer_t * mergeSubtitles(subtitle_sanitizer_t *sanitizer)
+{
+ hb_buffer_t *a, *b, *buf, *out = NULL, *last = NULL;
+
+ do
+ {
+ a = sanitizer->list_current;
+
+ buf = NULL;
+ if (a != NULL && a->s.stop != -1)
+ {
+ b = a->next;
+
+ if (!sanitizer->merge)
+ {
+ sanitizer->list_current = a->next;
+ if (sanitizer->list_current == NULL)
+ sanitizer->last = NULL;
+ a->next = NULL;
+ buf = a;
+ }
+ else if (b != NULL && a->s.stop > b->s.start)
+ {
+ // Overlap
+ if (ABS(a->s.start - b->s.start) <= 18000)
+ {
+ // subtitles start within 1/5 second of eachother, merge
+ sanitizer->list_current = a->next;
+ if (sanitizer->list_current == NULL)
+ sanitizer->last = NULL;
+ a->next = NULL;
+ b->s.start = a->s.stop;
+
+ buf = hb_buffer_init(a->size + b->size);
+ buf->s = a->s;
+ sprintf((char*)buf->data, "%s\n%s", a->data, b->data);
+ hb_buffer_close(&a);
+
+ if (b->s.stop != -1 && ABS(b->s.stop - b->s.start) <= 18000)
+ {
+ // b and a completely overlap, remove b
+ sanitizer->list_current = b->next;
+ if (sanitizer->list_current == NULL)
+ sanitizer->last = NULL;
+ hb_buffer_close(&b);
+ }
+ }
+ else
+ {
+ // a starts before b, output copy of a and
+ buf = hb_buffer_dup(a);
+ buf->s.stop = b->s.start;
+ a->s.start = b->s.start;
+ }
+ }
+ else if (b != NULL && a->s.stop <= b->s.start)
+ {
+ sanitizer->list_current = a->next;
+ if (sanitizer->list_current == NULL)
+ sanitizer->last = NULL;
+ a->next = NULL;
+ buf = a;
+ }
+ }
+
+ if (buf != NULL)
+ {
+ buf->s.duration = buf->s.stop - buf->s.start;
+ if (last == NULL)
+ {
+ out = last = buf;
+ }
+ else
+ {
+ last->next = buf;
+ last = buf;
+ }
+ }
+ } while (buf != NULL);
+
+ return out;
+}
+
+static hb_buffer_t * sanitizeSubtitle(
+ hb_work_private_t * pv,
+ int i,
+ hb_buffer_t * sub)
+{
+ hb_sync_video_t * sync;
+ subtitle_sanitizer_t * sanitizer;
+
+ sync = &pv->type.video;
+ sanitizer = &sync->subtitle_sanitizer[i];
+
+ if (!sanitizer->link && !sanitizer->merge)
+ {
+ return sub;
+ }
+
+ if (sub == NULL)
+ {
+ return mergeSubtitles(sanitizer);
+ }
+
+ hb_lock( pv->common->mutex );
+ sub->s.start -= pv->common->video_pts_slip;
+ if (sub->s.stop != -1)
+ sub->s.stop -= pv->common->video_pts_slip;
+ if (sub->s.renderOffset != -1)
+ sub->s.renderOffset -= pv->common->video_pts_slip;
+ hb_unlock( pv->common->mutex );
+
+ if (sanitizer->last != NULL && sanitizer->last->s.stop == -1)
+ {
+ sanitizer->last->s.stop = sub->s.start;
+ }
+
+ if (sanitizer->last == NULL)
+ {
+ sanitizer->list_current = sanitizer->last = sub;
+ }
+ else
+ {
+ sanitizer->last->next = sub;
+ sanitizer->last = sub;
+ }
+ return mergeSubtitles(sanitizer);
+}
+
/***********************************************************************
* syncVideoWork
***********************************************************************
@@ -559,66 +726,23 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
{
- int64_t sub_start, sub_stop, sub_dts, duration;
+ hb_buffer_t *out;
subtitle = hb_list_item( job->list_subtitle, i );
// Sanitize subtitle start and stop times, then pass to
// muxer or renderer filter.
- while ( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) != NULL )
+ while ( ( sub = hb_fifo_get( subtitle->fifo_raw ) ) != NULL )
{
- hb_lock( pv->common->mutex );
- sub_start = sub->s.start - pv->common->video_pts_slip;
- if (sub->s.renderOffset != -1)
- sub_dts = sub->s.renderOffset - pv->common->video_pts_slip;
- else
- sub_dts = -1;
- hb_unlock( pv->common->mutex );
-
- if (sub->s.stop == -1)
- {
- if (subtitle->config.dest != RENDERSUB &&
- hb_fifo_size( subtitle->fifo_raw ) < 2)
- {
- // For passthru subs, we want to wait for the
- // next subtitle so that we can fill in the stop time.
- // This way the muxer can compute the duration of
- // the subtitle.
- //
- // For render subs, we need to ensure that they
- // get to the renderer before the associated video
- // that they are to be applied to. It is the
- // responsibility of the renderer to handle
- // stop == -1.
- break;
- }
- }
-
- sub = hb_fifo_get( subtitle->fifo_raw );
- if ( sub->s.stop == -1 )
- {
- hb_buffer_t *next;
- next = hb_fifo_see( subtitle->fifo_raw );
- if (next != NULL)
- sub->s.stop = next->s.start;
- }
- // Need to re-write subtitle timestamps to account
- // for any slippage.
- sub_stop = -1;
- duration = -1;
- if ( sub->s.stop != -1 )
- {
- duration = sub->s.stop - sub->s.start;
- sub_stop = sub_start + duration;
- }
-
- sub->s.start = sub_start;
- sub->s.renderOffset = sub_dts;
- sub->s.stop = sub_stop;
- sub->s.duration = duration;
-
- hb_fifo_push( subtitle->fifo_out, sub );
+ out = sanitizeSubtitle(pv, i, sub);
+ if (out != NULL)
+ hb_fifo_push( subtitle->fifo_out, out );
}
+ // fifo empty, flush out any pending subtitle buffers in
+ // the sanitizer
+ out = sanitizeSubtitle(pv, i, NULL);
+ if (out != NULL)
+ hb_fifo_push( subtitle->fifo_out, out );
}
/*