Add a small time range around the timestamp when extracting a frame

This should hopefully result in frames on the edge of timestamps being extracted
from a combination of the neighboring segment and the naive one,
so that we don't get errors extracting a frame.
pull/367/head
Mike Lang 1 year ago
parent 7a27938244
commit e689626815

@ -101,6 +101,13 @@ class ContainsHoles(Exception):
"""Raised by get_best_segments() when a hole is found and allow_holes is False""" """Raised by get_best_segments() when a hole is found and allow_holes is False"""
def get_best_segments_for_frame(hour_path, timestamp):
# Add some leeway before and after so that we don't have errors related to
# landing on a segment edge.
leeway = datetime.timedelta(seconds=1)
return get_best_segments(hour_path, timestamp - leeway, timestamp + leeway)
@timed( @timed(
hours_path=lambda ret, hours_path, *args, **kwargs: hours_path, hours_path=lambda ret, hours_path, *args, **kwargs: hours_path,
has_holes=lambda ret, *args, **kwargs: None in ret, has_holes=lambda ret, *args, **kwargs: None in ret,
@ -765,19 +772,11 @@ def extract_frame(segments, timestamp):
# Remove holes # Remove holes
segments = [segment for segment in segments if segment is not None] segments = [segment for segment in segments if segment is not None]
# Find segment containing timestamp
segments = [
segment for segment in segments
if segment.start <= timestamp < segment.end
]
if not segments: if not segments:
raise ValueError("No data at timestamp within segment list") raise ValueError("No data at timestamp within segment list")
if len(segments) != 1:
raise ValueError("Segment list contains overlap at timestamp")
(segment,) = segments
# "cut" input so that first frame is our target frame # "cut" input so that first frame is our target frame
cut_start = (timestamp - segment.start).total_seconds() cut_start = (timestamp - segments[0].start).total_seconds()
ffmpeg = None ffmpeg = None
input_feeder = None input_feeder = None

@ -17,7 +17,7 @@ from psycopg2 import sql
import common import common
from common.database import DBManager, query, get_column_placeholder from common.database import DBManager, query, get_column_placeholder
from common.segments import get_best_segments, archive_cut_segments, fast_cut_segments, full_cut_segments, smart_cut_segments, extract_frame, ContainsHoles from common.segments import get_best_segments, archive_cut_segments, fast_cut_segments, full_cut_segments, smart_cut_segments, extract_frame, ContainsHoles, get_best_segments_for_frame
from common.images import compose_thumbnail_template from common.images import compose_thumbnail_template
from common.stats import timed from common.stats import timed
@ -275,7 +275,7 @@ class Cutter(object):
# Also check the thumbnail time if we need to generate it # Also check the thumbnail time if we need to generate it
thumbnail_segments = None thumbnail_segments = None
if candidate.thumbnail_mode in ('BARE', 'TEMPLATE') and candidate.thumbnail_image is None: if candidate.thumbnail_mode in ('BARE', 'TEMPLATE') and candidate.thumbnail_image is None:
thumbnail_segments = get_best_segments(hours_path, candidate.thumbnail_time, candidate.thumbnail_time) thumbnail_segments = get_best_segments_for_frame(hours_path, candidate.thumbnail_time, candidate.thumbnail_time)
if thumbnail_segments == [None]: if thumbnail_segments == [None]:
raise ContainsHoles raise ContainsHoles
return segment_ranges, thumbnail_segments return segment_ranges, thumbnail_segments
@ -762,7 +762,7 @@ class VideoUpdater(object):
if thumbnail_image is None: if thumbnail_image is None:
self.logger.info("Regenerating thumbnail for {}".format(job.id)) self.logger.info("Regenerating thumbnail for {}".format(job.id))
hours_path = os.path.join(self.segments_path, job.video_channel, job.video_quality) hours_path = os.path.join(self.segments_path, job.video_channel, job.video_quality)
segments = get_best_segments(hours_path, job.thumbnail_time, job.thumbnail_time) segments = get_best_segments_for_frame(hours_path, job.thumbnail_time)
frame = extract_frame(segments, job.thumbnail_time) frame = extract_frame(segments, job.thumbnail_time)
frame = b''.join(frame) frame = b''.join(frame)
if job.thumbnail_mode == 'BARE': if job.thumbnail_mode == 'BARE':

@ -17,7 +17,7 @@ from gevent.pywsgi import WSGIServer
from common import dateutil, get_best_segments, rough_cut_segments, fast_cut_segments, full_cut_segments, PromLogCountsHandler, install_stacksampler, serve_with_graceful_shutdown from common import dateutil, get_best_segments, rough_cut_segments, fast_cut_segments, full_cut_segments, PromLogCountsHandler, install_stacksampler, serve_with_graceful_shutdown
from common.flask_stats import request_stats, after_request from common.flask_stats import request_stats, after_request
from common.images import compose_thumbnail_template from common.images import compose_thumbnail_template
from common.segments import smart_cut_segments, feed_input, render_segments_waveform, extract_frame, list_segment_files from common.segments import smart_cut_segments, feed_input, render_segments_waveform, extract_frame, list_segment_files, get_best_segments_for_frame
from common.chat import get_batch_file_range, merge_messages from common.chat import get_batch_file_range, merge_messages
from . import generate_hls from . import generate_hls
@ -423,7 +423,7 @@ def get_frame(channel, quality):
if not os.path.isdir(hours_path): if not os.path.isdir(hours_path):
abort(404) abort(404)
segments = get_best_segments(hours_path, timestamp, timestamp) segments = get_best_segments_for_frame(hours_path, timestamp)
if not any(segment is not None for segment in segments): if not any(segment is not None for segment in segments):
return "We have no content available within the requested time range.", 406 return "We have no content available within the requested time range.", 406
@ -454,7 +454,7 @@ def get_thumbnail(channel, quality):
if not os.path.isdir(hours_path): if not os.path.isdir(hours_path):
abort(404) abort(404)
segments = get_best_segments(hours_path, timestamp, timestamp) segments = get_best_segments_for_frame(hours_path, timestamp)
if not any(segment is not None for segment in segments): if not any(segment is not None for segment in segments):
return "We have no content available within the requested time range.", 406 return "We have no content available within the requested time range.", 406

Loading…
Cancel
Save