Allow a fudge factor when checking for gaps/overlaps between segments

Sometimes in the wild (particularly on youtube) segments may not be timed perfectly, so allow up to 10ms of gap or overlap
to be counted as "equal" for purposes of finding the best segment.
mike/chat-subs
Mike Lang 1 year ago committed by Mike Lang
parent b8cf0f20b0
commit 2a1f7207a8

@ -151,6 +151,11 @@ def get_best_segments(hours_path, start, end, allow_holes=True):
# python's datetime types represent these as integer microseconds internally. So the parsing # python's datetime types represent these as integer microseconds internally. So the parsing
# to these types is exact, and all operations on them are exact, so all operations are exact. # to these types is exact, and all operations on them are exact, so all operations are exact.
# ...however in the wild we sometimes see timestamps or durations that differ by a few ms.
# So we allow some fudge factors.
ALLOWABLE_OVERLAP = 0.01 # 10ms
ALLOWABLE_GAP = 0.01 # 10ms
result = [] result = []
for hour in hour_paths_for_range(hours_path, start, end): for hour in hour_paths_for_range(hours_path, start, end):
@ -182,13 +187,14 @@ def get_best_segments(hours_path, start, end, allow_holes=True):
else: else:
# normal case: check against previous segment end time # normal case: check against previous segment end time
prev_end = result[-1].end prev_end = result[-1].end
if segment.start < prev_end: gap = (segment.start - prev_end).total_seconds()
if gap < -ALLOWABLE_OVERLAP:
# Overlap! This shouldn't happen, though it might be possible due to weirdness # Overlap! This shouldn't happen, though it might be possible due to weirdness
# if the stream drops then starts again quickly. We simply ignore the overlapping # if the stream drops then starts again quickly. We simply ignore the overlapping
# segment and let the algorithm continue. # segment and let the algorithm continue.
logging.info("Overlapping segments: {} overlaps end of {}".format(segment, result[-1])) logging.info("Overlapping segments: {} overlaps end of {}".format(segment, result[-1]))
continue continue
if result[-1].is_partial or prev_end < segment.start: if result[-1].is_partial or gap > ALLOWABLE_GAP:
# there's a gap between prev end and this start, so add a None # there's a gap between prev end and this start, so add a None
if not allow_holes: if not allow_holes:
raise ContainsHoles raise ContainsHoles

@ -254,6 +254,9 @@ class CoverageChecker(object):
"""Loop over available hours for each quality, checking segment coverage.""" """Loop over available hours for each quality, checking segment coverage."""
self.logger.info('Starting') self.logger.info('Starting')
ALLOWABLE_OVERLAP = 0.01 # 10ms
ALLOWABLE_GAP = 0.01 # 10ms
while not self.stopping.is_set(): while not self.stopping.is_set():
for quality in self.qualities: for quality in self.qualities:
@ -374,7 +377,8 @@ class CoverageChecker(object):
previous_editable = best_segment previous_editable = best_segment
else: else:
previous_end = previous.start + previous.duration previous_end = previous.start + previous.duration
if segment.start < previous_end: gap = (segment.start - previous_end).total_seconds()
if gap < -ALLOWABLE_OVERLAP:
if segment.type == 'full': if segment.type == 'full':
full_overlaps += 1 full_overlaps += 1
full_overlap_duration += previous_end - segment.start full_overlap_duration += previous_end - segment.start
@ -389,11 +393,11 @@ class CoverageChecker(object):
coverage += segment.duration coverage += segment.duration
editable_coverage += segment.duration editable_coverage += segment.duration
if segment.start > previous_end: if gap > ALLOWABLE_GAP:
holes.append((previous_end, segment.start)) holes.append((previous_end, segment.start))
previous_editable_end = previous_editable.start + previous_editable.duration previous_editable_end = previous_editable.start + previous_editable.duration
if segment.start > previous_editable_end: if (segment.start - previous_editable_end).total_seconds() > ALLOWABLE_GAP:
editable_holes.append((previous_editable_end, segment.start)) editable_holes.append((previous_editable_end, segment.start))
previous_editable = best_segment previous_editable = best_segment

Loading…
Cancel
Save