Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

FFmpeg: Subprocess arguments as list #2032

Merged
merged 17 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion openpype/hosts/harmony/plugins/publish/extract_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def process(self, instance):
thumbnail_path = os.path.join(path, "thumbnail.png")
ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
args = [
"{}".format(ffmpeg_path), "-y",
ffmpeg_path,
"-y",
"-i", os.path.join(path, list(collections[0])[0]),
"-vf", "scale=300:-1",
"-vframes", "1",
Expand Down
6 changes: 4 additions & 2 deletions openpype/hosts/photoshop/plugins/publish/extract_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def process(self, instance):
# Generate thumbnail.
thumbnail_path = os.path.join(staging_dir, "thumbnail.jpg")
args = [
"{}".format(ffmpeg_path), "-y",
ffmpeg_path,
"-y",
"-i", output_image_path,
"-vf", "scale=300:-1",
"-vframes", "1",
Expand All @@ -78,7 +79,8 @@ def process(self, instance):
# Generate mov.
mov_path = os.path.join(staging_dir, "review.mov")
args = [
ffmpeg_path, "-y",
ffmpeg_path,
"-y",
"-i", output_image_path,
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
"-vframes", "1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,14 @@ def process(self, instance):
jpeg_items.append("\"{}\"".format(full_thumbnail_path))

subprocess_jpeg = " ".join(jpeg_items)
subprocess_args = openpype.lib.split_command_to_list(
subprocess_jpeg
)

# run subprocess
self.log.debug("Executing: {}".format(subprocess_jpeg))
self.log.debug("Executing: {}".format(" ".join(subprocess_args)))
openpype.api.run_subprocess(
subprocess_jpeg, shell=True, logger=self.log
subprocess_args, shell=True, logger=self.log
)

# remove thumbnail key from origin repre
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,32 +59,35 @@ def process(self, instance):
if "trimming" not in fml
]

args = [
f"\"{ffmpeg_path}\"",
ffmpeg_args = [
ffmpeg_path,
"-ss", str(start / fps),
"-i", f"\"{video_file_path}\"",
"-i", video_file_path,
"-t", str(dur / fps)
]
if ext in [".mov", ".mp4"]:
args.extend([
ffmpeg_args.extend([
"-crf", "18",
"-pix_fmt", "yuv420p"])
"-pix_fmt", "yuv420p"
])
elif ext in ".wav":
args.extend([
"-vn -acodec pcm_s16le",
"-ar 48000 -ac 2"
ffmpeg_args.extend([
"-vn",
"-acodec", "pcm_s16le",
"-ar", "48000",
"-ac", "2"
])

# add output path
args.append(f"\"{clip_trimed_path}\"")
ffmpeg_args.append(clip_trimed_path)

self.log.info(f"Processing: {args}")
ffmpeg_args = " ".join(args)
joined_args = " ".join(ffmpeg_args)
self.log.info(f"Processing: {joined_args}")
openpype.api.run_subprocess(
ffmpeg_args, shell=True, logger=self.log
)

repr = {
repre = {
"name": ext[1:],
"ext": ext[1:],
"files": os.path.basename(clip_trimed_path),
Expand All @@ -97,10 +100,10 @@ def process(self, instance):
}

if ext in [".mov", ".mp4"]:
repr.update({
repre.update({
"thumbnail": True,
"tags": ["review", "ftrackreview", "delete"]})

instance.data["representations"].append(repr)
instance.data["representations"].append(repre)

self.log.debug(f"Instance data: {pformat(instance.data)}")
5 changes: 5 additions & 0 deletions openpype/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
get_pype_execute_args,
execute,
run_subprocess,
split_command_to_list,
path_to_subprocess_arg,
CREATE_NO_WINDOW
)
from .log import PypeLogger, timeit
Expand Down Expand Up @@ -171,6 +173,9 @@
"get_pype_execute_args",
"execute",
"run_subprocess",
"split_command_to_list",
"path_to_subprocess_arg",
"CREATE_NO_WINDOW",

"env_value_to_bool",
"get_paths_from_environ",
Expand Down
47 changes: 43 additions & 4 deletions openpype/lib/execute.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import logging
import os
import shlex
import subprocess
import platform

from .log import PypeLogger as Logger

log = logging.getLogger(__name__)

# MSDN process creation flag (Windows only)
CREATE_NO_WINDOW = 0x08000000

Expand Down Expand Up @@ -100,7 +99,9 @@ def run_subprocess(*args, **kwargs):
filtered_env = {str(k): str(v) for k, v in env.items()}

# Use lib's logger if was not passed with kwargs.
logger = kwargs.pop("logger", log)
logger = kwargs.pop("logger", None)
if logger is None:
logger = Logger.get_logger("run_subprocess")

# set overrides
kwargs['stdout'] = kwargs.get('stdout', subprocess.PIPE)
Expand Down Expand Up @@ -138,6 +139,44 @@ def run_subprocess(*args, **kwargs):
return full_output


def path_to_subprocess_arg(path):
"""Prepare path for subprocess arguments.

Returned path can be wrapped with quotes or kept as is.
"""
return subprocess.list2cmdline([path])


def split_command_to_list(string_command):
"""Split string subprocess command to list.

Should be able to split complex subprocess command to separated arguments:
`"C:\\ffmpeg folder\\ffmpeg.exe" -i \"D:\\input.mp4\\" \"D:\\output.mp4\"`

Should result into list:
`["C:\ffmpeg folder\ffmpeg.exe", "-i", "D:\input.mp4", "D:\output.mp4"]`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid escape sequence '\i'
invalid escape sequence '\o'


This may be required on few versions of python where subprocess can handle
only list of arguments.

To be able do that is using `shlex` python module.

Args:
string_command(str): Full subprocess command.

Returns:
list: Command separated into individual arguments.
"""
if not string_command:
return []

kwargs = {}
# Use 'posix' argument only on windows
if platform.system().lower() == "windows":
kwargs["posix"] = False
return shlex.split(string_command, **kwargs)


def get_pype_execute_args(*args):
"""Arguments to run pype command.

Expand Down
34 changes: 22 additions & 12 deletions openpype/plugins/publish/extract_jpeg_exr.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import os

import pyblish.api
import openpype.api
import openpype.lib
from openpype.lib import should_decompress, \
get_decompress_dir, decompress
from openpype.lib import (
get_ffmpeg_tool_path,

run_subprocess,
split_command_to_list,
path_to_subprocess_arg,

should_decompress,
get_decompress_dir,
decompress
)
import shutil


Expand Down Expand Up @@ -85,17 +92,19 @@ def process(self, instance):

self.log.info("output {}".format(full_output_path))

ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
ffmpeg_path = get_ffmpeg_tool_path("ffmpeg")
ffmpeg_args = self.ffmpeg_args or {}

jpeg_items = []
jpeg_items.append("\"{}\"".format(ffmpeg_path))
jpeg_items.append(path_to_subprocess_arg(ffmpeg_path))
# override file if already exists
jpeg_items.append("-y")
# use same input args like with mov
jpeg_items.extend(ffmpeg_args.get("input") or [])
# input file
jpeg_items.append("-i \"{}\"".format(full_input_path))
jpeg_items.append("-i {}".format(
path_to_subprocess_arg(full_input_path)
))
# output arguments from presets
jpeg_items.extend(ffmpeg_args.get("output") or [])

Expand All @@ -104,15 +113,16 @@ def process(self, instance):
jpeg_items.append("-vframes 1")

# output file
jpeg_items.append("\"{}\"".format(full_output_path))
jpeg_items.append(path_to_subprocess_arg(full_output_path))

subprocess_jpeg = " ".join(jpeg_items)
subprocess_command = " ".join(jpeg_items)
subprocess_args = split_command_to_list(subprocess_command)

# run subprocess
self.log.debug("{}".format(subprocess_jpeg))
self.log.debug("{}".format(subprocess_command))
try: # temporary until oiiotool is supported cross platform
openpype.api.run_subprocess(
subprocess_jpeg, shell=True, logger=self.log
run_subprocess(
subprocess_args, shell=True, logger=self.log
)
except RuntimeError as exp:
if "Compression" in str(exp):
Expand Down
49 changes: 29 additions & 20 deletions openpype/plugins/publish/extract_otio_audio_tracks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import pyblish
import openpype.api
from openpype.lib import (
get_ffmpeg_tool_path
get_ffmpeg_tool_path,
split_command_to_list,
path_to_subprocess_arg
)
import tempfile
import opentimelineio as otio
Expand Down Expand Up @@ -56,14 +58,17 @@ def process(self, context):
audio_inputs.insert(0, empty)

# create cmd
cmd = '"{}"'.format(self.ffmpeg_path) + " "
cmd = path_to_subprocess_arg(self.ffmpeg_path) + " "
cmd += self.create_cmd(audio_inputs)
cmd += "\"{}\"".format(audio_temp_fpath)
cmd += path_to_subprocess_arg(audio_temp_fpath)

# Split command to list for subprocess
cmd_list = split_command_to_list(cmd)

# run subprocess
self.log.debug("Executing: {}".format(cmd))
openpype.api.run_subprocess(
cmd, logger=self.log
cmd_list, logger=self.log
)

# remove empty
Expand Down Expand Up @@ -99,16 +104,16 @@ def add_audio_to_instances(self, audio_file, instances):
# temp audio file
audio_fpath = self.create_temp_file(name)

cmd = " ".join([
'"{}"'.format(self.ffmpeg_path),
"-ss {}".format(start_sec),
"-t {}".format(duration_sec),
"-i \"{}\"".format(audio_file),
cmd = [
self.ffmpeg_path,
"-ss", str(start_sec),
"-t", str(duration_sec),
"-i", audio_file,
audio_fpath
])
]

# run subprocess
self.log.debug("Executing: {}".format(cmd))
self.log.debug("Executing: {}".format(" ".join(cmd)))
openpype.api.run_subprocess(
cmd, logger=self.log
)
Expand Down Expand Up @@ -220,17 +225,17 @@ def create_empty(self, inputs):
max_duration_sec = max(end_secs)

# create empty cmd
cmd = " ".join([
'"{}"'.format(self.ffmpeg_path),
"-f lavfi",
"-i anullsrc=channel_layout=stereo:sample_rate=48000",
"-t {}".format(max_duration_sec),
"\"{}\"".format(empty_fpath)
])
cmd = [
self.ffmpeg_path,
"-f", "lavfi",
"-i", "anullsrc=channel_layout=stereo:sample_rate=48000",
"-t", str(max_duration_sec),
empty_fpath
]

# generate empty with ffmpeg
# run subprocess
self.log.debug("Executing: {}".format(cmd))
self.log.debug("Executing: {}".format(" ".join(cmd)))

openpype.api.run_subprocess(
cmd, logger=self.log
Expand Down Expand Up @@ -261,10 +266,14 @@ def create_cmd(self, inputs):
for index, input in enumerate(inputs):
input_format = input.copy()
input_format.update({"i": index})
input_format["mediaPath"] = path_to_subprocess_arg(
input_format["mediaPath"]
)

_inputs += (
"-ss {startSec} "
"-t {durationSec} "
"-i \"{mediaPath}\" "
"-i {mediaPath} "
).format(**input_format)

_filters += "[{i}]adelay={delayMilSec}:all=1[r{i}]; ".format(
Expand Down
Loading