Add VP9/Opus support, use VP8 by default

- support vp9 and opus
- switch to 2 pass encoding
- use ffmpeg -movflags +faststart instead of qtfaststart
This commit is contained in:
j 2016-06-23 14:10:32 +00:00
parent aaacc48259
commit 4785f314cb
3 changed files with 94 additions and 53 deletions

View File

@ -142,6 +142,7 @@ def load_config(init=False):
formats = config.get('video', {}).get('formats')
if set(old_formats) != set(formats):
sformats = supported_formats()
settings.FFMPEG_SUPPORTS_VP9 = 'vp9' in sformats
if sformats:
for f in formats:
if f not in sformats or not sformats[f]:

View File

@ -57,7 +57,10 @@ def supported_formats():
return {
'ogg': 'libtheora' in stdout and 'libvorbis' in stdout,
'webm': 'libvpx' in stdout and 'libvorbis' in stdout,
'vp8': 'libvpx' in stdout and 'libvorbis' in stdout,
'vp9': 'libvpx-vp9' in stdout and 'libopus' in stdout,
'mp4': 'libx264' in stdout and 'DEA.L. aac' in stdout,
'h264': 'libx264' in stdout and 'DEA.L. aac' in stdout,
}
@ -78,6 +81,8 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
'''
profile, format = profile.split('.')
bpp = 0.17
video_codec = 'libvpx'
audio_codec = 'libvorbis'
if 'error' in info:
return False, "Unsupported Format"
@ -140,11 +145,25 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
else:
height = 96
if settings.FFMPEG_SUPPORTS_VP9:
audio_codec = 'libopus'
video_codec = 'libvpx-vp9'
audiorate = 22050
audioquality = -1
audiobitrate = '22k'
audiochannels = 1
if format == 'webm' and audio_codec == 'libopus':
audiorate = 48000
if not audiobitrate:
audiobitrate = '%sk' % {
-1: 32, 0: 48, 1: 64, 2: 96, 3: 112, 4: 128,
5: 144, 6: 160, 7: 192, 8: 256, 9: 320, 10: 512,
}[audioquality]
if format == 'webm' and video_codec == 'libvpx-vp9':
bpp = 0.15
if info['video'] and 'display_aspect_ratio' in info['video'][0]:
# dont make video bigger
height = min(height, info['video'][0]['height'])
@ -197,11 +216,17 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
]
if format == 'webm':
video_settings += [
'-c:v', video_codec,
'-deadline', 'good',
'-cpu-used', '0',
'-lag-in-frames', '16',
'-cpu-used', '1' if video_codec == 'libvpx-vp9' else '0',
'-lag-in-frames', '25',
'-auto-alt-ref', '1',
]
if video_codec == 'libvpx-vp9':
video_settings += [
'-tile-columns', '6',
'-frame-parallel', '1',
]
if format == 'mp4':
video_settings += [
'-c:v', 'libx264',
@ -232,7 +257,9 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
else:
video_settings += ['-map', '0:%s,0:%s' % (info['audio'][audio_track]['id'], n)]
mono_mix = False
audio_settings = ['-ar', str(audiorate), '-aq', str(audioquality)]
audio_settings = ['-ar', str(audiorate)]
if audio_codec != 'libopus':
audio_settings += ['-aq', str(audioquality)]
if mono_mix:
ac = 2
else:
@ -246,31 +273,49 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
audio_settings += ['-ab', audiobitrate]
if format == 'mp4':
audio_settings += ['-c:a', 'aac', '-strict', '-2']
elif audio_codec == 'libopus':
audio_settings += ['-c:a', 'libopus', '-frame_duration', '60']
else:
audio_settings += ['-c:a', 'libvorbis']
audio_settings += ['-c:a', audio_codec]
else:
audio_settings = ['-an']
cmd = [settings.FFMPEG,
cmds = []
base = [settings.FFMPEG,
'-nostats', '-loglevel', 'error',
'-y', '-i', video, '-threads', '4', '-map_metadata', '-1', '-sn'] \
+ audio_settings \
+ video_settings
'-y', '-i', video, '-threads', '4', '-map_metadata', '-1', '-sn']
if format == 'webm':
enc_target = target + '.tmp.webm'
elif format == 'mp4':
enc_target = target + '.tmp.mp4'
else:
enc_target = target
if format == 'webm':
cmd += ['-f', 'webm', enc_target]
post = ['-f', 'webm', enc_target]
elif format == 'mp4':
# mp4 needs postprocessing(qt-faststart), write to temp file
cmd += ["%s.mp4" % enc_target]
post = ['-movflags', '+faststart', '-f', 'mp4', enc_target]
else:
cmd += [enc_target]
post = [target]
if video_settings != ['-vn']:
pass1_post = post[:]
pass1_post[-1] = '/dev/null'
if format == 'webm':
pass1_post = ['-speed', '4'] + pass1_post
post = ['-speed', '1'] + post
cmds.append(base + ['-an', '-pass', '1', '-passlogfile', '%s.log' % target]
+ video_settings + pass1_post)
cmds.append(base + ['-pass', '2', '-passlogfile', '%s.log' % target]
+ audio_settings + video_settings + post)
else:
cmds.append(base + audio_settings + video_settings + post)
# print(cmd)
# print(cmds)
n = 0
for cmd in cmds:
n += 1
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
@ -278,23 +323,13 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
stdout, stderr = p.communicate()
if p.returncode != 0:
t = "%s.mp4" % enc_target if format == 'mp4' else enc_target
if os.path.exists(t):
os.unlink(t)
if os.path.exists(enc_target):
os.unlink(enc_target)
if os.path.exists(target):
os.unlink(target)
stdout = stdout.replace('\r\n', '\n').replace('\r', '\n')
return False, stdout
if format == 'mp4':
cmd = ['qt-faststart', "%s.mp4" % enc_target, enc_target]
# print(cmd)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=open('/dev/null', 'w'),
stderr=subprocess.STDOUT,
close_fds=True)
p.communicate()
os.unlink("%s.mp4" % enc_target)
elif format == 'webm' and audio_only:
if format == 'webm' and audio_only:
cmd = ['mkvmerge', '-w', '-o', target, '--cues', '-1:all', enc_target]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@ -305,6 +340,8 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
enc_target = target
if p.returncode == 0 and enc_target != target:
shutil.move(enc_target, target)
for f in glob('%s.log*' % target):
os.unlink(f)
return True, None
@ -358,7 +395,8 @@ def ffmpeg_frame_cmd(video, frame, position, height=128):
def ffmpeg_version():
p = subprocess.Popen([settings.FFMPEG],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
version = stderr.split(' ')[2].split('-')[0]
try:
@ -414,16 +452,17 @@ def timeline(video, prefix, modes=None, size=None):
size = [64, 16]
if isinstance(video, basestring):
video = [video]
cmd = [
os.path.join(settings.PROJECT_ROOT, '../bin/oxtimelines'),
cmd = ['../bin/oxtimelines',
'-s', ','.join(map(str, reversed(sorted(size)))),
'-m', ','.join(modes),
'-o', prefix,
'-c', os.path.join(prefix, 'cuts.json'),
] + video
# print(cmd)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
close_fds=True)
# print(cmd)
# p = subprocess.Popen(cmd)
p.wait()

View File

@ -165,6 +165,7 @@ LOGGING = {
AUTH_PROFILE_MODULE = 'user.UserProfile'
AUTH_CHECK_USERNAME = True
FFMPEG = 'ffmpeg'
FFMPEG_SUPPORTS_VP9 = True
#=========================================================================
#Pan.do/ra related settings settings