Merge pull request #2851 from pypeclub/bugfix/OP-2860_Nuke-loader---loading-mp4-from-web-publisher-incorrect-duration

WebPublisher: Fix wrong number of frames for video file
This commit is contained in:
Petr Kalis 2022-03-08 16:43:17 +01:00 committed by GitHub
commit 2793ac61a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 27 deletions

View file

@ -10,14 +10,18 @@ Provides:
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
@ -49,10 +53,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
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"]
@ -97,11 +98,26 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
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))
@ -127,7 +143,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
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)
@ -188,6 +204,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
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,
@ -243,3 +260,41 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
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")

View file

@ -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)

View file

@ -6,6 +6,7 @@ import platform
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")
@ -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 = []