Added 'blender-video-chunks' job type
Requires that the file is configured for rendering to Matroska video files. Audio is only extracted when there is an audio codec configured. This is a bit arbitrary, but it's at least a way to tell whether the artist is considering that there is audio of any relevance in the current blend file.
This commit is contained in:
parent
4f32b49ad3
commit
4fd4ad7448
@ -1,7 +1,7 @@
|
|||||||
# Blender Cloud changelog
|
# Blender Cloud changelog
|
||||||
|
|
||||||
|
|
||||||
## Version 1.9.5 (in development)
|
## Version 2.0 (in development)
|
||||||
|
|
||||||
- Requires Blender-Asset-Tracer 0.7 or newer.
|
- Requires Blender-Asset-Tracer 0.7 or newer.
|
||||||
- Fix crashing Blender when running in background mode (e.g. without GUI).
|
- Fix crashing Blender when running in background mode (e.g. without GUI).
|
||||||
@ -13,6 +13,13 @@
|
|||||||
- Flamenco: Allow BAT-packing of only those assets that are referred to by relative path (e.g.
|
- Flamenco: Allow BAT-packing of only those assets that are referred to by relative path (e.g.
|
||||||
a path starting with `//`). Assets with an absolute path are ignored, and assumed to be reachable
|
a path starting with `//`). Assets with an absolute path are ignored, and assumed to be reachable
|
||||||
at the same path by the Workers.
|
at the same path by the Workers.
|
||||||
|
- Flamenco: Added 'blender-video-chunks' job type, meant for rendering the edit of a film from the
|
||||||
|
VSE. This job type requires that the file is configured for rendering to Matroska video
|
||||||
|
files.
|
||||||
|
|
||||||
|
Audio is only extracted when there is an audio codec configured. This is a bit arbitrary, but it's
|
||||||
|
at least a way to tell whether the artist is considering that there is audio of any relevance in
|
||||||
|
the current blend file.
|
||||||
|
|
||||||
|
|
||||||
## Version 1.9.4 (2018-11-01)
|
## Version 1.9.4 (2018-11-01)
|
||||||
|
@ -56,6 +56,19 @@ flamenco_is_active = False
|
|||||||
# 'image' file formats that actually produce a video.
|
# 'image' file formats that actually produce a video.
|
||||||
VIDEO_FILE_FORMATS = {'FFMPEG', 'AVI_RAW', 'AVI_JPEG'}
|
VIDEO_FILE_FORMATS = {'FFMPEG', 'AVI_RAW', 'AVI_JPEG'}
|
||||||
|
|
||||||
|
# Video container name (from bpy.context.scene.render.ffmpeg.format) to file
|
||||||
|
# extension mapping. Any container name not listed here will be converted to
|
||||||
|
# lower case and prepended with a period. This is basically copied from
|
||||||
|
# Blender's source, get_file_extensions() in writeffmpeg.c.
|
||||||
|
VIDEO_CONTAINER_TO_EXTENSION = {
|
||||||
|
'QUICKTIME': '.mov',
|
||||||
|
'MPEG1': '.mpg',
|
||||||
|
'MPEG2': '.dvd',
|
||||||
|
'MPEG4': '.mp4',
|
||||||
|
'OGG': '.ogv',
|
||||||
|
'FLASH': '.flv',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pyside_cache('manager')
|
@pyside_cache('manager')
|
||||||
def available_managers(self, context):
|
def available_managers(self, context):
|
||||||
@ -172,6 +185,18 @@ class FLAMENCO_OT_fmanagers(async_loop.AsyncModalOperatorMixin,
|
|||||||
super().quit()
|
super().quit()
|
||||||
|
|
||||||
|
|
||||||
|
def guess_output_file_extension(output_format: str, scene) -> str:
|
||||||
|
"""Return extension, including period, like '.png' or '.mkv'."""
|
||||||
|
if output_format not in VIDEO_FILE_FORMATS:
|
||||||
|
return scene.render.file_extension
|
||||||
|
|
||||||
|
container = scene.render.ffmpeg.format
|
||||||
|
try:
|
||||||
|
return VIDEO_CONTAINER_TO_EXTENSION[container]
|
||||||
|
except KeyError:
|
||||||
|
return '.' + container.lower()
|
||||||
|
|
||||||
|
|
||||||
class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
||||||
pillar.AuthenticatedPillarOperatorMixin,
|
pillar.AuthenticatedPillarOperatorMixin,
|
||||||
FlamencoPollMixin,
|
FlamencoPollMixin,
|
||||||
@ -215,11 +240,6 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
|||||||
return
|
return
|
||||||
self.log.info('Will output render files to %s', render_output)
|
self.log.info('Will output render files to %s', render_output)
|
||||||
|
|
||||||
# BAT-pack the files to the destination directory.
|
|
||||||
outdir, outfile, missing_sources = await self.bat_pack(filepath)
|
|
||||||
if not outfile:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Fetch Manager for doing path replacement.
|
# Fetch Manager for doing path replacement.
|
||||||
self.log.info('Going to fetch manager %s', self.user_id)
|
self.log.info('Going to fetch manager %s', self.user_id)
|
||||||
prefs = preferences()
|
prefs = preferences()
|
||||||
@ -233,18 +253,17 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
|||||||
self.quit()
|
self.quit()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create the job at Flamenco Server.
|
# Construct as much of the job settings as we can before BAT-packing.
|
||||||
context.window_manager.flamenco_status = 'COMMUNICATING'
|
# Validation should happen as soon as possible (BAT-packing can take minutes).
|
||||||
|
|
||||||
frame_range = scene.flamenco_render_frame_range.strip() or scene_frame_range(context)
|
frame_range = scene.flamenco_render_frame_range.strip() or scene_frame_range(context)
|
||||||
settings = {'blender_cmd': '{blender}',
|
settings = {'blender_cmd': '{blender}',
|
||||||
'chunk_size': scene.flamenco_render_fchunk_size,
|
'chunk_size': scene.flamenco_render_fchunk_size,
|
||||||
'filepath': manager.replace_path(outfile),
|
|
||||||
'frames': frame_range,
|
'frames': frame_range,
|
||||||
'render_output': manager.replace_path(render_output),
|
'render_output': manager.replace_path(render_output),
|
||||||
|
|
||||||
# Used for FFmpeg combining output frames into a video.
|
# Used for FFmpeg combining output frames into a video.
|
||||||
'fps': scene.render.fps / scene.render.fps_base,
|
'fps': scene.render.fps / scene.render.fps_base,
|
||||||
|
'extract_audio': scene.render.ffmpeg.audio_codec != 'NONE',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add extra settings specific to the job type
|
# Add extra settings specific to the job type
|
||||||
@ -264,14 +283,9 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
|||||||
# Let Flamenco Server know whether we'll output images or video.
|
# Let Flamenco Server know whether we'll output images or video.
|
||||||
output_format = settings.get('format') or scene.render.image_settings.file_format
|
output_format = settings.get('format') or scene.render.image_settings.file_format
|
||||||
if output_format in VIDEO_FILE_FORMATS:
|
if output_format in VIDEO_FILE_FORMATS:
|
||||||
# Currently we don't do any postprocessing for video, so we don't
|
|
||||||
# bother figuring out the final output filename.
|
|
||||||
settings['images_or_video'] = 'video'
|
settings['images_or_video'] = 'video'
|
||||||
else:
|
else:
|
||||||
settings['images_or_video'] = 'images'
|
settings['images_or_video'] = 'images'
|
||||||
# The file extension is necessary to find the output files and
|
|
||||||
# render them to a video with ffmpeg.
|
|
||||||
settings['output_file_extension'] = scene.render.file_extension # like '.png'
|
|
||||||
|
|
||||||
# Always pass the file format, even though it won't be
|
# Always pass the file format, even though it won't be
|
||||||
# necessary for the actual render command (the blend file
|
# necessary for the actual render command (the blend file
|
||||||
@ -282,6 +296,20 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
|||||||
# Note that this might be overridden above when the job type
|
# Note that this might be overridden above when the job type
|
||||||
# requires a specific file format.
|
# requires a specific file format.
|
||||||
settings.setdefault('format', scene.render.image_settings.file_format)
|
settings.setdefault('format', scene.render.image_settings.file_format)
|
||||||
|
settings['output_file_extension'] = guess_output_file_extension(output_format, scene)
|
||||||
|
|
||||||
|
if not self.validate_job_settings(context, settings):
|
||||||
|
self.quit()
|
||||||
|
return
|
||||||
|
|
||||||
|
# BAT-pack the files to the destination directory.
|
||||||
|
outdir, outfile, missing_sources = await self.bat_pack(filepath)
|
||||||
|
if not outfile:
|
||||||
|
return
|
||||||
|
settings['filepath'] = manager.replace_path(outfile)
|
||||||
|
|
||||||
|
# Create the job at Flamenco Server.
|
||||||
|
context.window_manager.flamenco_status = 'COMMUNICATING'
|
||||||
|
|
||||||
project_id = prefs.project.project
|
project_id = prefs.project.project
|
||||||
try:
|
try:
|
||||||
@ -355,6 +383,26 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
|
|||||||
|
|
||||||
self.quit()
|
self.quit()
|
||||||
|
|
||||||
|
def validate_job_settings(self, context, settings: dict) -> bool:
|
||||||
|
"""Perform settings validations for the selected job type.
|
||||||
|
|
||||||
|
:returns: True if ok, False if there was an error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
job_type = context.scene.flamenco_render_job_type
|
||||||
|
if job_type == 'blender-video-chunks':
|
||||||
|
# This is not really a requirement, but should catch the mistake where it was
|
||||||
|
# left at the default setting (at the moment of writing that's 1 frame per chunk).
|
||||||
|
if context.scene.flamenco_render_fchunk_size < 10:
|
||||||
|
self.report({'ERROR'}, 'Job type requires chunks of at least 10 frames.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if settings['output_file_extension'] != '.mkv':
|
||||||
|
self.report({'ERROR'}, 'Job type requires rendering to Matroska files.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
if bpy.context.window_manager.flamenco_status != 'ABORTED':
|
if bpy.context.window_manager.flamenco_status != 'ABORTED':
|
||||||
bpy.context.window_manager.flamenco_status = 'DONE'
|
bpy.context.window_manager.flamenco_status = 'DONE'
|
||||||
@ -659,10 +707,12 @@ def is_image_type(render_output_type: str) -> bool:
|
|||||||
def _render_output_path(
|
def _render_output_path(
|
||||||
local_project_path: str,
|
local_project_path: str,
|
||||||
blend_filepath: Path,
|
blend_filepath: Path,
|
||||||
|
flamenco_render_job_type: str,
|
||||||
flamenco_job_output_strip_components: int,
|
flamenco_job_output_strip_components: int,
|
||||||
flamenco_job_output_path: str,
|
flamenco_job_output_path: str,
|
||||||
render_image_format: str,
|
render_image_format: str,
|
||||||
flamenco_render_frame_range: str,
|
flamenco_render_frame_range: str,
|
||||||
|
*,
|
||||||
include_rel_path: bool = True,
|
include_rel_path: bool = True,
|
||||||
) -> typing.Optional[PurePath]:
|
) -> typing.Optional[PurePath]:
|
||||||
"""Cached version of render_output_path()
|
"""Cached version of render_output_path()
|
||||||
@ -696,6 +746,9 @@ def _render_output_path(
|
|||||||
if stem.endswith('.flamenco'):
|
if stem.endswith('.flamenco'):
|
||||||
stem = stem[:-9]
|
stem = stem[:-9]
|
||||||
|
|
||||||
|
if flamenco_render_job_type == 'blender-video-chunks':
|
||||||
|
return output_top / ('YYYY_MM_DD_SEQ-%s.mkv' % stem)
|
||||||
|
|
||||||
if include_rel_path:
|
if include_rel_path:
|
||||||
rel_parts = proj_rel.parts[flamenco_job_output_strip_components:]
|
rel_parts = proj_rel.parts[flamenco_job_output_strip_components:]
|
||||||
dir_components = output_top.joinpath(*rel_parts) / stem
|
dir_components = output_top.joinpath(*rel_parts) / stem
|
||||||
@ -733,6 +786,7 @@ def render_output_path(context, filepath: Path = None) -> typing.Optional[PurePa
|
|||||||
return _render_output_path(
|
return _render_output_path(
|
||||||
prefs.cloud_project_local_path,
|
prefs.cloud_project_local_path,
|
||||||
filepath,
|
filepath,
|
||||||
|
scene.flamenco_render_job_type,
|
||||||
prefs.flamenco_job_output_strip_components,
|
prefs.flamenco_job_output_strip_components,
|
||||||
job_output_path,
|
job_output_path,
|
||||||
scene.render.image_settings.file_format,
|
scene.render.image_settings.file_format,
|
||||||
@ -929,6 +983,8 @@ def register():
|
|||||||
('blender-render', 'Simple Render', 'Simple frame-by-frame render'),
|
('blender-render', 'Simple Render', 'Simple frame-by-frame render'),
|
||||||
('blender-render-progressive', 'Progressive Render',
|
('blender-render-progressive', 'Progressive Render',
|
||||||
'Each frame is rendered multiple times with different Cycles sample chunks, then combined'),
|
'Each frame is rendered multiple times with different Cycles sample chunks, then combined'),
|
||||||
|
('blender-video-chunks', 'Video Chunks',
|
||||||
|
'Render each frame chunk to a video file, then concateate those video files')
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user