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 #2851 from pypeclub/bugfix/OP-2860_Nuke-loader---l…
Browse files Browse the repository at this point in the history
…oading-mp4-from-web-publisher-incorrect-duration

WebPublisher: Fix wrong number of frames for video file
  • Loading branch information
kalisp authored Mar 8, 2022
2 parents 5bfbe4e + 5e84f45 commit 2793ac6
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
import os
import clique
import tempfile
import math

from avalon import io
import pyblish.api
from openpype.lib import prepare_template_data
from openpype.lib import prepare_template_data, get_asset, ffprobe_streams
from openpype.lib.vendor_bin_utils import get_fps
from openpype.lib.plugin_tools import (
parse_json,
get_subset_name_with_asset_doc
)


class CollectPublishedFiles(pyblish.api.ContextPlugin):
"""
This collector will try to find json files in provided
Expand Down Expand Up @@ -49,10 +53,7 @@ def process(self, context):
self.log.info("task_sub:: {}".format(task_subfolders))

asset_name = context.data["asset"]
asset_doc = io.find_one({
"type": "asset",
"name": asset_name
})
asset_doc = get_asset()
task_name = context.data["task"]
task_type = context.data["taskType"]
project_name = context.data["project_name"]
Expand Down Expand Up @@ -97,11 +98,26 @@ def process(self, context):
instance.data["frameEnd"] = \
instance.data["representations"][0]["frameEnd"]
else:
instance.data["frameStart"] = 0
instance.data["frameEnd"] = 1
frame_start = asset_doc["data"]["frameStart"]
instance.data["frameStart"] = frame_start
instance.data["frameEnd"] = asset_doc["data"]["frameEnd"]
instance.data["representations"] = self._get_single_repre(
task_dir, task_data["files"], tags
)
file_url = os.path.join(task_dir, task_data["files"][0])
duration = self._get_duration(file_url)
if duration:
try:
frame_end = int(frame_start) + math.ceil(duration)
instance.data["frameEnd"] = math.ceil(frame_end)
self.log.debug("frameEnd:: {}".format(
instance.data["frameEnd"]))
except ValueError:
self.log.warning("Unable to count frames "
"duration {}".format(duration))

instance.data["handleStart"] = asset_doc["data"]["handleStart"]
instance.data["handleEnd"] = asset_doc["data"]["handleEnd"]

self.log.info("instance.data:: {}".format(instance.data))

Expand All @@ -127,7 +143,7 @@ def _get_single_repre(self, task_dir, files, tags):
return [repre_data]

def _process_sequence(self, files, task_dir, tags):
"""Prepare reprentations for sequence of files."""
"""Prepare representation for sequence of files."""
collections, remainder = clique.assemble(files)
assert len(collections) == 1, \
"Too many collections in {}".format(files)
Expand Down Expand Up @@ -188,6 +204,7 @@ def _get_family(self, settings, task_type, is_sequence, extension):
msg = "No family found for combination of " +\
"task_type: {}, is_sequence:{}, extension: {}".format(
task_type, is_sequence, extension)
found_family = "render"
assert found_family, msg

return (found_family,
Expand Down Expand Up @@ -243,3 +260,41 @@ def _get_last_version(self, asset_name, subset_name):
return version[0].get("version") or 0
else:
return 0

def _get_duration(self, file_url):
"""Return duration in frames"""
try:
streams = ffprobe_streams(file_url, self.log)
except Exception as exc:
raise AssertionError((
"FFprobe couldn't read information about input file: \"{}\"."
" Error message: {}"
).format(file_url, str(exc)))

first_video_stream = None
for stream in streams:
if "width" in stream and "height" in stream:
first_video_stream = stream
break

if first_video_stream:
nb_frames = stream.get("nb_frames")
if nb_frames:
try:
return int(nb_frames)
except ValueError:
self.log.warning(
"nb_frames {} not convertible".format(nb_frames))

duration = stream.get("duration")
frame_rate = get_fps(stream.get("r_frame_rate", '0/0'))
self.log.debu("duration:: {} frame_rate:: {}".format(
duration, frame_rate))
try:
return float(duration) * float(frame_rate)
except ValueError:
self.log.warning(
"{} or {} cannot be converted".format(duration,
frame_rate))

self.log.warning("Cannot get number of frames")
20 changes: 20 additions & 0 deletions openpype/lib/vendor_bin_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,23 @@ def is_oiio_supported():
))
return False
return True


def get_fps(str_value):
"""Returns (str) value of fps from ffprobe frame format (120/1)"""
if str_value == "0/0":
print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".")
return "Unknown"

items = str_value.split("/")
if len(items) == 1:
fps = float(items[0])

elif len(items) == 2:
fps = float(items[0]) / float(items[1])

# Check if fps is integer or float number
if int(fps) == fps:
fps = int(fps)

return str(fps)
20 changes: 1 addition & 19 deletions openpype/scripts/otio_burnin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json
import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins
import openpype.lib
from openpype.lib.vendor_bin_utils import get_fps


ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
Expand Down Expand Up @@ -50,25 +51,6 @@ def _get_ffprobe_data(source):
return json.loads(out)


def get_fps(str_value):
if str_value == "0/0":
print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".")
return "Unknown"

items = str_value.split("/")
if len(items) == 1:
fps = float(items[0])

elif len(items) == 2:
fps = float(items[0]) / float(items[1])

# Check if fps is integer or float number
if int(fps) == fps:
fps = int(fps)

return str(fps)


def _prores_codec_args(stream_data, source_ffmpeg_cmd):
output = []

Expand Down

0 comments on commit 2793ac6

Please sign in to comment.