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

Commit

Permalink
Merge pull request #2032 from pypeclub/bugfix/ffmpeg_args_on_linux
Browse files Browse the repository at this point in the history
FFmpeg: Subprocess arguments as list
  • Loading branch information
iLLiCiTiT authored Sep 17, 2021
2 parents bf298c7 + 628e9df commit 6167a20
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 101 deletions.
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 @@ -172,6 +174,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"]`
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

0 comments on commit 6167a20

Please sign in to comment.