diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 82632fefcb8..3c4adbd72fa 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -576,8 +576,8 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, See www.imagemagick.org and www.ffmpeg.org for more information. """ - from sage.misc.sage_ostools import have_program - have_convert = have_program('convert') + from sage.features.imagemagick import ImageMagick + have_convert = ImageMagick().is_present() have_ffmpeg = self._have_ffmpeg() if use_ffmpeg or not have_convert: if have_ffmpeg: @@ -598,6 +598,7 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, www.ffmpeg.org, or use 'convert' to produce gifs instead.""" raise OSError(msg) else: + ImageMagick().require() if not savefile: savefile = tmp_filename(ext='.gif') if not savefile.endswith('.gif'): @@ -789,8 +790,8 @@ def _have_ffmpeg(self): sage: a._have_ffmpeg() # random: depends on whether ffmpeg is installed False """ - from sage.misc.sage_ostools import have_program - return have_program('ffmpeg') + from sage.features.ffmpeg import FFmpeg + return FFmpeg().is_present() def ffmpeg(self, savefile=None, show_path=False, output_format=None, ffmpeg_options='', delay=None, iterations=0, pix_fmt='rgb24'): @@ -871,75 +872,75 @@ def ffmpeg(self, savefile=None, show_path=False, output_format=None, sage: a.ffmpeg(output_format='gif',delay=30,iterations=5) # optional -- ffmpeg """ - if not self._have_ffmpeg(): - msg = """Error: ffmpeg does not appear to be installed. Saving an animation to -a movie file in any format other than GIF requires this software, so -please install it and try again.""" - raise OSError(msg) + from sage.features.ffmpeg import FFmpeg + need_msg = ("Error: ffmpeg does not appear to be installed. Saving an animation to " + "a movie file in any format other than GIF requires this software, so " + "please install it and try again.") + FFmpeg().require(need_msg) + + if savefile is None: + if output_format is None: + output_format = '.mpg' + else: + if output_format[0] != '.': + output_format = '.'+output_format + savefile = tmp_filename(ext=output_format) else: - if savefile is None: - if output_format is None: - output_format = '.mpg' + if output_format is None: + suffix = os.path.splitext(savefile)[1] + if len(suffix) > 0: + output_format = suffix else: - if output_format[0] != '.': - output_format = '.'+output_format - savefile = tmp_filename(ext=output_format) + output_format = '.mpg' + if not savefile.endswith(output_format): + savefile += output_format + early_options = '' + if output_format == '.gif': + # We try to set reasonable options for gif output. + # + # Older versions of ffmpeg (before 0.9, summer 2011) + # use the option -loop_output instead of -loop. + # Setting iterations=None is a way of preventing sage + # from adding the -loop option. A separate + # -loop_output option can be added with the + # ffmpeg_options argument. + if iterations is not None: + loop_cmd = '-loop {0} '.format(iterations) else: - if output_format is None: - suffix = os.path.splitext(savefile)[1] - if len(suffix) > 0: - output_format = suffix - else: - output_format = '.mpg' - if not savefile.endswith(output_format): - savefile += output_format - early_options = '' - if output_format == '.gif': - # We try to set reasonable options for gif output. - # - # Older versions of ffmpeg (before 0.9, summer 2011) - # use the option -loop_output instead of -loop. - # Setting iterations=None is a way of preventing sage - # from adding the -loop option. A separate - # -loop_output option can be added with the - # ffmpeg_options argument. - if iterations is not None: - loop_cmd = '-loop {0} '.format(iterations) - else: - loop_cmd = '' - # A pix_fmt value is required for some but not all - # ffmpeg installations. Setting pix_fmt=None will - # prevent sage from adding this option, and it may be - # controlled separately through ffmpeg_options. - if pix_fmt is not None: - pix_fmt_cmd = '-pix_fmt {0} '.format(pix_fmt) - else: - pix_fmt_cmd = '' - ffmpeg_options += ' {0}{1}'.format(pix_fmt_cmd,loop_cmd) - if delay is not None and output_format != '.mpeg' and output_format != '.mpg': - early_options += ' -r %s ' % int(100/delay) - savefile = os.path.abspath(savefile) - pngdir = self.png() - pngs = os.path.join(pngdir, "%08d.png") - # For ffmpeg, it seems that some options, like '-g ... -r - # ...', need to come before the input file names, while - # some options, like '-pix_fmt rgb24', need to come - # afterwards. Hence 'early_options' and 'ffmpeg_options' - cmd = 'cd "%s"; sage-native-execute ffmpeg -y -f image2 %s -i %s %s %s' % (pngdir, early_options, pngs, ffmpeg_options, savefile) - from subprocess import check_call, CalledProcessError, PIPE - try: - if sage.misc.verbose.get_verbose() > 0: - set_stderr = None - else: - set_stderr = PIPE - sage.misc.verbose.verbose("Executing '%s'" % cmd,level=1) - sage.misc.verbose.verbose("\n---- ffmpeg output below ----\n") - check_call(cmd, shell=True, stderr=set_stderr) - if show_path: - print("Animation saved to file %s." % savefile) - except (CalledProcessError, OSError): - print("Error running ffmpeg.") - raise + loop_cmd = '' + # A pix_fmt value is required for some but not all + # ffmpeg installations. Setting pix_fmt=None will + # prevent sage from adding this option, and it may be + # controlled separately through ffmpeg_options. + if pix_fmt is not None: + pix_fmt_cmd = '-pix_fmt {0} '.format(pix_fmt) + else: + pix_fmt_cmd = '' + ffmpeg_options += ' {0}{1}'.format(pix_fmt_cmd,loop_cmd) + if delay is not None and output_format != '.mpeg' and output_format != '.mpg': + early_options += ' -r %s ' % int(100/delay) + savefile = os.path.abspath(savefile) + pngdir = self.png() + pngs = os.path.join(pngdir, "%08d.png") + # For ffmpeg, it seems that some options, like '-g ... -r + # ...', need to come before the input file names, while + # some options, like '-pix_fmt rgb24', need to come + # afterwards. Hence 'early_options' and 'ffmpeg_options' + cmd = 'cd "%s"; sage-native-execute ffmpeg -y -f image2 %s -i %s %s %s' % (pngdir, early_options, pngs, ffmpeg_options, savefile) + from subprocess import check_call, CalledProcessError, PIPE + try: + if sage.misc.verbose.get_verbose() > 0: + set_stderr = None + else: + set_stderr = PIPE + sage.misc.verbose.verbose("Executing '%s'" % cmd,level=1) + sage.misc.verbose.verbose("\n---- ffmpeg output below ----\n") + check_call(cmd, shell=True, stderr=set_stderr) + if show_path: + print("Animation saved to file %s." % savefile) + except (CalledProcessError, OSError): + print("Error running ffmpeg.") + raise def apng(self, savefile=None, show_path=False, delay=20, iterations=0): r"""