single video render
This commit is contained in:
parent
2a2516bff9
commit
95a41fc2e2
2 changed files with 131 additions and 5 deletions
|
@ -16,6 +16,9 @@ class Command(BaseCommand):
|
|||
parser.add_argument('--duration', action='store', dest='duration', default="3600", help='target duration of all fragments in seconds')
|
||||
parser.add_argument('--offset', action='store', dest='offset', default="1024", help='inital offset in pi')
|
||||
parser.add_argument('--no-video', action='store_true', dest='no_video', default=False, help='don\'t render video')
|
||||
parser.add_argument('--single-file', action='store_true', dest='single_file', default=False, help='render to single video')
|
||||
parser.add_argument('--keep-audio', action='store_true', dest='keep_audio', default=False, help='keep independent audio tracks')
|
||||
parser.add_argument('--debug', action='store_true', dest='debug', default=False, help='output more info')
|
||||
|
||||
def handle(self, **options):
|
||||
render_all(options)
|
||||
|
|
133
render.py
133
render.py
|
@ -313,6 +313,9 @@ def compose(clips, target=150, base=1024, voice_over=None):
|
|||
return scene, used
|
||||
|
||||
def get_scene_duration(scene):
|
||||
if isinstance(scene, str):
|
||||
with open(scene) as fd:
|
||||
scene = json.load(fd)
|
||||
duration = 0
|
||||
for key, value in scene.items():
|
||||
for name, clips in value.items():
|
||||
|
@ -325,8 +328,6 @@ def get_offset_duration(prefix):
|
|||
for root, folders, files in os.walk(prefix):
|
||||
for f in files:
|
||||
if f == 'scene.json':
|
||||
path = os.path.join(root, f)
|
||||
scene = json.load(open(path))
|
||||
duration += get_scene_duration(scene)
|
||||
return duration
|
||||
|
||||
|
@ -414,6 +415,8 @@ def get_fragments(clips, voice_over, prefix):
|
|||
return fragments
|
||||
|
||||
|
||||
def render_timeline(options):
|
||||
|
||||
def render_all(options):
|
||||
prefix = options['prefix']
|
||||
duration = int(options['duration'])
|
||||
|
@ -472,7 +475,7 @@ def render_all(options):
|
|||
scene_json = json.dumps(scene, indent=2, ensure_ascii=False)
|
||||
write_if_new(os.path.join(fragment_prefix, 'scene.json'), scene_json)
|
||||
|
||||
if not options['no_video']:
|
||||
if not options['no_video'] and not options["single_file"]:
|
||||
for timeline in timelines:
|
||||
print(timeline)
|
||||
ext = '.mp4'
|
||||
|
@ -502,8 +505,8 @@ def render_all(options):
|
|||
subprocess.call(cmd)
|
||||
os.unlink(timeline.replace('.kdenlive', ext))
|
||||
|
||||
fragment_prefix = Path(fragment_prefix)
|
||||
cmds = []
|
||||
fragment_prefix = Path(fragment_prefix)
|
||||
for src, out1, out2 in (
|
||||
("audio-front.wav", "fl.wav", "fr.wav"),
|
||||
("audio-center.wav", "fc.wav", "lfe.wav"),
|
||||
|
@ -547,7 +550,8 @@ def render_all(options):
|
|||
fragment_prefix / "back-audio.mp4",
|
||||
])
|
||||
for cmd in cmds:
|
||||
#print(" ".join([str(x) for x in cmd]))
|
||||
if options["debug"]:
|
||||
print(" ".join([str(x) for x in cmd]))
|
||||
subprocess.call(cmd)
|
||||
|
||||
for a, b in (
|
||||
|
@ -562,6 +566,10 @@ def render_all(options):
|
|||
sys.exit(-1)
|
||||
shutil.move(fragment_prefix / "back-audio.mp4", fragment_prefix / "back.mp4")
|
||||
shutil.move(fragment_prefix / "front-5.1.mp4", fragment_prefix / "front.mp4")
|
||||
if options["keep_audio"]:
|
||||
shutil.move(fragment_prefix / "audio-center.wav", fragment_prefix / "vocals.wav")
|
||||
shutil.move(fragment_prefix / "audio-front.wav", fragment_prefix / "foley.wav")
|
||||
shutil.move(fragment_prefix / "audio-back.wav", fragment_prefix / "original.wav")
|
||||
for fn in (
|
||||
"audio-5.1.mp4",
|
||||
"audio-center.wav", "audio-rear.wav",
|
||||
|
@ -572,6 +580,109 @@ def render_all(options):
|
|||
if os.path.exists(fn):
|
||||
os.unlink(fn)
|
||||
|
||||
if options["single_file"]:
|
||||
cmds = []
|
||||
base_prefix = Path(base_prefix)
|
||||
for timeline in (
|
||||
"front",
|
||||
"back",
|
||||
"audio-back",
|
||||
"audio-center",
|
||||
"audio-front",
|
||||
"audio-rear",
|
||||
):
|
||||
timelines = list(sorted(glob('%s/*/%s.kdenlive' % (base_prefix, timeline))))
|
||||
ext = '.mp4'
|
||||
if '/audio' in timelines[0]:
|
||||
ext = '.wav'
|
||||
out = base_prefix / (timeline + ext)
|
||||
cmd = [
|
||||
'xvfb-run', '-a',
|
||||
'melt'
|
||||
] + timelines + [
|
||||
'-quiet',
|
||||
'-consumer', 'avformat:%s' % out,
|
||||
]
|
||||
if ext == '.wav':
|
||||
cmd += ['vn=1']
|
||||
else:
|
||||
cmd += ['an=1']
|
||||
cmd += ['vcodec=libx264', 'x264opts=keyint=1', 'crf=15']
|
||||
cmds.append(cmd)
|
||||
for src, out1, out2 in (
|
||||
("audio-front.wav", "fl.wav", "fr.wav"),
|
||||
("audio-center.wav", "fc.wav", "lfe.wav"),
|
||||
("audio-rear.wav", "bl.wav", "br.wav"),
|
||||
):
|
||||
cmds.append([
|
||||
"ffmpeg", "-y",
|
||||
"-nostats", "-loglevel", "error",
|
||||
"-i", base_prefix / src,
|
||||
"-filter_complex",
|
||||
"[0:0]pan=1|c0=c0[left]; [0:0]pan=1|c0=c1[right]",
|
||||
"-map", "[left]", base_prefix / out1,
|
||||
"-map", "[right]", base_prefix / out2,
|
||||
])
|
||||
cmds.append([
|
||||
"ffmpeg", "-y",
|
||||
"-nostats", "-loglevel", "error",
|
||||
"-i", base_prefix / "fl.wav",
|
||||
"-i", base_prefix / "fr.wav",
|
||||
"-i", base_prefix / "fc.wav",
|
||||
"-i", base_prefix / "lfe.wav",
|
||||
"-i", base_prefix / "bl.wav",
|
||||
"-i", base_prefix / "br.wav",
|
||||
"-filter_complex", "[0:a][1:a][2:a][3:a][4:a][5:a]amerge=inputs=6[a]",
|
||||
"-map", "[a]", "-c:a", "aac", base_prefix / "audio-5.1.mp4"
|
||||
])
|
||||
cmds.append([
|
||||
"ffmpeg", "-y",
|
||||
"-nostats", "-loglevel", "error",
|
||||
"-i", base_prefix / "front.mp4",
|
||||
"-i", base_prefix / "audio-5.1.mp4",
|
||||
"-c", "copy",
|
||||
base_prefix / "front-5.1.mp4",
|
||||
])
|
||||
cmds.append([
|
||||
"ffmpeg", "-y",
|
||||
"-nostats", "-loglevel", "error",
|
||||
"-i", base_prefix / "back.mp4",
|
||||
"-i", base_prefix / "audio-back.wav",
|
||||
"-c:v", "copy",
|
||||
base_prefix / "back-audio.mp4",
|
||||
])
|
||||
for cmd in cmds:
|
||||
if options["debug"]:
|
||||
print(" ".join([str(x) for x in cmd]))
|
||||
subprocess.call(cmd)
|
||||
|
||||
for a, b in (
|
||||
("back-audio.mp4", "back.mp4"),
|
||||
("front-5.1.mp4", "back.mp4"),
|
||||
):
|
||||
duration_a = ox.avinfo(str(base_prefix / a))['duration']
|
||||
duration_b = ox.avinfo(str(base_prefix / b))['duration']
|
||||
if duration_a != duration_b:
|
||||
print('!!', duration_a, base_prefix / a)
|
||||
print('!!', duration_b, base_prefix / b)
|
||||
sys.exit(-1)
|
||||
shutil.move(base_prefix / "back-audio.mp4", base_prefix / "back.mp4")
|
||||
shutil.move(base_prefix / "front-5.1.mp4", base_prefix / "front.mp4")
|
||||
if options["keep_audio"]:
|
||||
shutil.move(base_prefix / "audio-center.wav", base_prefix / "vocals.wav")
|
||||
shutil.move(base_prefix / "audio-front.wav", base_prefix / "foley.wav")
|
||||
shutil.move(base_prefix / "audio-back.wav", base_prefix / "original.wav")
|
||||
for fn in (
|
||||
"audio-5.1.mp4",
|
||||
"audio-center.wav", "audio-rear.wav",
|
||||
"audio-front.wav", "audio-back.wav", "back-audio.mp4",
|
||||
"fl.wav", "fr.wav", "fc.wav", "lfe.wav", "bl.wav", "br.wav",
|
||||
):
|
||||
fn = base_prefix / fn
|
||||
if os.path.exists(fn):
|
||||
os.unlink(fn)
|
||||
join_subtitles(base_prefix)
|
||||
|
||||
print("Duration - Target: %s Actual: %s" % (target_position, position))
|
||||
print(json.dumps(dict(stats), sort_keys=True, indent=2))
|
||||
with open(_cache, "w") as fd:
|
||||
|
@ -727,3 +838,15 @@ def render_infinity(options):
|
|||
with open(state_f + "~", "w") as fd:
|
||||
json.dump(state, fd, indent=2)
|
||||
shutil.move(state_f + "~", state_f)
|
||||
|
||||
|
||||
def join_subtitles(base_prefix):
|
||||
subtitles = list(sorted(glob('%s/*/front.srt' % base_prefix)))
|
||||
data = []
|
||||
position = 0
|
||||
for srt in subtitles:
|
||||
scene = srt.replace('front.srt', 'scene.json')
|
||||
data += ox.srt.load(srt, offset=position)
|
||||
position += get_scene_duration(scene)
|
||||
with open(base_prefix / 'front.srt', 'wb') as fd:
|
||||
fd.write(ox.srt.encode(data))
|
||||
|
|
Loading…
Reference in a new issue