finished: conversion to x264, audio extraction to wav, normalization and reporting has been tested, theora is assumed to work
This commit is contained in:
parent
b165bb628d
commit
7eb29f6fe6
2 changed files with 93 additions and 23 deletions
22
aux.py
22
aux.py
|
@ -40,11 +40,9 @@ class Command:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sequence):
|
def __init__(self, *sequence):
|
||||||
self.__dict__["sequence"] = list(sequence)
|
self.__dict__["sequence_original"] = list(sequence)
|
||||||
self.__dict__["thread"] = None
|
self.reset()
|
||||||
self.__dict__["fifo"] = None
|
|
||||||
self.__dict__["terminated"] = False
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
found = False
|
found = False
|
||||||
|
@ -54,8 +52,14 @@ class Command:
|
||||||
self.sequence[i] = value
|
self.sequence[i] = value
|
||||||
found = True
|
found = True
|
||||||
|
|
||||||
if not found:
|
#if not found:
|
||||||
raise AttributeError("Command has no attribute \'%s\'" %(name))
|
#raise AttributeError("Command has no attribute \'%s\'" %(name))
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.__dict__["sequence"] = list(self.sequence_original)
|
||||||
|
self.__dict__["thread"] = None
|
||||||
|
self.__dict__["fifo"] = None
|
||||||
|
self.__dict__["terminated"] = False
|
||||||
|
|
||||||
def dump(self, sequence=None):
|
def dump(self, sequence=None):
|
||||||
out = []
|
out = []
|
||||||
|
@ -66,7 +70,9 @@ class Command:
|
||||||
for item in sequence:
|
for item in sequence:
|
||||||
if type(item) is list:
|
if type(item) is list:
|
||||||
out += self.dump(item)
|
out += self.dump(item)
|
||||||
else:
|
elif type(item) is Command:
|
||||||
|
out += item.dump()
|
||||||
|
elif item is not None:
|
||||||
out.append(str(item))
|
out.append(str(item))
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
|
@ -6,7 +6,9 @@ import os
|
||||||
import over
|
import over
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
import pathlib # FIXME sort
|
||||||
|
|
||||||
from aux import Command, parse_fps
|
from aux import Command, parse_fps
|
||||||
|
|
||||||
|
@ -18,17 +20,18 @@ _print = over.core.textui.Output("over.video")
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
command = over.core.types.ndict()
|
command = over.core.types.ndict()
|
||||||
command.identify = ("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "INFILE")
|
command.identify = Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "INFILE")
|
||||||
command.normalize_prepass = ("ffmpeg", "-i", "INFILE", "-af", "volumedetect", "-f", "null", "/dev/null")
|
command.normalize_prepass = Command("ffmpeg", "-i", "INFILE", "-af", "volumedetect", "-f", "null", "/dev/null")
|
||||||
command.encode_theora = ("ffmpeg", "-i", "INFILE", "-codec:v", "libtheora", "-qscale:v", "VQ", "-codec:a", "libvorbis", "-qscale:a", "AQ", "NORMALIZE", "OUTFILE")
|
command.encode_theora = Command("ffmpeg", "-i", "INFILE", "-codec:v", "libtheora", "-qscale:v", "VQ", "-codec:a", "libvorbis", "-qscale:a", "AQ", "NORMALIZE", "OUTFILE")
|
||||||
command.encode_x264 = ("ffmpeg", "-i", "INFILE", "-c:v", "libx264", "-preset", "slow", "-crf", "VQ", "-profile:v", "high", "-level", "4.2", "-codec:a", "libvorbis", "-qscale:a", "AQ", "NORMALIZE", "OUTFILE")
|
command.encode_x264 = Command("ffmpeg", "-i", "INFILE", "-c:v", "libx264", "-preset", "slow", "-crf", "VQ", "-profile:v", "high", "-level", "4.2", "-codec:a", "libvorbis", "-qscale:a", "AQ", "NORMALIZE", "OUTFILE")
|
||||||
command.normalize = ("-filter:a", "VOLUME")
|
command.encode_wav = Command("ffmpeg", "-i", "INFILE", "-codec:a", "pcm_s16le", "NORMALIZE", "OUTFILE")
|
||||||
|
command.normalize = Command("-filter:a", "VOLUME")
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main = over.core.app.Main("Over-Video", "0.1", "AWARE-Overwatch Joint Software License", "~/.over/video.cfg")
|
main = over.core.app.Main("Over-Video", "0.1", "AWARE-Overwatch Joint Software License", "~/.over/video.cfg")
|
||||||
main.add_option("profile", "str", "x264", "Encoding profile to use. Available are either §mtheora§/ for Theora and Vorbis in an Ogg container, or §mx264§/ for H.264 and Vorbis in an MP4 container.")
|
main.add_option("profile", "str", "x264", "Encoding profile to use. Available are either §mtheora§/ for Theora and Vorbis in an Ogg container, §mx264§/ for H.264 and Vorbis in an MP4 container, or §mwav§/ which just extracts audio into a wav for further processing.")
|
||||||
main.add_option("video-quality", "float", 22, "Video encoding quality. Use 0-10 for Theora (0 being the lowest, 5-7 is generally watchable) and 0-51 for x264 (0 being lossless, 18-28 is reasonable).", short_name="v")
|
main.add_option("video-quality", "float", 22, "Video encoding quality. Use 0-10 for Theora (0 being the lowest, 5-7 is generally watchable) and 0-51 for x264 (0 being lossless, 18-28 is reasonable).", short_name="v")
|
||||||
main.add_option("audio-quality", "float", 2, "Audio encoding quality with -1 being the worst and 10 being the best.", short_name="a")
|
main.add_option("audio-quality", "float", 2, "Audio encoding quality with -1 being the worst and 10 being the best.", short_name="a")
|
||||||
main.add_option("normalize", "bool", True, "Normalize the audio track.", short_name="N")
|
main.add_option("normalize", "bool", True, "Normalize the audio track.", short_name="N")
|
||||||
|
@ -50,10 +53,10 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
_print("processing §B%s§/" %(tgt), prefix.start)
|
_print("processing §B%s§/" %(tgt), prefix.start)
|
||||||
|
|
||||||
identify_cmd = Command(command.identify)
|
command.identify.reset()
|
||||||
identify_cmd.INFILE = tgt
|
command.identify.INFILE = tgt
|
||||||
identify_cmd.run()
|
command.identify.run()
|
||||||
identify_raw = identify_cmd.get_all_output()
|
identify_raw = command.identify.get_all_output()
|
||||||
identify_dict = json.loads(identify_raw.decode("utf-8"))
|
identify_dict = json.loads(identify_raw.decode("utf-8"))
|
||||||
|
|
||||||
info = over.core.types.ndict()
|
info = over.core.types.ndict()
|
||||||
|
@ -94,18 +97,18 @@ if __name__ == "__main__":
|
||||||
if (main.cfg.armed or main.cfg.dump_commands) and main.cfg.normalize:
|
if (main.cfg.armed or main.cfg.dump_commands) and main.cfg.normalize:
|
||||||
_print("running normalization pre-pass")
|
_print("running normalization pre-pass")
|
||||||
|
|
||||||
norm_pre_cmd = Command(command.normalize_prepass)
|
command.normalize_prepass.reset()
|
||||||
norm_pre_cmd.INFILE = tgt
|
command.normalize_prepass.INFILE = tgt
|
||||||
norm_pre_cmd.run(stderr=True)
|
command.normalize_prepass.run(stderr=True)
|
||||||
|
|
||||||
pb = over.core.textui.ProgressBar(50, int(info.video_fps.value * info.duration), "frames")
|
pb = over.core.textui.ProgressBar(60, int(info.video_fps.value * info.duration), "frames")
|
||||||
|
|
||||||
output_buffer = []
|
output_buffer = []
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(.25)
|
time.sleep(.25)
|
||||||
|
|
||||||
out = norm_pre_cmd.get_output()
|
out = command.normalize_prepass.get_output()
|
||||||
|
|
||||||
if out:
|
if out:
|
||||||
output_buffer.append(out)
|
output_buffer.append(out)
|
||||||
|
@ -130,4 +133,65 @@ if __name__ == "__main__":
|
||||||
pb.blank()
|
pb.blank()
|
||||||
_print("detected volume %.2f dB, correction %.2f dB" %(info.mean_volume, info.volume_correction))
|
_print("detected volume %.2f dB, correction %.2f dB" %(info.mean_volume, info.volume_correction))
|
||||||
|
|
||||||
|
# select encoding command
|
||||||
|
if main.cfg.profile == "theora":
|
||||||
|
encode_cmd = command.encode_theora
|
||||||
|
info.tmp_file = tempfile.mktemp(suffix='.ogv', dir='.')
|
||||||
|
elif main.cfg.profile == "x264":
|
||||||
|
encode_cmd = command.encode_x264
|
||||||
|
info.tmp_file = tempfile.mktemp(suffix='.mp4', dir='.')
|
||||||
|
elif main.cfg.profile == "wav":
|
||||||
|
encode_cmd = command.encode_wav
|
||||||
|
info.tmp_file = tempfile.mktemp(suffix='.wav', dir='.')
|
||||||
|
else:
|
||||||
|
_print("§runknown profile selected§/: §r%s§/" %(main.cfg.profile), prefix.fail)
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
# populate its arguments
|
||||||
|
encode_cmd.reset()
|
||||||
|
encode_cmd.INFILE = tgt
|
||||||
|
encode_cmd.OUTFILE = info.tmp_file
|
||||||
|
encode_cmd.AQ = main.cfg.audio_quality
|
||||||
|
encode_cmd.VQ = main.cfg.video_quality
|
||||||
|
|
||||||
|
if main.cfg.normalize:
|
||||||
|
command.normalize.reset()
|
||||||
|
command.normalize.VOLUME = "volume=%.2fdB" %(info.volume_correction)
|
||||||
|
encode_cmd.NORMALIZE = command.normalize
|
||||||
|
else:
|
||||||
|
encode_cmd.NORMALIZE = None
|
||||||
|
|
||||||
|
if main.cfg.dump_commands:
|
||||||
|
_print("will execute §B%s§/" %(" ".join(encode_cmd.dump())), prefix.start)
|
||||||
|
|
||||||
|
if main.cfg.armed:
|
||||||
|
pb = over.core.textui.ProgressBar(60, int(info.video_fps.value * info.duration), "frames")
|
||||||
|
|
||||||
|
encode_cmd.run(stderr=True)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(.25)
|
||||||
|
|
||||||
|
out = encode_cmd.get_output()
|
||||||
|
|
||||||
|
if out:
|
||||||
|
if b"frame=" in out:
|
||||||
|
frame_id = re.findall(b"frame= *(\d+) ", out)[0]
|
||||||
|
pb.update(int(frame_id))
|
||||||
|
|
||||||
|
elif out is None:
|
||||||
|
break
|
||||||
|
|
||||||
|
original_filesize = over.core.textui.Unit(pathlib.Path(tgt).stat().st_size, "B")
|
||||||
|
new_filesize = over.core.textui.Unit(pathlib.Path(info.tmp_file).stat().st_size, "B")
|
||||||
|
|
||||||
|
pb.blank()
|
||||||
|
_print("encoding finished: %s -> %s" %(original_filesize, new_filesize), prefix.done)
|
||||||
|
|
||||||
|
new_filename = pathlib.Path(tgt).stem + pathlib.Path(info.tmp_file).suffix
|
||||||
|
|
||||||
|
if main.cfg.dump_commands:
|
||||||
|
_print('will execute §Bmv "%s" "%s"§/' %(info.tmp_file, new_filename), prefix.start)
|
||||||
|
|
||||||
|
if main.cfg.armed:
|
||||||
|
pathlib.Path(info.tmp_file).rename(new_filename)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue