diff --git a/over-video.py b/over-video.py index d99ac06..41bdd8f 100755 --- a/over-video.py +++ b/over-video.py @@ -28,7 +28,8 @@ command.encode_generic = Command('ffmpeg', '-i', 'INFILE', 'VIDEO', 'AUDIO', '-s command.sub_vorbis = Command('-codec:a', 'libvorbis', '-qscale:a', 'QUALITY', 'NORMALIZE') command.sub_pcm = Command('-codec:a', 'pcm_s16le', 'NORMALIZE') command.sub_theora = Command('-codec:v', 'libtheora', '-qscale:v', 'QUALITY', 'VFILTER') -command.sub_x264 = Command('PIXFMT', '-codec:v', 'libx264', '-preset', 'slow', '-crf', 'QUALITY', '-profile:v', 'high', '-level', '4.2', 'VFILTER') +command.sub_x264 = Command('PIXFMT', '-codec:v', 'libx264', '-preset', 'PRESET', '-crf', 'QUALITY', '-profile:v', 'high', '-level', '4.2', 'VFILTER') +command.sub_x265 = Command('PIXFMT', '-codec:v', 'libx265', '-preset', 'PRESET', '-crf', 'QUALITY', 'VFILTER') command.sub_normalize = Command('-filter:a', 'VOLUME') command.sub_vfilter = Command('-filter:v', 'ARGS') command.force_yuv420p = Command('-pix_fmt', 'yuv420p') @@ -40,18 +41,21 @@ command.sub_copy_video = Command('-codec:v', 'copy') if __name__ == '__main__': main = over.core.app.Main('over-video', 'Overwatch Video', version.str, 'AO-JSL', use_cfg_file=True) main.add_option('audio', 'str', 'vorbis', 'Audio codec to use, either §mvorbis§/, §mpcm§/, §mcopy§/ or §mdrop§/.', 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='q') - main.add_option('video', 'str', 'x264', 'Video codec to use, either §mx264§/, §mtheora§/, §mcopy§/ or §mdrop§/.', 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='Q') + main.add_option('audio-quality', 'float', 4, 'Audio encoding quality with §m-1§/ being the worst and §m10§/ being the best.', short_name='q') + main.add_option('video', 'str', 'x264', 'Video codec to use, either §mx265§/, §mx264§/, §mtheora§/, §mcopy§/ or §mdrop§/.', short_name='v') + main.add_option('video-preset', 'str', 'slow', 'Video encoding preset, if supported by the selected encoder.', short_name='P') + main.add_option('video-quality', 'float', 22, 'Video encoding quality (CRF). Use §m0§/-§m10§/ for Theora (§m0§/ being the lowest, §m5§/-§m7§/ is generally watchable) and §m0§/-§m51§/ for x264/5 (§m0§/ being lossless, §m18§/-§m28§/ is reasonable).', short_name='Q') main.add_option('normalize', 'bool', True, 'Normalize the audio track.', short_name='n') main.add_option('normalize-target', 'float', -20.0, 'Target mean volume to target.') - main.add_option('normalize-override', 'float', 0.0, 'Volume correction to use instead of computing the required value in a (lengthy) pre-pass.', short_name='N') - main.add_option('ffmpeg-vfilter', 'str', '', 'Raw ffmpeg -filter:v options, e.g. "scale=1280:trunc(ow/a/2)*2,transpose=dir=1"', short_name='f') - main.add_option('move-source', 'str', '', 'Move source file to this directory after conversion.', short_name='m') + main.add_option('normalize-override', 'float', 0.0, 'Volume correction to use instead of computing the required value in a (lengthy) pre-pass.', short_name='N', use_cfg_file=False) + main.add_option('ffmpeg-vfilter', 'str', '', 'Raw ffmpeg -filter:v options, e.g. "§mscale=1280:trunc(ow/a/2)*2,transpose=dir=1§/"', short_name='f') + main.add_option('move-source', 'str', 'processed', 'Move source file to this directory after conversion. Use an empty string to disable.', short_name='m') main.add_option('dump-commands', 'bool', False, 'Print ffmpeg commands that would be executed. If §B--§gnormalize§/ is in effect, the normalization pre-pass will still be performed so that the proper volume correction can be computed.', short_name='D') + main.add_option('probe', 'bool', False, 'Print the raw JSON output of ffprobe and exit.', short_name='p') main.add_option('armed', 'bool', False, 'Perform the suggested action.', short_name='A', use_cfg_file=False) main.enable_help('h') main.add_help('Description', ['Over-Video is a simple video converter.']) + main.add_help('Good encoder settings', ['§Bx264§/: §B--§gvideo§/ §mx264§/ §B--§gvideo-preset§/ §mslow§/ §B--§gvideo-quality§/ §m22§/', '§Bx265§/: §B--§gvideo§/ §mx265§/ §B--§gvideo-preset§/ §mmedium§/ §B--§gvideo-quality§/ §m20§/']) main.parse() # -------------------------------------------------- @@ -80,9 +84,11 @@ if __name__ == '__main__': else: video_words.append('§gcodec§/=§m%s§/' %(main.cfg.video)) - video_words.append('§gquality§/=§m%.1f§/' %(main.cfg.video_quality)) + if main.cfg.video_preset and main.cfg.video in ('x264', 'x265'): + video_words.append('§gpreset§/=§m%s§/' %(main.cfg.video_preset)) + if main.cfg.ffmpeg_vfilter: video_words.append('§gvfilter§/=§m%s§/' %(main.cfg.ffmpeg_vfilter)) @@ -104,7 +110,7 @@ if __name__ == '__main__': main.print('unknown audio codec', prefix.fail) raise RuntimeError - if main.cfg.video not in ('drop', 'copy', 'theora', 'x264'): + if main.cfg.video not in ('drop', 'copy', 'theora', 'x264', 'x265'): main.print('unknown video codec', prefix.fail) raise RuntimeError @@ -131,8 +137,12 @@ if __name__ == '__main__': command.identify.reset() command.identify.INFILE = 'file:' + str(files.infile) command.identify.run() - identify_raw = command.identify.get_all_output() - identify_dict = json.loads(identify_raw.decode('utf-8')) + identify_raw = command.identify.get_all_output().decode('utf-8') + identify_dict = json.loads(identify_raw) + + if main.cfg.probe: + print(identify_raw) + continue info = over.core.types.ndict() @@ -159,7 +169,13 @@ if __name__ == '__main__': info.video_size_x = video['width'] info.video_size_y = video['height'] info.video_fps = over.core.text.Unit(parse_fps(video['r_frame_rate']), 'Hz') - info.video_bitrate = over.core.text.Unit(video['bit_rate'], 'b/s') if 'bit_rate' in video else '§r??§/' + + if 'bit_rate' in video: + info.video_bitrate = over.core.text.Unit(video['bit_rate'], 'b/s') + elif 'tags' in video and 'BPS' in video['tags']: + info.video_bitrate = over.core.text.Unit(int(video['tags']['BPS']), 'b/s') + else: + info.video_bitrate = '§r??§/' info.pixel_fmt = video['pix_fmt'] if audio_streams: @@ -282,6 +298,7 @@ if __name__ == '__main__': elif main.cfg.video == 'x264': command.sub_x264.reset() command.sub_x264.QUALITY = main.cfg.video_quality + command.sub_x264.PRESET = main.cfg.video_preset command.sub_x264.VFILTER = info.vfilter_command if info.pixel_fmt in X264_BANNED_PIXFMTS: @@ -291,6 +308,14 @@ if __name__ == '__main__': command.sub_x264.PIXFMT = None encode_cmd.VIDEO = command.sub_x264 + elif main.cfg.video == 'x265': + command.sub_x265.reset() + command.sub_x265.QUALITY = main.cfg.video_quality + command.sub_x265.PRESET = main.cfg.video_preset + command.sub_x265.VFILTER = info.vfilter_command + command.sub_x265.PIXFMT = None + + encode_cmd.VIDEO = command.sub_x265 # -------------------------------------------------- # run the command if armed