f'This script requires python version 3.6 or higher.'
from common.cli import Cli
from common.filectrl import FileCtrl
from common.processctrl import Logger, call_sanity_check, call, MultiProcess
from common.misc import decorate, check_extention, time_msg, get_scale_metadata_setting
import common.dv_workflow as dv
import common.app_cmd as cmd
from datetime import datetime
import sys
import os
import shutil
import math
import platform
import time
from common.globals import set_logger, set_file_ctrl, set_multiproc, fc, log, mp
def windows_sleep():
if platform.system() == 'Windows':
time.sleep(5)
def get_mdpp_help():
print((
'Post-processing configuration options: \n'
' none - disable post-processing step entirely. \n'
' L5 - enable post-processing for L5 metadata only. \n'
' full - enable standard post-processing. \n'
))
def validate(cli):
sanity_check_apps = {'PREPROC': ('dee_dv5_preproc', '1.0'),
'VES_MUXER': ('dee_dv_vesmux', '1.1'),
'POSTPROC': ('dee_dv_postproc', '1.1'),
'PARSE_MEZZ': ('dee_dv_parse_mezz', '1.0'),
'STREAM_SPLITTER': ('dee_stream_splitter', '1.0'),
'MEDIAINFO': ('mediainfo', None)}
for tag, (app, version) in sanity_check_apps.items():
if call_sanity_check(tag, [os.path.join(cli.toolset_dir, app), '-h'], log().errors, version):
raise RuntimeError
if call_sanity_check('MP4_MUXER', [os.path.join(cli.toolset_dir, 'mp4muxer'), '-h',
'--log-file', fc().register_temp_file('mp4muxer_log_sanity_check')], log().errors):
raise RuntimeError
if call_sanity_check('FFMPEG', [cli.ffmpeg, '-version'], log().errors):
raise RuntimeError
if cli.dvesverifier:
if call_sanity_check('DVESVERIFIER', [cli.dvesverifier], log().errors):
raise RuntimeError
license_check_apps = ['dee_dv5_preproc', 'dee_dv_vesmux',
'dee_dv_postproc', 'dee_dv_parse_mezz', 'dee_stream_splitter']
for app in license_check_apps:
if call('LICENSE_CHECK', [os.path.join(cli.toolset_dir, app), '-l', cli.license], 'all', log().errors):
raise RuntimeError
def max_scene_frames(gop_size):
max_frames = gop_size * 2
if max_frames > 255:
max_frames = min(gop_size, 255)
return max_frames
def configure_dv_preproc(cli, cfg):
pipe_buffer_size = dv.get_pipe_buffer_size(
cli.yuv_buffer_size, cfg['target_resolution'])
dv_preproc_cfg = {'license': cli.license,
'input-format': cli.input_format,
'resize-options': f"scale={cfg['target_resolution']}" if cfg['target_resolution'] != cfg['input_resolution'] else None,
'input': cli.input,
'input-metadata': cli.input_metadata,
'metadata-offset': cli.metadata_offset,
'start': cli.start,
'duration': cli.duration,
'overwrite': 1,
'decoder': dv.get_decoder(cli.input_format),
'max-scene-frames': max_scene_frames(cfg['gop_size']),
'show-frames': 'counters' if cli.progress else None,
'progress': '1' if cli.progress else '0',
'keep-temp': cli.keep_temp,
'temp-dir': fc().get_temp_dir(),
'loglevel': 'info:timestamps=0' if cli.print_all == 'all' or cli.progress else 'error:timestamps=0'}
if cli.encoder_pass_num == 2 and cli.data_stream:
cmds = []
# pass 1
dv_preproc_cfg.update({'output': dv.get_output_name(cfg['output'], True, pipe_buffer_size),
'output-rpu': "NULL"})
cmds.append(cmd.DvPreproc(os.path.join(
cli.toolset_dir, 'dee_dv5_preproc'), dv_preproc_cfg).get_cmd())
# pass 2
dv_preproc_cfg.update({'output': dv.get_output_name(cfg['output'], True, pipe_buffer_size),
'output-rpu': dv.get_output_name(cfg['output_rpu'], True, cfg['rpu_pipe_buffer_size']),
})
cmds.append(cmd.DvPreproc(os.path.join(
cli.toolset_dir, 'dee_dv5_preproc'), dv_preproc_cfg).get_cmd())
return cmds
else:
dv_preproc_cfg.update({'output': dv.get_output_name(cfg['output'], cli.data_stream, pipe_buffer_size),
'output-rpu': dv.get_output_name(cfg['output_rpu'], cli.data_stream, cfg.get('rpu_pipe_buffer_size')),
})
return [cmd.DvPreproc(os.path.join(cli.toolset_dir, 'dee_dv5_preproc'), dv_preproc_cfg).get_cmd()]
def configure_encoder(cli, cfg):
data_rate = cfg['data_rate']
enc_cfg = {'ffmpeg': cli.ffmpeg,
'input_resolution': cfg['input_resolution'],
'target_resolution': cfg['target_resolution'].replace('x', ':'),
'frame_rate': cfg['frame_rate'],
'loglevel': 'info' if cli.print_all == 'all' else 'error',
'input': dv.get_input_name(cfg['input'], cli.data_stream, None),
'preset': cli.preset,
'data_rate': data_rate,
'gop_size': cfg['gop_size'],
'lookahead': min(cfg['gop_size'], 240),
'buffer_size': 160000 * cli.gop_duration,
'pass_str': '',
'output': dv.get_output_name(cfg['output'], cli.data_stream, None),
}
encoder = ('{ffmpeg} -y '
'-f rawvideo '
'-s {input_resolution} '
'-pix_fmt yuv420p10le '
'-loglevel {loglevel} '
'-framerate {frame_rate} '
'-i {input} '
'-vf scale=3840:2160'
'-c:v libx265 -preset {preset} ' # changing this would over ride dovi script settings
'-x265-params crf=17:deblock=-2-2:aq-mode=2:min-keyint=23:keyint=250:level-idc=5.1:no-open-gop=1:aud=1:hrd=1:repeat-headers=1:sar=1:sao=0:chromaloc=0:colormatrix=2:colorprim=2:transfer=2:'
'vbv-maxrate=160000:vbv-bufsize=160000:'
'input-csp=i420:fps={frame_rate}:range=full:' # must be full range
'no-info=0:log-level=0'
'{pass_str} '
'-f hevc {output} '
'-nostdin')
if cli.encoder_pass_num == 1:
return [encoder.format(**enc_cfg).split(' ')]
elif cli.encoder_pass_num == 2:
enc_cmds = []
enc_cfg_pass_2 = dict(enc_cfg)
prefix = os.path.join(fc().get_temp_dir(), cfg['pass_log'])
pass_str = ":pass={num}:stats='{prefix}'"
# pass 1
enc_cfg['pass_str'] = pass_str.format(num=1, prefix=prefix)
enc_cfg['output'] = 'NUL' if platform.system(
) == 'Windows' else '/dev/null'
enc_cmds.append(encoder.format(**enc_cfg).split(' '))
# pass 2
enc_cfg_pass_2['pass_str'] = pass_str.format(num=2, prefix=prefix)
enc_cmds.append(encoder.format(**enc_cfg_pass_2).split(' '))
return enc_cmds
def configure_dv_vesmux(cli, cfg):
bl_pipe_buffer = 4096
dv_vesmux_cfg = {'license': cli.license,
'overwrite': 1,
'input-bl': dv.get_input_name(cfg['input_bl'], cli.data_stream, bl_pipe_buffer),
'input-rpu': dv.get_input_name(cfg['input_rpu'], cli.data_stream, cfg.get('rpu_pipe_buffer_size'), 'true'),
'output': fc().register_temp_file(cfg['output']),
'loglevel': 'info:timestamps=0' if cli.print_all == 'all' or cli.progress else 'error:timestamps=0'}
return cmd.DvVesmux(os.path.join(cli.toolset_dir, 'dee_dv_vesmux'), dv_vesmux_cfg).get_cmd()
def configure_dv_postproc(cli, cfg):
if cli.mdpp == 'none':
return None
dv_postproc_cfg = {'license': cli.license,
'overwrite': 1,
'progress': cli.progress,
'input': fc().get_temp_file(cfg['input']),
'scale-metadata': get_scale_metadata_setting(cfg['input_resolution'], cfg['target_resolution']),
'dv-profile': 5,
'output': fc().register_temp_file(cfg['output']),
'loglevel': 'info:timestamps=0' if cli.print_all == 'all' or cli.progress else 'error:timestamps=0'}
if cli.mdpp == 'L5':
dv_postproc_cfg.update({'update-L4': 0,
'update-L6': 0,
'L1-filtering': 'none', })
return cmd.DvPostproc(os.path.join(cli.toolset_dir, 'dee_dv_postproc'), dv_postproc_cfg).get_cmd()
def create_output_file(cli, cfg):
output = fc().register_file(cfg['output_path'])
if output.endswith('.mp4'):
mp4muxer = [os.path.join(cli.toolset_dir, 'mp4muxer'),
'--overwrite',
'--input-file', fc().get_temp_file(cfg['input_path']),
'--dv-profile', '5',
'--log-file', fc().register_temp_file(cfg['log_path']),
'--mpeg4-comp-brand', 'dby1',
'--output-file', output]
if cli.codec_id == 'dvh1':
mp4muxer.append('--dvh1flag')
call(cfg['id'], mp4muxer, cli.print_all, log().errors,
log().infos, None, decorate, 'MP4_MUXER')
else:
shutil.move(fc().get_temp_file(cfg['input_path']), output)
def configure_dv_es_verifier(cli, cfg):
return[cli.dvesverifier,
'-i', fc().get_temp_file(cfg['input']),
'-dp', '5']
def configure_splitter(cli, cfg):
splitter_cfg = {'license': cli.license,
'block-size': cfg['block_size'] if 'block_size' in cfg else None,
'input': dv.get_input_name(cfg['input'], True, cfg['input_buffer'] if 'input_buffer' in cfg else None, 'true') + ':read_threshold=1'}
splitter_cfg = cmd.StreamSplitter(os.path.join(
cli.toolset_dir, 'dee_stream_splitter'), splitter_cfg).get_cmd()
# Every ouput path needs to be a separate element in list, otherwise they are wrapped up in quotes and interpreted as one path
return splitter_cfg + ['--output'] + [dv.get_output_name(o, True, cfg['output_buffer'] if 'output_buffer' in cfg else None, '0') for o in cfg['output']]
def make_prefix(file_no, target_resolution):
return str(file_no) + '_' + target_resolution + '_'
def data_stream_workflow(cli, cfg):
rpu_pipe_buffer_size = 4096 * cfg['gop_size']
original_resolution = cfg['input_resolution']
for i, (target_resolution, output_path, data_rate) in enumerate(zip(cli.target_resolution, cli.output, cli.data_rate)):
prefix = make_prefix(i, target_resolution)
preproc = configure_dv_preproc(cli, {'input_resolution': original_resolution,
'target_resolution': target_resolution,
'gop_size': cfg['gop_size'],
'output_rpu': prefix + 'dv_preproc_output_rpu.bin',
'rpu_pipe_buffer_size': rpu_pipe_buffer_size,
'output': prefix + 'dv_preproc_output.yuv'}) # list of cmds
encoder = configure_encoder(cli, {'input': prefix + 'dv_preproc_output.yuv',
'gop_size': cfg['gop_size'],
'data_rate': data_rate,
'input_resolution': target_resolution,
'target_resolution': target_resolution,
'frame_rate': cfg['frame_rate'],
'pass_log': prefix + 'ffmpegPassLog',
'output': prefix + 'ffmpeg_output_hevc.hevc'}) # list of cmds
vesmux = configure_dv_vesmux(cli, {'input_bl': prefix + 'ffmpeg_output_hevc.hevc',
'input_rpu': prefix + 'dv_preproc_output_rpu.bin',
'rpu_pipe_buffer_size': rpu_pipe_buffer_size,
'output': prefix + 'dv_vesmux_output.hevc'})
postproc = configure_dv_postproc(cli, {'input': prefix + 'dv_vesmux_output.hevc',
'input_resolution': target_resolution,
'target_resolution': target_resolution,
'output': prefix + 'dv_postproc_output_hevc.hevc'})
if cli.encoder_pass_num == 1:
t1 = datetime.now()
mp().run_multiproc(call, f"[{target_resolution}] PREPROC", preproc[0], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC')
windows_sleep()
mp().run_multiproc(call, f"[{target_resolution}] VES_MUXER", vesmux, cli.print_all, log().errors, None, mp().lock, decorate, 'VES_MUXER')
windows_sleep()
mp().run_multiproc(call, f"[{target_resolution}] ENCODER", encoder[0], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER')
mp().join(log().errors)
log().info(time_msg(
f'[{target_resolution}] PREPROC + ENCODER + VES_MUXER', t1, datetime.now()))
elif cli.encoder_pass_num == 2:
# pass 1
t1 = datetime.now()
mp().run_multiproc(call, f"[{target_resolution}] PREPROC_PASS1", preproc[0], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC_PASS1')
windows_sleep()
mp().run_multiproc(call, f"[{target_resolution}] ENCODER_PASS1", encoder[0], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER_PASS1')
mp().join(log().errors)
log().info(time_msg(
f'[{target_resolution}] PREPROC_PASS1 + ENCODER_PASS1', t1, datetime.now()))
# pass 2
t1 = datetime.now()
mp().run_multiproc(call, f"[{target_resolution}] PREPROC_PASS2", preproc[1], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC_PASS2')
windows_sleep()
mp().run_multiproc(call, f"[{target_resolution}] VES_MUXER", vesmux, cli.print_all, log().errors, None, mp().lock, decorate, 'VES_MUXER')
windows_sleep()
mp().run_multiproc(call, f"[{target_resolution}] ENCODER_PASS2", encoder[1], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER_PASS2')
mp().join(log().errors)
log().info(time_msg(
f'[{target_resolution}] PREPROC_PASS2 + ENCODER_PASS2 + VES_MUXER', t1, datetime.now()))
if postproc:
if call(f"[{target_resolution}] POSTPROC", postproc, cli.print_all, log().errors, log().infos, None, decorate, 'POSTPROC'):
raise RuntimeError
input_path = prefix
input_path += 'dv_postproc_output_hevc.hevc'if postproc else 'dv_vesmux_output.hevc'
if cli.dvesverifier:
verifier = configure_dv_es_verifier(cli, {'input': input_path})
dv.run_verifier(f'[{target_resolution}] DVESVERIFIER', verifier, 'all', log(
).errors, log().infos, None, decorate, 'DVESVERIFIER')
create_output_file(cli, {'input_path': input_path,
'output_path': output_path,
'log_path': prefix + 'mp4muxer_log().log',
'id': f'[{target_resolution}] MP4_MUXER'})
windows_sleep()
def data_stream_workflow_parallel(cli, cfg):
max_output_res = dv.find_max_resolution(cli.target_resolution)
original_resolution = cfg['input_resolution']
rpu_pipe_buffer_size = 4096 * cfg['gop_size']
yuv_pipe_buffer_size = dv.get_pipe_buffer_size(
cli.yuv_buffer_size, original_resolution)
encoders = []
vesmuxers = []
postprocs = []
preproc = configure_dv_preproc(cli, {'input_resolution': original_resolution,
'target_resolution': max_output_res,
'gop_size': cfg['gop_size'],
'output_rpu': 'dv_preproc_output_rpu.bin',
'rpu_pipe_buffer_size': rpu_pipe_buffer_size,
'output': 'dv_preproc_output.yuv'}) # list of cmds
splitter_rpu = configure_splitter(cli, {'block_size': rpu_pipe_buffer_size,
'input_buffer': rpu_pipe_buffer_size,
'output_buffer': rpu_pipe_buffer_size,
'input': 'dv_preproc_output_rpu.bin',
'output': [make_prefix(i, target_res) + 'dv_preproc_output_rpu.bin' for i, target_res in enumerate(cli.target_resolution)]})
splitter_yuv = configure_splitter(cli, {'block_size': yuv_pipe_buffer_size,
'input_buffer': yuv_pipe_buffer_size,
'output_buffer': yuv_pipe_buffer_size,
'input': 'dv_preproc_output.yuv',
'output': [make_prefix(i, target_res) + 'dv_preproc_output.yuv' for i, target_res in enumerate(cli.target_resolution)]})
for i, (target_resolution, output_path, data_rate) in enumerate(zip(cli.target_resolution, cli.output, cli.data_rate)):
prefix = make_prefix(i, target_resolution)
encoders.append(
configure_encoder(cli, {'input': prefix + 'dv_preproc_output.yuv',
'gop_size': cfg['gop_size'],
'data_rate': data_rate,
'input_resolution': max_output_res,
'target_resolution': target_resolution,
'frame_rate': cfg['frame_rate'],
'pass_log': prefix + 'ffmpegPassLog',
'output': prefix + 'ffmpeg_output_hevc.hevc'})) # appending list of cmds
vesmuxers.append(
configure_dv_vesmux(cli, {'input_bl': prefix + 'ffmpeg_output_hevc.hevc',
'input_rpu': prefix + 'dv_preproc_output_rpu.bin',
'rpu_pipe_buffer_size': rpu_pipe_buffer_size,
'output': prefix + 'dv_vesmux_output.hevc'}))
postprocs.append(
configure_dv_postproc(cli, {'input': prefix + 'dv_vesmux_output.hevc',
'input_resolution': max_output_res,
'target_resolution': target_resolution,
'output': prefix + 'dv_postproc_output_hevc.hevc'}))
t1 = datetime.now()
if cli.encoder_pass_num == 1:
# PREPROC PASS 1
mp().run_multiproc(call, f"[{max_output_res}] PREPROC", preproc[0], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC')
# SPLITTER YUV
windows_sleep()
mp().run_multiproc(call, f"[{max_output_res}] SPLITTER_YUV", splitter_yuv, "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'SPLITTER_YUV')
# SPLITTER RPU
mp().run_multiproc(call, f"[{max_output_res}] SPLITTER_RPU", splitter_rpu, "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'SPLITTER_RPU')
for (encoder, vesmux, resolution) in zip(encoders, vesmuxers, cli.target_resolution):
# VES_MUXER
windows_sleep()
mp().run_multiproc(call, f"[{resolution}] VES_MUXER", vesmux, cli.print_all, log().errors, None, mp().lock, decorate, 'VES_MUXER')
# ENCODER
windows_sleep()
mp().run_multiproc(call, f"[{resolution}] ENCODER", encoder[0], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER')
mp().join(log().errors)
log().info(time_msg(f'PREPROC + ENCODER + VES_MUXER', t1, datetime.now()))
elif cli.encoder_pass_num == 2:
# pass 1
# PREPROC PASS 1
mp().run_multiproc(call, f"[{max_output_res}] PREPROC_PASS1", preproc[0], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC_PASS1')
# SPLITTER YUV
windows_sleep()
mp().run_multiproc(call, f"[{max_output_res}] SPLITTER_YUV", splitter_yuv, "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'SPLITTER_YUV')
for (encoder, resolution) in zip(encoders, cli.target_resolution):
# ENCODER 1
windows_sleep()
mp().run_multiproc(call, f"[{resolution}] ENCODER_PASS1", encoder[0], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER_PASS1')
mp().join(log().errors)
log().info(time_msg(f'PREPROC_PASS1 + ENCODER_PASS1', t1, datetime.now()))
# pass 2
t1 = datetime.now()
# PREPROC PASS 2
mp().run_multiproc(call, f"[{target_resolution}] PREPROC_PASS2", preproc[1], "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'PREPROC_PASS2')
# SPLITTER YUV
windows_sleep()
mp().run_multiproc(call, f"[{max_output_res}] SPLITTER_YUV", splitter_yuv, "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'SPLITTER_YUV')
# SPLITTER RPU
mp().run_multiproc(call, f"[{max_output_res}] SPLITTER_RPU", splitter_rpu, "all" if cli.progress == 1 else cli.print_all, log().errors, None, mp().lock, decorate, 'SPLITTER_RPU')
for (encoder, vesmux, resolution) in zip(encoders, vesmuxers, cli.target_resolution):
# VES_MUXER
windows_sleep()
mp().run_multiproc(call, f"[{resolution}] VES_MUXER", vesmux, cli.print_all, log().errors, None, mp().lock, decorate, 'VES_MUXER')
# ENCODER 2
windows_sleep()
mp().run_multiproc(call, f"[{resolution}] ENCODER_PASS2", encoder[1], cli.print_all, log().errors, None, mp().lock, decorate, 'ENCODER_PASS2')
mp().join(log().errors)
log().info(time_msg(f'PREPROC_PASS2 + ENCODER_PASS2 + VES_MUXER', t1, datetime.now()))
if postprocs and postprocs[0]:
for (postproc, resolution) in zip(postprocs, cli.target_resolution):
mp().run_multiproc(call, f"[{resolution}] POSTPROC", postproc, cli.print_all, log().errors, None, mp().lock, decorate, 'POSTPROC')
mp().join(log().errors)
input_paths = [make_prefix(i, target_res) + ('dv_postproc_output_hevc.hevc' if postproc else 'dv_vesmux_output.hevc') for i, target_res in enumerate(cli.target_resolution)]
if cli.dvesverifier:
for (input_path, target_resolution) in zip(input_paths, cli.target_resolution):
verifier = configure_dv_es_verifier(cli, {'input': input_path})
dv.run_verifier(f'[{target_resolution}] DVESVERIFIER', verifier, 'all', log().errors, log().infos, None, decorate, 'DVESVERIFIER')
for i, (input_path, output_path, target_resolution) in enumerate(zip(input_paths, cli.output, cli.target_resolution)):
create_output_file(cli, {'input_path': input_path,
'output_path': output_path,
'log_path': make_prefix(i, target_resolution) + 'mp4muxer_log().log',
'id': f'[{target_resolution}] MP4_MUXER'})
windows_sleep()
def file_based_workflow(cli, cfg):
max_res = dv.find_max_resolution(cli.target_resolution)
preproc = configure_dv_preproc(cli, {'input_resolution': cfg['input_resolution'],
'target_resolution': max_res,
'gop_size': cfg['gop_size'],
'output_rpu': 'dv_preproc_output_rpu.bin',
'output': 'dv_preproc_output.yuv'}) # list of cmds
if call(f"[{max_res}] PREPROC", preproc[0], "all" if cli.progress == 1 else cli.print_all, log().errors, log().infos, None, decorate, 'PREPROC'):
raise RuntimeError
for i, (target_resolution, output_path, data_rate) in enumerate(zip(cli.target_resolution, cli.output, cli.data_rate)):
prefix = make_prefix(i, target_resolution)
encoder = configure_encoder(cli, {'input': 'dv_preproc_output.yuv',
'gop_size': cfg['gop_size'],
'data_rate': data_rate,
'input_resolution': max_res,
'target_resolution': target_resolution,
'frame_rate': cfg['frame_rate'],
'pass_log': prefix + 'ffmpegPassLog',
'output': prefix + 'ffmpeg_output_hevc.hevc'}) # list of cmds
vesmux = configure_dv_vesmux(cli, {'input_bl': prefix + 'ffmpeg_output_hevc.hevc',
'input_rpu': 'dv_preproc_output_rpu.bin',
'output': prefix + 'dv_vesmux_output.hevc'})
postproc = configure_dv_postproc(cli, {'input_resolution': max_res,
'target_resolution': target_resolution,
'input': prefix + 'dv_vesmux_output.hevc',
'output': prefix + 'dv_postproc_output_hevc.hevc'})
for i, cmd in enumerate(encoder):
decorator_text = f"ENCODER_PASS{i+1}" if len(
encoder) > 1 else "ENCODER"
if call(f"[{target_resolution}] " + decorator_text, cmd, "all" if cli.progress == 1 else cli.print_all, log().errors, log().infos, None, decorate, decorator_text):
raise RuntimeError
if call(f"[{target_resolution}] VES_MUXER", vesmux, cli.print_all, log().errors, log().infos, None, decorate, 'VES_MUXER'):
raise RuntimeError
if postproc:
if call(f"[{target_resolution}] POSTPROC", postproc, cli.print_all, log().errors, log().infos, None, decorate, 'POSTPROC'):
raise RuntimeError
input_path = prefix
input_path += 'dv_postproc_output_hevc.hevc'if postproc else 'dv_vesmux_output.hevc'
if cli.dvesverifier:
verifier = configure_dv_es_verifier(cli, {'input': input_path})
dv.run_verifier(f'[{target_resolution}] DVESVERIFIER', verifier, 'all', log().errors, log().infos, None, decorate, 'DVESVERIFIER')
create_output_file(cli, {'input_path': input_path,
'output_path': output_path,
'log_path': prefix + 'mp4muxer_log().log',
'id': f'[{target_resolution}] MP4_MUXER'})
def reconfigure_cli_params(cli, info):
cli.start, cli.end, cli.duration = dv.reconfigure_start_end_duration(cli.parser.get_default('start'), cli.parser.get_default('end'), cli.parser.get_default('duration'), cli.start, cli.end, cli.duration, info['frame_rate'])
cli.target_resolution = dv.reconfigure_target_resolution(info['input_resolution'], cli.target_resolution, len(cli.output))
cli.data_rate = dv.reconfigure_data_rates(cli.data_rate, cli.target_resolution)
cli.output = dv.reconfigure_output_names(cli.output, cli.data_rate, cli.target_resolution, cli.overwrite)
def run(cli):
validate(cli)
info = dv.get_input_info(cli.input, cli.input_format, os.path.join(cli.toolset_dir, 'mediainfo'), os.path.join(cli.toolset_dir, 'dee_dv_parse_mezz'), cli.print_all, cli.license)
info['gop_size'] = math.floor(math.ceil((info['frame_rate'])) * cli.gop_duration)
reconfigure_cli_params(cli, info)
for output in cli.output:
check_extention(output, ('.h265', '.265', '.mp4'))
if cli.data_stream and cli.data_stream_arch == 'serial':
data_stream_workflow(cli, info)
elif cli.data_stream and cli.data_stream_arch == 'parallel':
data_stream_workflow_parallel(cli, info)
else:
file_based_workflow(cli, info)
def init_cli():
cli_instance = Cli("Dolby Vision profile 5 encoding workflow.",
needs_license=True,
needs_concurency=False,
needs_progress=True,
needs_toolset_dir=True,
needs_overwrite=True,
needs_print_all=True,
needs_temp_dir=True,
needs_more_help=True)
cli_instance.add_option('--ffmpeg', help="FFmpeg executable.", default='ffmpeg')
cli_instance.add_option('--dvesverifier', help="Dolby Vision ES Verifier executable. If not specified, the script will skip the verification step.", required=False)
cli_instance.add_option('--input-format', help='Input format followed by format-specific options. Use '
'"--morehelp input-format" for more details. Values: '
'jpeg2000_list|jpeg2000_mxf|prores_list|prores_mov|tiff_list.', required=True)
cli_instance.add_option('-i', '--input', help="Input file with mezzanine video. Input directory for list-based inputs.", required=True)
cli_instance.add_option('-m', '--input-metadata', help="Optional input metadata file. If not specified, the application attempts to extract metadata from the input.", required=False)
cli_instance.add_option('-o', '--output', help='Space-separated output file paths in HEVC or MP4 format.', required=True, nargs='+')
cli_instance.add_option('--metadata-offset', help='Offset added to each frame index, when accessing frame metadata from the source.', required=False, default=0, type=int)
cli_instance.add_option('--start', help="Start position in 'xs' format, where 'x' represents seconds or 'xf' format, where 'x' is a frame number.", required=False, default="0s")
group = cli_instance.parser.add_mutually_exclusive_group()
group.add_argument('--duration', help="Duration in 'xs' format, where 'x' represents seconds or 'xf' format, where 'x' is a frame number. '-1' means \"end of file\".", required=False, default="-1")
group.add_argument('--end', help="End position in 'xs' format, where 'x' represents seconds or 'xf' format, where 'x' is a frame number. '-1' means \"end of file\".", required=False, default="-1")
cli_instance.add_option('-r', '--target-resolution', help="Output resolution, or space-separated target resolutions in 'WxH' format.", required=False, default=[], nargs='+')
cli_instance.add_option('--data-rate', help="Target data rate, or space-separated list of target data rates, in kbps units. '0' means \"assign automatically\".", required=False, default=[], type=int, nargs='+')
cli_instance.add_option('--preset', help='Encoder preset.', choices=['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow'], default='medium')
cli_instance.add_option('--data-stream', help='Use data streaming instead of temporary files, wherever possible.', choices=[0, 1], type=int, default=0, required=False)
cli_instance.add_option('--data-stream-arch', help='Data streaming architecture. Relevant only if --data-stream is set to 1.', choices=['serial', 'parallel'], default='serial', required=False)
cli_instance.add_option('--yuv-buffer-size', help="YUV buffer size in bytes. '-1' means \"assign automatically\". Relevant only if 'data-stream' is enabled.", type=int, default=-1, required=False)
cli_instance.add_option('-p', '--encoder-pass-num', help='Number of encoder passes.', choices=[1, 2], required=False, default=2, type=int)
cli_instance.add_option('-g', '--gop-duration', help='GOP duration (seconds). Affects the distance between IDR frames in outputs.', default=2, type=int, required=False)
cli_instance.add_option('--codec-id', help="Video codec ID. Relevant for MP4 outputs only.", default='dvhe', choices=['dvhe', 'dvh1'])
cli_instance.add_option('--mdpp', help='Post-processing configuration. Use "--morehelp mdpp" for more details.', choices=['full', 'L5', 'none'], default='full')
cli_instance.add_more_help('mdpp', get_mdpp_help)
cli_instance.add_more_help('input-format', dv.get_input_format_help, cli_instance, 'dee_dv5_preproc')
cli_instance.parse()
return cli_instance
def main():
try:
set_logger(Logger())
cli = init_cli()
set_file_ctrl(FileCtrl(cli.temp_dir, cli.keep_temp))
set_multiproc(MultiProcess())
run(cli)
log().execution_summary()
except RuntimeError:
log().execution_summary()
sys.exit(1)
if __name__ == '__main__':
main()