Streaming a Gallery of Images as Video

I have many family, friends, and pet pictures on my home server that I wanted to present as a gallery on various devices. All these display devices are connected to the network. However, some devices have a browser, and some have video players. So, I had to figure out how to stream images to browsers and players.

While I was doing my local camera setup, I came across the MJPG video format. It's also called Motion JPEG or MPEG, etc. In simple words, it's just a stream of JPEG image frames. And your browser can receive that stream and display it. Many video players also can receive and display it. It could be a more efficient format, but it is easy to grasp and implement. And it has good support.

Watching Photo stream on Android/VLC
Watching Photo stream on Android/VLC
from flask import Flask, Response, render_template
import os
import time

app = Flask(__name__)

def generate_frames():
    photo_folder = "./photo_frame"
    image_files = [f for f in os.listdir(photo_folder) if f.endswith(".jpg")]

    while True:
        image_files = [f for f in os.listdir(photo_folder) if f.endswith(".jpg")]
        for image_file in image_files:
            image_path = os.path.join(photo_folder, image_file)

            with open(image_path, 'rb') as image_file:
                frame_data = image_file.read()

            yield b'--frame\r\n'
            yield b'Content-Type: image/jpeg\r\n\r\n' + frame_data + b'\r\n'
            time.sleep(0.5)  # Adjust the delay based on your preference


@app.route('/video_feed')
def video_feed():
    return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host="0.0.0.0")

So, I used Flask to write a server that would read a bunch of images from a folder and send them as frames one by one. The loop is a never-ending loop. The server doesn't close the HTTP connection; it keeps it open and sends the images one by one with a frame separator. The image frames are separated by the string "--frame" (line 19). I have used half a second as the delay between frames; you can adjust that frame rate. Look at the mime part of the response, which tells the browser how to display it.

  1. multipart/x-mixed-replace:
  • This is the MIME type that indicates the content type of the response.
  • multipart/x-mixed-replace is a subtype of multipart that allows the client (browser) to receive multiple parts in a single connection, with each part replacing the previous one.
  1. ; boundary=frame:
  • The boundary parameter is used to specify a string that separates the different parts in the multipart response.
  • In this case, the boundary is set to "frame", meaning that each part (each frame of the MJPEG stream) will be separated by the string "--frame".
  • The browser uses this boundary to identify the start and end of each frame in the MJPEG stream.

You can add Gunicorn to the mix to make the server LAN-ready. It is not Internet-ready, especially in terms of security. It works on Firefox, Chrome, and VLC on Desktop and Mobile. You can also use any IP Camera Viewing/Manipulation tools to work with the stream. 

The stream looks better if all the images are of the same size or at least the height and are horizontal or at least the same orientation. 


You can read this blog using RSS Feed. But if you are the person who loves getting emails, then you can join my readers by signing up.

Join 2,242 other subscribers