Ability to set custom start / end frames (#14)

* Accept start / end frames in job submissions. Start / end frame support for Blender

* Remove old render_all_frames variables and misc cleanup

* Client work - Client determines frame count for FFMPEG and shows frame picker UI
This commit is contained in:
2023-06-11 20:45:16 -05:00
committed by GitHub
parent 94bb1e4362
commit 78a389080c
6 changed files with 102 additions and 49 deletions

View File

@@ -48,7 +48,9 @@ class BaseRenderWorker(Base):
renderer = Column(String)
renderer_version = Column(String)
priority = Column(Integer)
total_frames = Column(Integer)
project_length = Column(Integer)
start_frame = Column(Integer)
end_frame = Column(Integer, nullable=True)
owner = Column(String)
client = Column(String)
name = Column(String)
@@ -86,8 +88,10 @@ class BaseRenderWorker(Base):
self.name = name
# Frame Ranges
self.total_frames = 0
self.current_frame = 0
self.project_length = -1
self.current_frame = 0 # should this be a 1 ?
self.start_frame = 0 # should this be a 1 ?
self.end_frame = None
# Logging
self.start_time = None
@@ -107,6 +111,10 @@ class BaseRenderWorker(Base):
self.is_finished = False
self.last_output = None
@property
def total_frames(self):
return (self.end_frame or self.project_length) - self.start_frame + 1
@property
def status(self):
return self._status
@@ -119,6 +127,7 @@ class BaseRenderWorker(Base):
def status(self):
if self._status in [RenderStatus.RUNNING.value, RenderStatus.NOT_STARTED.value]:
if not hasattr(self, 'errors'):
self._status = RenderStatus.CANCELLED
return RenderStatus.CANCELLED
return string_to_status(self._status)
@@ -129,7 +138,7 @@ class BaseRenderWorker(Base):
def generate_subprocess(self):
# Convert raw args from string if available and catch conflicts
generated_args = self.generate_worker_subprocess()
generated_args = [str(x) for x in self.generate_worker_subprocess()]
generated_args_flags = [x for x in generated_args if x.startswith('-')]
if len(generated_args_flags) != len(set(generated_args_flags)):
msg = "Cannot generate subprocess - Multiple arg conflicts detected"
@@ -175,7 +184,7 @@ class BaseRenderWorker(Base):
return
self.status = RenderStatus.RUNNING
logger.info(f'Starting {self.engine.name()} {self.engine.version()} Render for {self.input_path}')
logger.info(f'Starting {self.engine.name()} {self.engine.version()} Render for {self.input_path} | Frame Count: {self.total_frames}')
self.__thread.start()
def run(self):

View File

@@ -22,17 +22,16 @@ class BlenderRenderWorker(BaseRenderWorker):
self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
self.export_format = self.args.get('export_format', None) or 'JPEG'
self.camera = self.args.get('camera', None)
self.render_all_frames = self.args.get('render_all_frames', False) or \
'-a' in (self.args.get('raw', None) or "").split(' ')
# Stats
self.__frame_percent_complete = 0.0
# Scene Info
self.scene_info = Blender.get_scene_info(input_path)
self.total_frames = (int(self.scene_info.get('frame_end', 1)) - int(self.scene_info.get('frame_start', 1)) + 1) \
if self.render_all_frames else 1
self.current_frame = int(self.scene_info.get('frame_start', 1))
self.start_frame = int(self.scene_info.get('start_frame', 1))
self.end_frame = int(self.scene_info.get('end_frame', self.start_frame))
self.project_length = (self.end_frame - self.start_frame) + 1
self.current_frame = -1
def generate_worker_subprocess(self):
@@ -44,10 +43,12 @@ class BlenderRenderWorker(BaseRenderWorker):
if self.camera:
cmd.extend(['--python-expr', f"import bpy;bpy.context.scene.camera = bpy.data.objects['{self.camera}'];"])
cmd.extend(['-E', self.blender_engine, '-o', self.output_path, '-F', self.export_format])
# add dash at end of given path to separate frame numbers
path_with_ending_dash = os.path.splitext(self.output_path)[0] + "-" + os.path.splitext(self.output_path)[1]
cmd.extend(['-E', self.blender_engine, '-o', path_with_ending_dash, '-F', self.export_format])
# all frames or single
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.current_frame)])
# set frame range
cmd.extend(['-s', self.start_frame, '-e', self.end_frame, '-a'])
# Convert raw args from string if available
raw_args = self.get_raw_args()
@@ -96,10 +97,10 @@ class BlenderRenderWorker(BaseRenderWorker):
match = re.match(r'Time: (.*) \(Saving', line)
if match:
time_completed = match.groups()[0]
if self.render_all_frames:
logger.debug(f'Frame {self.current_frame} completed in {time_completed}')
else:
logger.info(f'Render completed in {time_completed}')
frame_count = self.current_frame - self.end_frame + self.total_frames
logger.info(f'Frame #{self.current_frame} - '
f'{frame_count} of {self.total_frames} completed in {time_completed} | '
f'Total Elapsed Time: {datetime.now() - self.start_time}')
else:
logger.debug(line)
else:
@@ -118,13 +119,14 @@ class BlenderRenderWorker(BaseRenderWorker):
def post_processing(self):
output_dir = os.listdir(os.path.dirname(self.output_path))
if self.render_all_frames and len(output_dir) > 1:
if self.total_frames > 1 and len(output_dir) > 1:
from ..utilities.ffmpeg_helper import image_sequence_to_video
logger.info("Generating preview for image sequence")
# get proper file extension
found_output = next(obj for obj in output_dir if os.path.basename(self.output_path) in obj)
glob_pattern = self.output_path + '%04d' + ('.' + found_output.split('.')[-1] if found_output else "")
path_with_ending_dash = os.path.splitext(self.output_path)[0] + "-"
found_output = next(obj for obj in output_dir if os.path.basename(path_with_ending_dash) in obj)
glob_pattern = path_with_ending_dash + '%04d' + ('.' + found_output.split('.')[-1] if found_output else "")
try:
image_sequence_to_video(source_glob_pattern=glob_pattern,

View File

@@ -17,10 +17,7 @@ class FFMPEGRenderWorker(BaseRenderWorker):
input_path, "-map", "0:v:0", "-c", "copy", "-f", "null", "-y",
"/dev/null"], stderr=subprocess.STDOUT).decode('utf-8')
found_frames = re.findall('frame=\s*(\d+)', stream_info)
self.total_frames = found_frames[-1] if found_frames else '-1'
self.frame = 0
# Stats
self.project_length = found_frames[-1] if found_frames else '-1'
self.current_frame = -1
def generate_worker_subprocess(self):