Skip to content

Commit

Permalink
Add mjpg strem example
Browse files Browse the repository at this point in the history
  • Loading branch information
givehug committed Sep 1, 2019
1 parent ded4fb8 commit c014f94
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,49 @@
# picam-mjpeg
# MJPEG stream from your raspberry pi over the internet

#### With a little bit of Python, NodeJS, [ngrok](https://ngrok.com) and powerful [Node wrapper for ngrok](https://github.com/bubenshchykov/ngrok)

I saw a lot of attempts to stream a video from a raspberry pi to your web application running on NodeJS. Most of them come up with non-idiomatic ideas 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 web and use it in your web app.

All of the source code is in `src` dir, assuming you have nodejs installed and camera set up on you rasperry pi.


## Example:
![mjpg raspberry nodejs video](./example.gif)


## Usage:
1. Copy src dir content
2. `npm i`
3. `npm start`


## Explanation:
1. First we'll create an mjpeg stream. I couldn't find proper nodejs solution for this, so we are using python (src/stream.py).
2. Now all we need to do is to connect our js ngrok to the port at which our python stream runs (src/main.js)
```js
const url = await ngrok.connect(9090);
```
Thats it, we have ngrok generated url string with our mjpg stream, use it however you want.


## PS:
You may want to password protect your video:

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

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


## Refs
- [ngrok](https://ngrok.com)
- [ngrok npm](https://github.com/bubenshchykov/ngrok)
- [picamera web streaming](https://picamera.readthedocs.io/en/latest/recipes2.html#web-streaming)
Binary file added example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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);
});
14 changes: 14 additions & 0 deletions src/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"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"
}
}
71 changes: 71 additions & 0 deletions src/stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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()

0 comments on commit c014f94

Please sign in to comment.