Always define pixfmt for timelapse in video filter chain
Apparently having a chain AND the pix_fmt parameter produces issues with higher resolutions. Should fix #1317
This commit is contained in:
parent
4c971a92db
commit
6bd788a83f
2 changed files with 78 additions and 29 deletions
|
|
@ -809,17 +809,10 @@ class TimelapseRenderJob(object):
|
|||
|
||||
@classmethod
|
||||
def _create_ffmpeg_command_string(cls, ffmpeg, fps, bitrate, threads, input, output, hflip=False, vflip=False,
|
||||
rotate=False, watermark=None):
|
||||
rotate=False, watermark=None, pixfmt="yuv420p"):
|
||||
"""
|
||||
Create ffmpeg command string based on input parameters.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg")
|
||||
'/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob "/path/to/output.mpg"'
|
||||
>>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg", hflip=True)
|
||||
'/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob -vf \\'[in] hflip [out]\\' "/path/to/output.mpg"'
|
||||
|
||||
Arguments:
|
||||
ffmpeg (str): Path to ffmpeg
|
||||
fps (int): Frames per second for output
|
||||
|
|
@ -831,16 +824,19 @@ class TimelapseRenderJob(object):
|
|||
vflip (bool): Perform vertical flip on input material.
|
||||
rotate (bool): Perform 90° CCW rotation on input material.
|
||||
watermark (str): Path to watermark to apply to lower left corner.
|
||||
pixfmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.
|
||||
|
||||
Returns:
|
||||
(str): Prepared command string to render `input` to `output` using ffmpeg.
|
||||
"""
|
||||
|
||||
### See unit tests in test/timelapse/test_timelapse_renderjob.py
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
command = [
|
||||
ffmpeg, '-framerate', str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input), '-vcodec', 'mpeg2video',
|
||||
'-threads', str(threads), '-pix_fmt', 'yuv420p', '-r', "25", '-y', '-b', str(bitrate),
|
||||
'-threads', str(threads), '-r', "25", '-y', '-b', str(bitrate),
|
||||
'-f', 'vob']
|
||||
|
||||
filter_string = cls._create_filter_string(hflip=hflip,
|
||||
|
|
@ -859,38 +855,25 @@ class TimelapseRenderJob(object):
|
|||
return " ".join(command)
|
||||
|
||||
@classmethod
|
||||
def _create_filter_string(cls, hflip=False, vflip=False, rotate=False, watermark=None):
|
||||
def _create_filter_string(cls, hflip=False, vflip=False, rotate=False, watermark=None, pixfmt="yuv420p"):
|
||||
"""
|
||||
Creates an ffmpeg filter string based on input parameters.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> TimelapseRenderJob._create_filter_string()
|
||||
>>> TimelapseRenderJob._create_filter_string(hflip=True)
|
||||
'[in] hflip [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(vflip=True)
|
||||
'[in] vflip [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(rotate=True)
|
||||
'[in] transpose=2 [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(vflip=True, rotate=True)
|
||||
'[in] vflip,transpose=2 [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(vflip=True, hflip=True, rotate=True)
|
||||
'[in] hflip,vflip,transpose=2 [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(watermark="/path/to/watermark.png")
|
||||
'movie=/path/to/watermark.png [wm]; [in][wm] overlay=10:main_h-overlay_h-10 [out]'
|
||||
>>> TimelapseRenderJob._create_filter_string(hflip=True, watermark="/path/to/watermark.png")
|
||||
'[in] hflip [postprocessed]; movie=/path/to/watermark.png [wm]; [postprocessed][wm] overlay=10:main_h-overlay_h-10 [out]'
|
||||
|
||||
Arguments:
|
||||
hflip (bool): Perform horizontal flip on input material.
|
||||
vflip (bool): Perform vertical flip on input material.
|
||||
rotate (bool): Perform 90° CCW rotation on input material.
|
||||
watermark (str): Path to watermark to apply to lower left corner.
|
||||
pixfmt (str): Pixel format to use, defaults to "yuv420p" which should usually fit the bill
|
||||
|
||||
Returns:
|
||||
(str or None): filter string or None if no filters are required
|
||||
"""
|
||||
filters = []
|
||||
|
||||
### See unit tests in test/timelapse/test_timelapse_renderjob.py
|
||||
|
||||
# apply pixel format
|
||||
filters = ["format={}".format(pixfmt)]
|
||||
|
||||
# flip video if configured
|
||||
if hflip:
|
||||
|
|
|
|||
66
tests/timelapse/test_timelapse_renderjob.py
Normal file
66
tests/timelapse/test_timelapse_renderjob.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# coding=utf-8
|
||||
from __future__ import absolute_import
|
||||
|
||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
__copyright__ = "Copyright (C) 2016 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
||||
|
||||
import unittest
|
||||
|
||||
from ddt import ddt, data, unpack
|
||||
|
||||
from octoprint.timelapse import TimelapseRenderJob
|
||||
|
||||
@ddt
|
||||
class TimelapseRenderJobTest(unittest.TestCase):
|
||||
|
||||
@data(
|
||||
(("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg"),
|
||||
dict(),
|
||||
'/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -r 25 -y -b 10000k -f vob -vf \'[in] format=yuv420p [out]\' "/path/to/output.mpg"'),
|
||||
|
||||
(("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg"),
|
||||
dict(hflip=True),
|
||||
'/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -r 25 -y -b 10000k -f vob -vf \'[in] format=yuv420p,hflip [out]\' "/path/to/output.mpg"'),
|
||||
|
||||
(("/path/to/ffmpeg", 25, "20000k", 4, "/path/to/input/files_%d.jpg", "/path/to/output.mpg"),
|
||||
dict(rotate=True, watermark="/path/to/watermark.png"),
|
||||
'/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 4 -r 25 -y -b 20000k -f vob -vf \'[in] format=yuv420p,transpose=2 [postprocessed]; movie=/path/to/watermark.png [wm]; [postprocessed][wm] overlay=10:main_h-overlay_h-10 [out]\' "/path/to/output.mpg"')
|
||||
)
|
||||
@unpack
|
||||
def test_create_ffmpeg_command_string(self, args, kwargs, expected):
|
||||
actual = TimelapseRenderJob._create_ffmpeg_command_string(*args, **kwargs)
|
||||
self.assertEquals(actual, expected)
|
||||
|
||||
@data(
|
||||
(dict(),
|
||||
'[in] format=yuv420p [out]'),
|
||||
|
||||
(dict(pixfmt="test"),
|
||||
'[in] format=test [out]'),
|
||||
|
||||
(dict(hflip=True),
|
||||
'[in] format=yuv420p,hflip [out]'),
|
||||
|
||||
(dict(vflip=True),
|
||||
'[in] format=yuv420p,vflip [out]'),
|
||||
|
||||
(dict(rotate=True),
|
||||
'[in] format=yuv420p,transpose=2 [out]'),
|
||||
|
||||
(dict(vflip=True, rotate=True),
|
||||
'[in] format=yuv420p,vflip,transpose=2 [out]'),
|
||||
|
||||
(dict(vflip=True, hflip=True, rotate=True),
|
||||
'[in] format=yuv420p,hflip,vflip,transpose=2 [out]'),
|
||||
|
||||
(dict(watermark="/path/to/watermark.png"),
|
||||
'[in] format=yuv420p [postprocessed]; movie=/path/to/watermark.png [wm]; [postprocessed][wm] overlay=10:main_h-overlay_h-10 [out]'),
|
||||
|
||||
(dict(hflip=True, watermark="/path/to/watermark.png"),
|
||||
'[in] format=yuv420p,hflip [postprocessed]; movie=/path/to/watermark.png [wm]; [postprocessed][wm] overlay=10:main_h-overlay_h-10 [out]'),
|
||||
|
||||
)
|
||||
@unpack
|
||||
def test_create_filter_string(self, kwargs, expected):
|
||||
actual = TimelapseRenderJob._create_filter_string(**kwargs)
|
||||
self.assertEquals(actual, expected)
|
||||
Loading…
Reference in a new issue