4

I have a Python program that generates images at various intervals. I'd like to be able to create a streaming video of each image as it appears. Previously I was using an ffmpeg command like

ffmpg -re -loop 1 -i image.jpg -c:v libx264 -pix_fmt yuv420p out.mp4

I would just have my code overwrite image.jpg whenever it created a new image. However, I'd like to be able to just pipe directly to the ffmpeg command so I don't have to deal with saving the file.

I know I can use something like

ffmpeg -re -y -f image2pipe -vcodec mjpeg -r 24 -i - ...

to take a constant input stream, but I'd like to be able to take one image and loop over it until another one is ready. Is that possible? Just throwing the loop flag into the above doesn't seem to work.

A little more information: I'm running this all on Windows via Python's subprocess command and sending the images in via subprocess.PIPE to stdin. I think my problem has to do with getting the ffmpeg command correct and not with Python, which is why I'm posting it here.

2
  • I need to do exactly the same. Did you found solution? – Bresiu Apr 10 '15 at 11:50
  • @Bresiu, I've posted the code I ended up using. It'll take me a little longer to get the code to call ffmpeg from Python, but I'll find it and let you know when it's up. – Ric Apr 10 '15 at 17:10
3

I found the best solution was to actually just overwrite the image locally. I played around a lot with pipes and processes in Python, but nothing worked as well as just running ffmpeg from the command line and overwriting the image. I ended up doing this on Linux, but it should work on Windows.

ffmpeg command:

ffmpeg -re -loop 1 -i out.jpg -re -f lavfi -i aevalsrc=0 -codec:v libx264 -video_size 1280x720 -f flv -strict experimental rtmp://...

ffmpeg streams to an rtmp URL, but it should be easy to change it to save locally. Just change the -f flag to your desired format and the URL to the file name.

Python code:

import cv2
import os
import threading

class ffmpegHelper(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.image_queue = queue
        self.timer = 1


    def tick(self):
        threading.Timer(self.timer,self.tick).start()
        temp = None
        if not self.image_queue.empty():
            temp = self.image_queue.get()
        try:
            cv2.imwrite("temp.jpg",temp)
            os.rename("temp.jpg","out.jpg")
        except:
            print "Temp didn't get fully written"
            print "Skipping this frame"


    def run(self):
        self.tick()

The Python code's not the cleanest in the world, but it should work. It writes to "temp.jpg" first so ffmpeg doesn't end up reading from the image when Python's writing it.

5
  • I have the code to run ffmpeg from Python, but it's going to take me a little while to find it. I'll update this answer when I get it – Ric Apr 10 '15 at 17:08
  • I ended with something very similar (except written in java), but ffmpeg crashes when trying to open out.jpg. Solutions with renaming can do the job. I will test it. Thank you. – Bresiu Apr 10 '15 at 19:46
  • one last question. Did you encounter 'Could not open file' exception from time to time? It seems that renaming file is not sufficient to avoid this error. ffmpeg crashes after this. – Bresiu Apr 13 '15 at 8:49
  • I do, but it doesn't crash ffmpeg on Linux. It might be a Windows only issue – Ric Apr 13 '15 at 16:06
  • I'm using osx and unfortunately it is crashing. I will stick with image2pipe. Thanks for help. – Bresiu Apr 14 '15 at 7:21
0

when the out.jpg file is opened in the program creating the jpg files, it has to be set to "shared" read/write access, since the 2 processes (ffmpeg and the program creating the file) can both access it simultaneously.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.