"I have a simple mission: To create an open-source, non-linear video editor for Linux. Many have tried and fallen before me, but for some reason I feel compelled to try myself. I am documenting my journey in this blog for all to read. It will be a dangerous journey, and I might not make it back alive. Hold on tight, and enjoy the ride! By the way, I'm calling this project OpenShot Video Editor!"

I am revisiting the topic of exporting a frame of video to a PNG image file using Python and Gstreamer. In my previous post, I demonstrated how to use the gst-launch command to accomplish this, but I never could get it to work with just Python. Also, my previous pipeline only worked for MPEG and DV video files.

Before we get into the Python part of this post, let me share an updated pipeline that can be used from the command line. This should work with any valid Gstreamer video file, and export the 1st frame to a PNG:

gst-launch gstfilesrc location=/home/MyUser/Videos/MyMovieFile.MPG ! decodebin ! ffmpegcolorspace ! pngenc ! filesink location=/home/MyUser/Videos/MyExportedFrame.png
Now on to the fun part... the Python version of this same pipeline. There are really two approaches to building a Gstreamer pipeline in Python. The 1st approach is to create each element as a separate object, link the elements together, and then change the state of the pipeline to "PLAYING". This can be very difficult to do correctly (based on the complexity of your pipeline), and due to the fact not all elements can be linked together before the pipeline has been prerolled.

The 2nd approach to build a pipeline is much simpler and has many limitations, but it works great for exporting video frames. Please see the 5 lines of Python code below which does the same thing as my pipeline above.

# This example Python script takes any gstreamer movie format, and exports a PNG image of the
# 1st frame. In other words, it takes a screen grab or screen capture of your video file.
# This example uses an MPEG video, but it should work the same with any video format.

# Import the gstreamer library
import gst

# Define your pipeline, just as you would at the command prompt.
# This is much easier than trying to create and link each gstreamer element in Python.
# This is great for pipelines that end with a filesink (i.e. there is no audible or visual output)
pipe = gst.parse_launch("filesrc location=/home/MyUser/Videos/MyMovieFile.MPG ! decodebin ! ffmpegcolorspace ! pngenc ! filesink location=/home/MyUser/Videos/MyExportedFrame.png")

# Get a reference to the pipeline's bus
bus = pipe.get_bus()

# Set the pipeline's state to PLAYING
pipe.set_state(gst.STATE_PLAYING)

# Listen to the pipeline's bus indefinitely until we receive a EOS (end of stream) message.
# This is a super important step, or the pipeline might not work as expected. For example,
# in my example pipeline above, the pngenc will not export an actual image unless you have
# this line of code. It just exports a 0 byte png file. So... don't forget this step.
bus.poll(gst.MESSAGE_EOS, -1)

I hope these examples are useful to you. It's hard to find good Python and Gstreamer examples, and this might be one of the few good examples of this technique on the Internet (as of today). If there are other examples of this, please let me know, because I haven't found a single one. Good luck!

3 comments

  1. bilboed  

    you should also listen for gst.MESSAGE_ERROR on the bus !

    Else, if you use a file that produces an error in the pipeline... you'll never see that and your script will be waiting there forever.

    Otherwise, you can have a look at the generic thumbnailer we use in PiTiVi (pitivi/thumbnailer.py and pitivi/elements/thumbnailsink.py).

  2. Olivier Aubert  

    You can also have a look at the code I have written for Advene, which exports the playbin _frame property to png, using a gstreamer pipeline. See snapshot() method in http://svn.gna.org/viewcvs/advene/trunk/lib/advene/player/gstreamer.py?view=markup

  3. brandon lewis  

    The strategy I'm using in pitivi right now is similar to the thumbnailer which edward mentioned. Except it produces a cairo surface instead of a pixbuf, it caches multiple thumbnails by timestamp, and it can draw a sequence of thumbnails into a cairo context when asked to do so. The goal is to separate the fetching/caching of thumbnails from the display code, so that the two can happen asynchronously, and also to make sure that drawing a long strip of thumbnails doesn't slow down updates.

Post a Comment

Subscribe to: Post Comments (Atom)