diff --git a/encoder.py b/encoder.py index e35aa6e..98ce667 100644 --- a/encoder.py +++ b/encoder.py @@ -1,26 +1,25 @@ -import datetime # Filename from threading import Thread # Encoder is a thread -import subprocess # System call -import os +import subprocess not_found_msg = """ The ffmpeg command was not found; -ffmpeg is used by this script to make an avi file from a set of pngs. +ffmpeg is used by this script to make a video file from a set of pngs. It is typically not installed by default distros , but it is widely available. +On macOS, try running `brew install ffmpeg`. """ class Encoder(Thread): """Create a video file from images""" - def __init__(self, output_dir): + def __init__(self, input_dir, output_dir): # Initialize the thread Thread.__init__(self) # Set config options - self.output_dir = output_dir + self.input = "{}/*.png".format(input_dir) + self.output = "{}/output.mp4".format(output_dir) - self.checkLibrary() print("Encoder started") def join(self, timeout=None): @@ -28,48 +27,15 @@ def join(self, timeout=None): Thread.join(self) def run(self): - """ Render video """ - - def checkLibrary(self): - try: - subprocess.check_call(['ffmpeg']) - except subprocess.CalledProcessError: - print "ffmpeg command was found" - pass # ffmpeg is found, but returns non-zero exit as expected - # This is a quick and dirty check; it leaves some spurious output - # for the user to puzzle over. - except OSError: - print not_found_msg - - # Now that we have graphed images of the dataset, we will stitch them - # together using Mencoder to create a movie. Each image will become - # a single frame in the movie. - # - # We want to use Python to make what would normally be a command line - # call to Mencoder. Specifically, the command line call we want to - # emulate is (without the initial '#'): - # mencoder mf://*.png -mf type=png:w=800:h=600:fps=25 -ovc lavc -lavcopts vcodec=mpeg4 -oac copy -o output.avi - # See the MPlayer and Mencoder documentation for details. - # - - command = ('mencoder', - 'mf://*.png', - '-mf', - 'type=png:w=800:h=600:fps=25', - '-ovc', - 'lavc', - '-lavcopts', - 'vcodec=mpeg4', - '-oac', - 'copy', - '-o', - 'output.avi') - - # os.spawnvp(os.P_WAIT, 'mencoder', command) + """ + Now that we have graphed images of the dataset, we will stitch them + together using ffmpeg to create a movie. Each image will become + a single frame in the movie. + """ + command = ( + "ffmpeg", "-framerate", "30", "-pattern_type", "glob", "-i", self.input, self.output + ) print "\n\nabout to execute:\n%s\n\n" % ' '.join(command) subprocess.check_call(command) - - print "\n\n The movie was written to 'output.avi'" - - print "\n\n You may want to delete *.png now.\n\n" + print "\n\n The movie was saved to `{}`".format(self.output) diff --git a/timelapse.py b/timelapse.py index 5e72c24..a37fa2b 100644 --- a/timelapse.py +++ b/timelapse.py @@ -1,7 +1,9 @@ -import os # For directory traversing -import time # To create subdirectories +import os +import time +import subprocess from PyObjCTools import AppHelper +from AppKit import * from encoder import Encoder # Creates timelapse video from recorder import Recorder # Takes screenshots @@ -32,6 +34,8 @@ class Timelapse(NSObject): """ Creates a timelapse video """ def applicationDidFinishLaunching_(self, notification): + self.check_dependencies() + # Initialize recording self.recording = start_recording @@ -39,6 +43,8 @@ def applicationDidFinishLaunching_(self, notification): self.recorder_output_basedir = os.path.join(dir_base, dir_pictures, dir_app) self.encoder_output_basedir = os.path.join(dir_base, dir_movies) + self.image_dir = self.create_dir(self.recorder_output_basedir) + # Create a reference to the statusbar (menubar) self.statusbar = NSStatusBar.systemStatusBar() @@ -86,20 +92,20 @@ def startStopRecording_(self, notification): self.recorder.join() # Create timelapse after recording? if encode: - self.encoder = Encoder(self.encoder_output_basedir) + self.encoder = Encoder(self.image_dir, self.encoder_output_basedir) self.encoder.start() else: - output_dir = self.createDir(self.recorder_output_basedir) - self.recorder = Recorder(output_dir, screenshot_interval) + self.recorder = Recorder(self.image_dir, screenshot_interval) self.recorder.start() self.recording = not self.recording self.setStatus() - def createDir(self, base_dir): + @objc.python_method + def create_dir(self, base_dir): """ Creates a specified directory and the path to it if necessary """ if create_session_subdir: # Create a new subdirectory - output_dir = os.path.join(base_dir, self.getSubDir(base_dir)) + output_dir = os.path.join(base_dir, self.get_sub_dir(base_dir)) else: # Don't create a subdirectory. Use base directory for output output_dir = base_dir @@ -107,12 +113,13 @@ def createDir(self, base_dir): try: print(output_dir) os.makedirs(output_dir) - except OSError: - print("oops") + except OSError, e: + print("Error while creating directory:", e) exit() return output_dir - def getSubDir(self, base_dir): + @objc.python_method + def get_sub_dir(self, base_dir): """ Returns the next nonexistend subdirectory to base_dir """ subdir_base = os.path.join(base_dir, subdir_suffix) # Check if we can use subdir without any session id @@ -125,6 +132,17 @@ def getSubDir(self, base_dir): subdir = subdir_base + "-" + str(session_number) return subdir + def check_dependencies(self): + try: + subprocess.check_call(['ffmpeg']) + except subprocess.CalledProcessError: + print "ffmpeg command was found" + pass # ffmpeg is found, but returns non-zero exit as expected + # This is a quick and dirty check; it leaves some spurious output + # for the user to puzzle over. + except OSError: + print not_found_msg + if __name__ == "__main__": app = NSApplication.sharedApplication()