cutter,restreamer: Set stream=True for full cuts when appropriate

And also default to a new ffmpeg encoding setting for high-quality mpegts
(ie. still streamable) that is encoded very quickly.
pull/136/head
Mike Lang 5 years ago
parent 9afcc7b399
commit 4d52b18b04

@ -414,7 +414,7 @@ def feed_input(segments, pipe):
try: try:
shutil.copyfileobj(f, pipe) shutil.copyfileobj(f, pipe)
except OSError as e: except OSError as e:
# ignore EPIPE, as this just means the end cut meant we didn't need all i> # ignore EPIPE, as this just means the end cut meant we didn't need all it
if e.errno != errno.EPIPE: if e.errno != errno.EPIPE:
raise raise
pipe.close() pipe.close()

@ -294,8 +294,14 @@ class Cutter(object):
self.logger.debug("No encoding settings, using fast cut") self.logger.debug("No encoding settings, using fast cut")
cut = fast_cut_segments(job.segments, job.video_start, job.video_end) cut = fast_cut_segments(job.segments, job.video_start, job.video_end)
else: else:
self.logger.debug("Using encoding settings for cut: {}".format(upload_backend.encoding_settings)) self.logger.debug("Using encoding settings for {} cut: {}".format(
cut = full_cut_segments(job.segments, job.video_start, job.video_end, upload_backend.encoding_settings) "streamable" if upload_backend.encoding_streamable else "non-streamable",
upload_backend.encoding_settings,
))
cut = full_cut_segments(
job.segments, job.video_start, job.video_end,
upload_backend.encoding_settings, stream=upload_backend.encoding_streamable,
)
# This flag tracks whether we've told requests to finalize the upload, # This flag tracks whether we've told requests to finalize the upload,
# and serves to detect whether errors from the request call are recoverable. # and serves to detect whether errors from the request call are recoverable.

@ -67,12 +67,17 @@ class UploadBackend(object):
under the 'encoding_settings' attribute. under the 'encoding_settings' attribute.
If this is None, instead uses the 'fast cut' strategy where nothing If this is None, instead uses the 'fast cut' strategy where nothing
is transcoded. is transcoded.
In addition, if the output format doesn't need a seekable file,
you should set encoding_streamable = True so we know we can stream the output directly.
""" """
needs_transcode = False needs_transcode = False
# reasonable default if settings don't otherwise matter # reasonable default if settings don't otherwise matter:
encoding_settings = ['-f', 'mp4'] # high-quality mpegts, without wasting too much cpu on encoding
encoding_args = ['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '0', '-f', 'mpegts']
encoding_streamable = True
def upload_video(self, title, description, tags, data): def upload_video(self, title, description, tags, data):
raise NotImplementedError raise NotImplementedError
@ -92,10 +97,14 @@ class Youtube(UploadBackend):
language: language:
The language code to describe all videos as. The language code to describe all videos as.
Default is "en", ie. English. Set to null to not set. Default is "en", ie. English. Set to null to not set.
use_yt_recommended_encoding:
Default False. If True, use the ffmpeg settings that Youtube recommends for
fast processing once uploaded. We suggest not bothering, as it doesn't appear
to make much difference.
""" """
needs_transcode = True needs_transcode = True
encoding_settings = [ recommended_settings = [
# Youtube's recommended settings: # Youtube's recommended settings:
'-codec:v', 'libx264', # Make the video codec x264 '-codec:v', 'libx264', # Make the video codec x264
'-crf', '21', # Set the video quality, this produces the bitrate range that YT likes '-crf', '21', # Set the video quality, this produces the bitrate range that YT likes
@ -108,7 +117,7 @@ class Youtube(UploadBackend):
'-movflags', 'faststart', # put MOOV atom at the front of the file, as requested '-movflags', 'faststart', # put MOOV atom at the front of the file, as requested
] ]
def __init__(self, credentials, hidden=False, category_id=23, language="en"): def __init__(self, credentials, hidden=False, category_id=23, language="en", use_yt_recommended_encoding=False):
self.logger = logging.getLogger(type(self).__name__) self.logger = logging.getLogger(type(self).__name__)
self.client = GoogleAPIClient( self.client = GoogleAPIClient(
credentials['client_id'], credentials['client_id'],
@ -118,6 +127,9 @@ class Youtube(UploadBackend):
self.hidden = hidden self.hidden = hidden
self.category_id = category_id self.category_id = category_id
self.language = language self.language = language
if use_yt_recommended_encoding:
self.encoding_settings = self.recommended_settings
self.encoding_streamable = False
def upload_video(self, title, description, tags, data): def upload_video(self, title, description, tags, data):
json = { json = {

@ -237,7 +237,6 @@ def cut(channel, quality):
Even if holes are allowed, a 406 may result if the resulting video would be empty. Even if holes are allowed, a 406 may result if the resulting video would be empty.
type: One of "fast" or "full". Default to "fast". type: One of "fast" or "full". Default to "fast".
A fast cut is much faster but minor artifacting may be present near the start and end. A fast cut is much faster but minor artifacting may be present near the start and end.
A fast cut is encoded as MPEG-TS, a full as mp4.
""" """
start = dateutil.parse_utc_only(request.args['start']) if 'start' in request.args else None start = dateutil.parse_utc_only(request.args['start']) if 'start' in request.args else None
end = dateutil.parse_utc_only(request.args['end']) if 'end' in request.args else None end = dateutil.parse_utc_only(request.args['end']) if 'end' in request.args else None
@ -272,8 +271,9 @@ def cut(channel, quality):
if type == 'fast': if type == 'fast':
return Response(fast_cut_segments(segments, start, end), mimetype='video/MP2T') return Response(fast_cut_segments(segments, start, end), mimetype='video/MP2T')
elif type == 'full': elif type == 'full':
# output as mp4 with no more specific encoding args # output as high-quality mpegts, without wasting too much cpu on encoding
return Response(full_cut_segments(segments, start, end, ['-f', 'mp4']), mimetype='video/mp4') encoding_args = ['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '0', '-f', 'mpegts']
return Response(full_cut_segments(segments, start, end, encoding_args, stream=True), mimetype='video/MP2T')
else: else:
return "Unknown type {!r}. Must be 'fast' or 'full'.".format(type), 400 return "Unknown type {!r}. Must be 'fast' or 'full'.".format(type), 400

Loading…
Cancel
Save