MediaDash/transcode.py

158 lines
4.4 KiB
Python

import io
import json
import os
import shlex
import subprocess as SP
import sys
import time
import uuid
from tqdm import tqdm
from utils import handle_config
profiles = handle_config().get("transcode_profiles", {})
profiles[None] = {
"command": "-vcodec copy -acodec copy -scodec copy -f null",
"doc": "null output for counting frames",
}
def ffprobe(file):
cmd = [
"ffprobe",
"-v",
"error",
"-print_format",
"json",
"-show_format",
"-show_streams",
file,
]
try:
out = SP.check_output(cmd)
except KeyboardInterrupt:
raise
except BaseException:
return file, None
return file, json.loads(out)
def make_ffmpeg_command_line(infile, outfile, profile=None, **kwargs):
default_opts = ["-v", "error", "-y", "-nostdin"]
ffmpeg = (
"C:\\Users\\Earthnuker\\scoop\\apps\\ffmpeg-nightly\\current\\bin\\ffmpeg.exe"
)
cmdline = profile["command"]
opts = profile.get("defaults", {}).copy()
opts.update(kwargs)
if isinstance(cmdline, str):
cmdline = shlex.split(cmdline)
cmdline = list(cmdline or [])
cmdline += ["-progress", "-", "-nostats"]
ret = [ffmpeg, *default_opts, "-i", infile, *cmdline, outfile]
ret = [v.format(**opts) for v in ret]
return ret
def count_frames(file, **kwargs):
total_frames = None
for state in run_transcode(file, os.devnull, None):
if state.get("progress") == "end":
total_frames = int(state.get("frame", -1))
if total_frames is None:
return total_frames
if total_frames <= 0:
total_frames = None
return total_frames
def run_transcode(file, outfile, profile, job_id=None, **kwargs):
job_id = job_id or str(uuid.uuid4())
stderr_fh = None
if outfile != os.devnull:
stderr_fh = open("{}.log".format(job_id), "w")
proc = SP.Popen(
make_ffmpeg_command_line(file, outfile, profiles[profile], **kwargs),
stdout=SP.PIPE,
stderr=stderr_fh,
encoding="utf8",
)
state = {}
poll = None
while poll is None:
poll = proc.poll()
state["ret"] = poll
if outfile != os.devnull:
with open("{}.log".format(job_id), "r") as tl:
state["stderr"] = tl.read()
line = proc.stdout.readline().strip()
if not line:
continue
try:
key, val = line.split("=", 1)
except ValueError:
print(line)
continue
key = key.strip()
val = val.strip()
state[key] = val
if key == "progress":
yield state
if stderr_fh:
stderr_fh.close()
os.unlink(stderr_fh.name)
yield state
def transcode(file, outfile, profile, job_id=None, **kwargs):
from pprint import pprint
info = ffprobe(file)
frames = count_frames(file)
progbar = tqdm(
desc="Processing {}".format(outfile),
total=frames,
unit=" frames",
disable=False,
leave=False,
)
for state in run_transcode(file, outfile, profile, job_id, **kwargs):
if "frame" in state:
progbar.n = int(state["frame"])
progbar.update(0)
state["total_frames"] = frames
state["file"] = file
state["outfile"] = outfile
# progbar.write(state["stderr"])
yield state
progbar.close()
def preview_command(file, outfile, profile, **kwargs):
return make_ffmpeg_command_line(file, outfile, profiles[profile], **kwargs)
if __name__ == "__main__":
file = sys.argv[1]
for profile in ["H.265 transcode", "H.264 transcode"]:
for preset in ["ultrafast", "fast", "medium", "slow", "veryslow"]:
for crf in list(range(10, 54, 4))[::-1]:
outfile = os.path.join(
"E:\\",
"transcode",
profile,
"{}_{}.mkv".format(
crf,
preset))
os.makedirs(os.path.dirname(outfile), exist_ok=True)
if os.path.isfile(outfile):
print("Skipping", outfile)
continue
for _ in transcode(
file, outfile, profile, "transcode", preset=preset, crf=crf
):
pass