Animating direct to mp4 with img-genner

Two posts ago I started talking about img-genner. Guiding my authorship of the library by using it. Today I made a change that allows for what I think are exciting possibilities.

One of the best uses I've found for this library has been using it to generate animations, surprisingly I've started to take a liking to my own library(oh dear, I've started warming to my own work, whatever will I do with my non-existent objectivity?).

Today, I learned something very useful from the ffmpeg-user mailing list, that it is possible to just concatenate png files and pass them to ffmpeg. This enables an easy method for img-genner to be used to generate animations directly without having to turn the png files into video.

(ql:quickload :img-genner)
(defparameter *ffmpeg-format* "ffmpeg -f png_pipe -i - ~a ~a")
(defparameter *ffmpeg-input* (uiop:launch-program (format nil *ffmpeg-format* "-deadline best" "movie.webm") :input :stream))
(defparameter *image* (img-genner:make-image 640 480))

(defparameter *circles* nil)

(defun reset-image(image)
  ;Taken from the previous post
  (loop for i from 0 below (array-total-size image)
        do(setf (row-major-aref image i) 0)))
(defun bound(pos min-x min-y max-x max-y)
  ;As is this
  (setf (aref pos 0) (if (> (aref pos 0) max-x) min-x (if (< (aref pos 0) min-x) max-x (aref pos 0)))
        (aref pos 1) (if (> (aref pos 1) max-y) min-y (if (< (aref pos 1) min-y) max-y (aref pos 1)))))
(defun tick()
  (reset-image *image*)
  (loop for (i . (dx  dy c)) in *circles*
        do(img-genner:move-by i dx dy)
        do(bound (slot-value i 'img-genner:origin) 0.0 0.0 640.0 480.0)
        do(img-genner:rotate-by i (- (random 1.5) 0.75))
        do(img-genner:fill-shape i *image* (img-genner:static-color-stroker c))))
(loop repeat 20
      do(push
         (cons (img-genner:make-ellipse (random 640.0) (random 640.0) (+ 10.0 (random 10.0)) (+ 10.0 (random 10.0)))
               (list (1- (random 2.0)) (1- (random 2.0)) (img-genner:get-random-color)))
         *circles*))
(loop repeat 1000
      do(tick)
      (img-genner:save-image *image* (uiop:process-info-input *ffmpeg-input*)))

(uiop:close-streams *ffmpeg-input*)
(uiop:wait-process *ffmpeg-input*)

You will notice the return of some code from the last one, but it avoids the usage of hashtables.

The important part is the uiop:launch-program. It creates a process which runs asynchronously, that is, that it doesn't block, and you can obtain the input stream, which is what :input :stream does. Obtaining the stream is done by calling uiop:process-info-input on the ffmpeg-input structure.

The next step will be reading from ffmpeg, but I'll leave that for my next post.

1 Comment

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.