1

In a Python script I want to capture a webcam stream and save it to a video file on local harddisk, but I want to script to determine lifecycle of the video. For this I am using python-ffmpeg library which is essentially a simple Python SDK around ffmpeg in a subprocess.

This is my snipped currently:

from time import sleep
import ffmpeg


APPLICATION = "ffmpeg.exe"
CAMERA_NAME = "Sandberg USB Webcam Pro"

stream = ffmpeg.input(f"video={CAMERA_NAME}", format="dshow")
stream = ffmpeg.output(stream, "output.mkv", preset="medium", c="copy")

if __name__ == "__main__":
    process = ffmpeg.run_async(stream, cmd=APPLICATION, overwrite_output=True, quiet=True)
    _ = input("Press enter when finished.")
    print("Shutting down in 3 seconds..")
    sleep(3)
    process.terminate()

This works fine. However, the process.terminate() is quite abrupt, since it essentially just kills the subprocess immediately. I've seen that this tends to cut off a few seconds of the video stream. I assume it is being written continuously and when killed whatever is already written remains.

Right now I have no control over the duration (of the.. buffer?). I'm just guessing it's around 3 seconds, hence my sleep call. Is there a way to configure this to ffmpeg?

casparjespersen
  • 3,460
  • 5
  • 38
  • 63
  • How about monitor `-progress` file? You can check out how I implemented it in [my package](https://github.com/python-ffmpegio/python-ffmpegio), [`ffmpegprocess.py`](https://github.com/python-ffmpegio/python-ffmpegio/blob/main/src/ffmpegio/ffmpegprocess.py) to be exact. – kesh Mar 13 '22 at 14:51

1 Answers1

2

Instead of trying to control the buffer size, we have to close FFmpeg gracefully.

For closing FFmpeg gracefully, we may write 'q' to stdin pipe, as described in this post.

When we start recording, the following message appears: Press [q] to stop, [?] for help.
Writing 'q' to stdin simulates pressing the q key.


  • Open FFmpeg sub-process with pipe_stdin=True:

     process = ffmpeg.run_async(stream, cmd=APPLICATION, pipe_stdin=True, overwrite_output=True, quiet=True)
    
  • Write 'q' letter (with encode("GBK")) to stdin pipe, and "communicate()":

     process.stdin.write('q'.encode("GBK"))
     process.communicate()
    
  • Wait for FFmpeg sub-process to end:

     process.wait()
    

Code sample:

from time import sleep
import ffmpeg
import signal

APPLICATION = "ffmpeg.exe"
CAMERA_NAME = "Sandberg USB Webcam Pro"

stream = ffmpeg.input(f"video={CAMERA_NAME}", format="dshow")   
stream = ffmpeg.output(stream, "output.mkv", preset="medium", c="copy")

if __name__ == "__main__":   
    process = ffmpeg.run_async(stream, cmd=APPLICATION, pipe_stdin=True, overwrite_output=True, quiet=True)
    _ = input("Press enter when finished.")
    #sleep(10) # Record 10 seconds for testing
    process.stdin.write('q'.encode("GBK"))
    process.communicate()
    process.wait()

When I used sleep(10) instead of _ = input, the recorded video duration was about 9 seconds.
I think it records 9 seconds and not 10 seconds because it takes FFmpeg about one second to be loaded and start execution.


Setting real-time buffer to 100M, and fflags="nobuffer" had no effect.

rtbufsize="100M":

stream = ffmpeg.input(f"video={CAMERA_NAME}", rtbufsize="100M", format="dshow")

fflags="nobuffer":

stream = ffmpeg.input(f"video={CAMERA_NAME}", fflags="nobuffer", format="dshow")

It looks like FFmpeg flushes the buffers when we write 'q', there is no data loss and the buffer size has no effect.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
Rotem
  • 30,366
  • 4
  • 32
  • 65