Using the Nchan module to stream images over HTTP.
12. Jun 2019
Thomas Kerpe
2 minute read

Extract from my MJPEG-Nchan-experiment today. (Nchan is no streaming server, this is just for educational purposes :-) )

Nchan is a scalable, flexible pub/sub server for the modern web, built as a module for the Nginx web server. https://nchan.io

Motion JPEG (MJPEG)

In multimedia, Motion JPEG (M-JPEG or MJPEG) is a video compression format in which each video frame or interlaced field of a digital video sequence is compressed separately as a JPEG image. Wikipedia: Motion JPEG

Example MJPEG HTTP session:

HTTP/1.1 200 OK
Server: Nginx/1.10.3
Connection: close
Content-Type: multipart/x-mixed-replace; boundary=--boundary

--boundary
Content-Type: image/jpeg
Content-Length: [size in bytes]

[raw jpeg data]

--boundary
Content-Type: image/jpeg
Content-Length: [size in bytes]

[raw jpeg data]

[…]

Nginx+Nchan Configuration

Requirements: nginx with nchan module - Debian: nginx-extras

client_max_body_size 25M;

server {
    listen 80;
    root /srv/http/nchandemo;
    server_name _;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ /mjpeg_pub/(\w+)$ {
        nchan_publisher;
        nchan_channel_id $1;
        nchan_channel_group mjpeg;
        nchan_message_buffer_length 5;
        nchan_message_timeout 5m;
    }

    location ~ /mjpeg/(\w+)$ {
        add_header Content-Type "multipart/x-mixed-replace; boundary=--boundary";
        nchan_subscriber http-raw-stream;
        nchan_channel_id $1;
        nchan_channel_group mjpeg;
        nchan_subscriber_first_message newest;
    }
}

HTML Document

/srv/http/nchandemo/mjpeg.html

<html>
<h1>MJPEG Stream</h1>
<img src="/mjpeg/img" width="640px" height="480px"/>
</html>

Publish Script

stream_frame.sh

#!/usr/bin/env sh

set -e

: "${PUBLISH_URL:=http://127.0.0.1:80/mjpeg_pub/img}"

image="${1:-}"
[ $# -gt 0 ] && shift

[ ! -s "$image" ] && echo "File not found or empty" && exit 1

(
    printf -- '%s\n' "--boundary" "Content-Type: text/jpeg"
    printf -- 'Content-Length: %s\n\n' "$(stat --printf='%s' $image)"
    cat $image
    printf -- '\n\n'
) | curl --request POST --data-binary @-  "$PUBLISH_URL"

# vim: set ft=sh :

Test it

  • Start the nginx server with the config above.
  • Open the url http://127.0.0.1/mjpeg.html in a webbrowser capable of playing MJPEG (i.e Firefox).
  • Run ./stream_frame.sh <JPEG_FILE.jpg>
  • Repeat…