2020-10-11 03:29:07 +00:00
|
|
|
import os
|
|
|
|
import platform
|
2020-08-19 07:06:42 +00:00
|
|
|
import shlex
|
2020-08-05 03:18:34 +00:00
|
|
|
import subprocess
|
2021-03-18 18:06:56 +00:00
|
|
|
import sys
|
2020-08-05 03:18:34 +00:00
|
|
|
from pathlib import Path
|
2020-08-19 07:06:42 +00:00
|
|
|
from subprocess import PIPE, STDOUT
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2020-12-25 15:32:45 +00:00
|
|
|
from av1an.logger import log
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-03-18 22:09:37 +00:00
|
|
|
if platform.system() == "Linux":
|
|
|
|
import resource
|
|
|
|
|
2020-08-05 03:18:34 +00:00
|
|
|
|
|
|
|
def vvc_concat(temp: Path, output: Path):
|
|
|
|
"""
|
|
|
|
Concatenates vvc files
|
|
|
|
|
|
|
|
:param temp: the temp directory
|
|
|
|
:param output: the output video
|
|
|
|
:return: None
|
|
|
|
"""
|
2021-04-12 23:23:48 +00:00
|
|
|
encode_files = sorted((temp / "encode").iterdir())
|
2020-08-05 03:18:34 +00:00
|
|
|
bitstreams = [x.as_posix() for x in encode_files]
|
2021-04-12 23:23:48 +00:00
|
|
|
bitstreams = " ".join(bitstreams)
|
|
|
|
cmd = f"vvc_concat {bitstreams} {output.as_posix()}"
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-03-18 22:09:37 +00:00
|
|
|
subprocess.run(cmd, shell=True, check=True)
|
2020-08-05 03:18:34 +00:00
|
|
|
|
|
|
|
|
2020-11-03 21:54:00 +00:00
|
|
|
def concatenate_ffmpeg(temp: Path, output: Path, encoder: str):
|
2020-08-05 03:18:34 +00:00
|
|
|
"""
|
|
|
|
Uses ffmpeg to concatenate encoded segments into the final file
|
|
|
|
|
|
|
|
:param temp: the temp directory
|
|
|
|
:param output: the final output file
|
|
|
|
:param encoder: the encoder
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
log("Concatenating")
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
with open(temp / "concat", "w") as f:
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
encode_files = sorted((temp / "encode").iterdir())
|
|
|
|
f.writelines(
|
|
|
|
f'file {shlex.quote("file:"+str(file.absolute()))}\n'
|
|
|
|
for file in encode_files
|
|
|
|
)
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2020-12-05 16:38:06 +00:00
|
|
|
# Add the audio/subtitles/else file if one was extracted from the input
|
2020-08-05 03:18:34 +00:00
|
|
|
audio_file = temp / "audio.mkv"
|
2021-05-04 00:01:43 +00:00
|
|
|
if audio_file.exists() and audio_file.stat().st_size > 1024:
|
2021-04-12 23:23:48 +00:00
|
|
|
audio = ("-i", audio_file.as_posix(), "-c", "copy", "-map", "1")
|
2020-08-05 03:18:34 +00:00
|
|
|
else:
|
2020-08-12 21:41:00 +00:00
|
|
|
audio = ()
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
if encoder == "x265":
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-02-01 03:07:07 +00:00
|
|
|
cmd = [
|
2021-04-12 23:23:48 +00:00
|
|
|
"ffmpeg",
|
|
|
|
"-y",
|
|
|
|
"-fflags",
|
|
|
|
"+genpts",
|
|
|
|
"-hide_banner",
|
|
|
|
"-loglevel",
|
|
|
|
"error",
|
|
|
|
"-f",
|
|
|
|
"concat",
|
|
|
|
"-safe",
|
|
|
|
"0",
|
|
|
|
"-i",
|
|
|
|
(temp / "concat").as_posix(),
|
|
|
|
*audio,
|
|
|
|
"-c",
|
|
|
|
"copy",
|
|
|
|
"-movflags",
|
|
|
|
"frag_keyframe+empty_moov",
|
|
|
|
"-map",
|
|
|
|
"0",
|
|
|
|
"-f",
|
|
|
|
"mp4",
|
|
|
|
output.as_posix(),
|
2021-02-01 03:07:07 +00:00
|
|
|
]
|
2021-04-12 23:23:48 +00:00
|
|
|
concat = subprocess.run(cmd, stdout=PIPE, stderr=STDOUT, check=True).stdout
|
2020-08-05 03:18:34 +00:00
|
|
|
|
|
|
|
else:
|
2021-02-01 03:07:07 +00:00
|
|
|
cmd = [
|
2021-04-12 23:23:48 +00:00
|
|
|
"ffmpeg",
|
|
|
|
"-y",
|
|
|
|
"-hide_banner",
|
|
|
|
"-loglevel",
|
|
|
|
"error",
|
|
|
|
"-f",
|
|
|
|
"concat",
|
|
|
|
"-safe",
|
|
|
|
"0",
|
|
|
|
"-i",
|
|
|
|
(temp / "concat").as_posix(),
|
|
|
|
*audio,
|
|
|
|
"-c",
|
|
|
|
"copy",
|
|
|
|
"-map",
|
|
|
|
"0",
|
|
|
|
output.as_posix(),
|
2021-02-01 03:07:07 +00:00
|
|
|
]
|
2020-08-05 03:18:34 +00:00
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
concat = subprocess.run(cmd, stdout=PIPE, stderr=STDOUT, check=True).stdout
|
2020-08-05 03:18:34 +00:00
|
|
|
|
|
|
|
if len(concat) > 0:
|
|
|
|
log(concat.decode())
|
|
|
|
print(concat.decode())
|
2021-03-18 18:06:56 +00:00
|
|
|
tb = sys.exc_info()[2]
|
|
|
|
raise RuntimeError.with_traceback(tb)
|
2020-08-13 21:09:58 +00:00
|
|
|
|
2020-08-15 13:31:01 +00:00
|
|
|
|
2020-08-13 21:09:58 +00:00
|
|
|
def concatenate_mkvmerge(temp: Path, output):
|
|
|
|
"""
|
|
|
|
Uses mkvmerge to concatenate encoded segments into the final file
|
|
|
|
|
|
|
|
:param temp: the temp directory
|
|
|
|
:param output: the final output file
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
log("Concatenating")
|
2020-08-13 21:09:58 +00:00
|
|
|
|
2020-10-11 03:29:07 +00:00
|
|
|
output = shlex.quote(output.as_posix())
|
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
encode_files = sorted(
|
|
|
|
(temp / "encode").iterdir(),
|
|
|
|
key=lambda x: int(x.stem) if x.stem.isdigit() else x.stem,
|
|
|
|
)
|
2020-10-10 13:23:48 +00:00
|
|
|
encode_files = [shlex.quote(f.as_posix()) for f in encode_files]
|
2020-08-13 21:09:58 +00:00
|
|
|
|
2020-10-11 03:29:07 +00:00
|
|
|
if platform.system() == "Linux":
|
|
|
|
file_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
|
2021-04-12 23:23:48 +00:00
|
|
|
cmd_limit = os.sysconf(os.sysconf_names["SC_ARG_MAX"])
|
2020-10-11 03:29:07 +00:00
|
|
|
else:
|
|
|
|
file_limit = -1
|
|
|
|
cmd_limit = 32767
|
|
|
|
|
2020-08-13 21:09:58 +00:00
|
|
|
audio_file = temp / "audio.mkv"
|
2021-04-12 23:23:48 +00:00
|
|
|
audio = audio_file.as_posix() if audio_file.exists() else ""
|
2020-08-13 21:09:58 +00:00
|
|
|
|
2020-10-10 13:23:48 +00:00
|
|
|
if len(encode_files) > 1:
|
2020-10-11 03:29:07 +00:00
|
|
|
encode_files = [
|
|
|
|
_concatenate_mkvmerge(encode_files, output, file_limit, cmd_limit)
|
|
|
|
]
|
|
|
|
|
2021-04-12 23:23:48 +00:00
|
|
|
cmd = ["mkvmerge", "-o", output, encode_files[0]]
|
2020-10-10 13:23:48 +00:00
|
|
|
|
|
|
|
if audio:
|
|
|
|
cmd.append(audio)
|
|
|
|
|
|
|
|
concat = subprocess.Popen(cmd, stdout=PIPE, universal_newlines=True)
|
2020-10-11 03:29:07 +00:00
|
|
|
message, _ = concat.communicate()
|
2020-08-13 21:09:58 +00:00
|
|
|
concat.wait()
|
|
|
|
|
|
|
|
if concat.returncode != 0:
|
2020-10-11 03:29:07 +00:00
|
|
|
log(message)
|
|
|
|
print(message)
|
2021-03-18 18:06:56 +00:00
|
|
|
tb = sys.exc_info()[2]
|
|
|
|
raise RuntimeError.with_traceback(tb)
|
2020-10-11 03:29:07 +00:00
|
|
|
|
|
|
|
# remove temporary files used by recursive concat
|
|
|
|
if os.path.exists("{}.tmp0.mkv".format(output)):
|
|
|
|
os.remove("{}.tmp0.mkv".format(output))
|
|
|
|
|
|
|
|
if os.path.exists("{}.tmp1.mkv".format(output)):
|
|
|
|
os.remove("{}.tmp1.mkv".format(output))
|
|
|
|
|
|
|
|
|
|
|
|
def _concatenate_mkvmerge(files, output, file_limit, cmd_limit, flip=False):
|
|
|
|
tmp_out = "{}.tmp{}.mkv".format(output, int(flip))
|
|
|
|
cmd = ["mkvmerge", "-o", tmp_out, files[0]]
|
|
|
|
|
|
|
|
remaining = []
|
|
|
|
for i, file in enumerate(files[1:]):
|
2021-04-12 23:23:48 +00:00
|
|
|
new_cmd = cmd + ["+{}".format(file)]
|
|
|
|
if sum(len(s) for s in new_cmd) < cmd_limit and (
|
|
|
|
file_limit == -1 or i < max(1, file_limit - 10)
|
|
|
|
):
|
2020-10-11 03:29:07 +00:00
|
|
|
cmd = new_cmd
|
|
|
|
else:
|
2021-04-12 23:23:48 +00:00
|
|
|
remaining = files[i + 1 :]
|
2020-10-11 03:29:07 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
concat = subprocess.Popen(cmd, stdout=PIPE, universal_newlines=True)
|
|
|
|
message, _ = concat.communicate()
|
|
|
|
concat.wait()
|
|
|
|
|
|
|
|
if concat.returncode != 0:
|
|
|
|
log(message)
|
|
|
|
print(message)
|
2021-03-18 18:06:56 +00:00
|
|
|
tb = sys.exc_info()[2]
|
|
|
|
raise RuntimeError.with_traceback(tb)
|
2020-10-11 03:29:07 +00:00
|
|
|
|
|
|
|
if len(remaining) > 0:
|
2021-04-12 23:23:48 +00:00
|
|
|
return _concatenate_mkvmerge(
|
|
|
|
[tmp_out] + remaining, output, file_limit, cmd_limit, not flip
|
|
|
|
)
|
2021-03-18 22:09:37 +00:00
|
|
|
return tmp_out
|