Compare commits

...

3 Commits

Author SHA1 Message Date
Mike Lang e50adbf2da Fix a bug where transitions past the first are not timed correctly
The video offset is timed relative to the full video up until that point, not the previous range.
1 month ago
Mike Lang 69bfa79e24 Make video transition durations floats, not intervals
Almost all code was already expecting this.
1 month ago
Mike Lang 649a0fb2dd cutter: Improve error message for unhandled cutting exceptions
In particular, use exception chaining instead of printing the old error.
1 month ago

@ -180,7 +180,7 @@ columns | type | role
`upload_location` | `TEXT` | edit input | The upload location to upload the cut video to. This is used by the cutter, and must match one of the cutter's configured upload locations. If it does not, the cutter will not claim the event.
`public` | `BOOLEAN NOT NULL DEFAULT TRUE` | edit input | Whether the uploaded video should be public or not, if the upload location supports that distinction. For example, on youtube, non-public videos are "unlisted". It also controls whether the video will be added to playlists, only public videos are added to playlists.
`video_ranges` | `{start TIMESTAMP, end TIMESTAMP}[]` | edit input | A non-zero number of start and end times, describing the ranges of video to cut. They will be cut back-to-back in the given order, with the transitions between as per `video_transitions`. If already set, used as the default range settings when editing.
`video_transitions` | `{type TEXT, duration INTERVAL}[]` | edit input | Defines how to transition between each range defined in `video_ranges`, and must be exactly the length of `video_ranges` minus 1. Each index in `video_transitions` defines the transition between the range with the same index in `video_ranges` and the next one. Transitions either specify a transition type as understood by `ffmpeg`'s `xfade` filter and a duration (amount of overlap), or can be NULL to indicate a hard cut.
`video_transitions` | `{type TEXT, duration DOUBLE PRECISION}[]`| edit input | Defines how to transition between each range defined in `video_ranges`, and must be exactly the length of `video_ranges` minus 1. Each index in `video_transitions` defines the transition between the range with the same index in `video_ranges` and the next one. Transitions either specify a transition type as understood by `ffmpeg`'s `xfade` filter and a duration (amount of overlap), or can be NULL to indicate a hard cut.
`video_title` | `TEXT` | edit input | The title of the video. If already set, used as the default title when editing instead of `description`.
`video_description` | `TEXT` | edit input | The description field of the video. If already set, used as the default description when editing instead of `description`.
`video_tags` | `TEXT[]` | edit input | Custom tags to annotate the video with. If already set, used as the default when editing instead of `tags`.

@ -926,6 +926,9 @@ def full_cut_segments(segment_ranges, ranges, transitions, encode_args, stream=F
inputs.append((segments, args))
filters = []
# We need to keep track of the full video length so transitions start at the correct time.
# This variable tracks the start of prev_range relative to the start of the output video.
prev_video_start_offset = 0
# with no additional ranges, the output stream is just the first input stream
output_video_stream = "0:v"
output_audio_stream = "0:a"
@ -947,6 +950,7 @@ def full_cut_segments(segment_ranges, ranges, transitions, encode_args, stream=F
kwargs = ":".join(f"{k}={v}" for k, v in kwargs.items())
filters.append(f"{inputs}{name}={kwargs}{outputs}")
prev_length = (prev_range[1] - prev_range[0]).total_seconds()
if transition is None:
input_streams = [
prev_video_stream,
@ -956,14 +960,16 @@ def full_cut_segments(segment_ranges, ranges, transitions, encode_args, stream=F
]
output_streams = [output_video_stream, output_audio_stream]
add_filter("concat", input_streams, output_streams, n=2, v=1, a=1)
prev_video_start_offset += prev_length
else:
video_type, duration = transition
# transition should start at DURATION seconds before prev_range ends,
# which is timed relative to prev_range start. So if prev_range is 60s long
# Transition should start at DURATION seconds before prev_range ends.
# We know when prev_range begins from prev_video_start_offset.
# So if prev_range is 40s long, prev_range starts at 20s,
# and duration is 2s, we should start at 58s.
prev_length = (prev_range[1] - prev_range[0]).total_seconds()
offset = prev_length - duration
# This is also the start time of the next range.
offset = prev_video_start_offset + prev_length - duration
kwargs = {
"duration": duration,
"offset": offset,
@ -980,6 +986,8 @@ def full_cut_segments(segment_ranges, ranges, transitions, encode_args, stream=F
# audio cross-fade across the same period
add_filter("acrossfade", [prev_audio_stream, next_audio_stream], [output_audio_stream], duration=duration)
prev_video_start_offset = offset
if stream:
# When streaming, we can just use a pipe
output_file = subprocess.PIPE

@ -89,15 +89,15 @@ def get_duration(job):
# Each range overlaps the previous by duration, so we add all the ranges
# then subtract all the durations.
without_transitions = sum([
range.end - range.start
(range.end - range.start).total_seconds()
for range in job.video_ranges
], datetime.timedelta())
])
overlap = sum([
transition.duration
for transition in job.video_transitions
if transition is not None
], datetime.timedelta())
return (without_transitions - overlap).total_seconds()
])
return without_transitions - overlap
def format_job(job):
@ -428,8 +428,8 @@ class Cutter(object):
yield chunk
except Exception as ex:
self.logger.exception("Error occurred while trying to cut job {}".format(format_job(job)))
# Assumed error is not retryable
raise UploadError("Error while cutting: {}".format(ex), retryable=False)
# Assumed error is not retryable. Exception chaining preserves original error message.
raise UploadError("Unhandled exception while cutting", retryable=False) from ex
# The data is now fully uploaded, but the request is not finalized.
# We now set the DB state to finalized so we know about failures during this

@ -16,7 +16,7 @@ CREATE TYPE video_range as (
CREATE TYPE video_transition as (
type TEXT,
duration INTERVAL
duration DOUBLE PRECISON
);
CREATE TYPE thumbnail_mode as ENUM (

Loading…
Cancel
Save