Introduction: Raspberry Camera Mjpg Video Streaming Over the Internet

I saw attempts to stream a video from a raspberry pi to your web application running on NodeJS. Most of the ideas used non standard solutions eg sending base64 image strings over WebSockets to a web client.

This is a simple example of how you can create mjpg stream, expose it to the internet from your local raspberry pi and have access to it from anywhere over the internet or use it in your application UI.

The instruction assumes you have nodejs installed and camera set up on you rasperry pi.

Step 1: Create Local Python Http Server and Capture Video Stream From Your Raspberry

stream.py:

<pre style="color: rgb(0, 0, 0); white-space: pre-wrap;">import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server

PORT = 9090
FRAME_RATE = 24
RESOLUTION = '640x480'

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()
        self.condition = Condition()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # New frame, copy the existing buffer's content and notify all
            # clients it's available
            self.buffer.truncate()
            with self.condition:
                self.frame = self.buffer.getvalue()
                self.condition.notify_all()
            self.buffer.seek(0)
        return self.buffer.write(buf)

class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
            self.end_headers()
            try:
                while True:
                    with output.condition:
                        output.condition.wait()
                        frame = output.frame
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        else:
            self.send_error(404)
            self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

with picamera.PiCamera(resolution=RESOLUTION, framerate=FRAME_RATE) as camera:
    output = StreamingOutput()
    #Uncomment the next line to change your Pi's Camera rotation (in degrees)
    #camera.rotation = 90
    camera.start_recording(output, format='mjpeg')
    try:
        address = ('', PORT)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()

Step 2: Create Config File for Our JS App

package.json:

{
"name": "mjpeg-video-stream", "version": "1.0.0", "description": "mjpeg video stream", "main": "main.js", "scripts": { "start": "node main.js" }, "author": "givehug", "license": "MIT", "dependencies": { "ngrok": "3.2.5" } }

Step 3: Create Main JS File to Run Application

main.js:

const ngrok = require('ngrok');
const {spawn} = require('child_process');
const stream = spawn('python3',  ['./stream.py']);

stream.stdout.once('data', async() => {
	const url = await ngrok.connect(9090);
});

Attachments

Step 4: Run a Program

Thats almost it, run

npm i
npm start

Now you have control over your ngrok generated video stream url here

const url = await ngrok.connect(9090);

use it however you prefer. I usually send it to my NodeJS web server in cloud and then to my web application as src to an image element:

<img src="https://user:pwd@c038f6f8.ngrok.io" />


PS:

- generated url will be different every time, you may want to opt in for prepaid static url by ngrok

- you may want to password protect your video stream, here is an example:

const url = await ngrok.connect({
	addr: 9090,
	auth: 'user:pwd',
});

- if you do not use NodeJS, you can install standalone ngrok executable directly on your raspberry pi (or look for similar python modules). Run your stream.py script and start ngrok at port 9090:

./ngrok http 9090

then follow the url displayed in the terminal window.

References:

- full source code
- ngrok
- ngrok js
- picamera web streaming