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

WebPublisher: Fix wrong number of frames for video file #2851

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
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 @@ -130,3 +130,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