Skip to content

Instantly share code, notes, and snippets.

@arvind-iyer
Last active May 15, 2020 05:04
Show Gist options
  • Save arvind-iyer/90cd941d0885f422bdf90905d86f9e04 to your computer and use it in GitHub Desktop.
Save arvind-iyer/90cd941d0885f422bdf90905d86f9e04 to your computer and use it in GitHub Desktop.
Async video stream relay with opencv + websockets
<html>
<head>
<title>Websocket video stream client example</title>
</head>
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<script>
var websocketAddress = "ws://localhost:8765/media/zurich";
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var websocket = new WebSocket(websocketAddress);
websocket.onopen = function () {
console.log("websocket connected");
};
websocket.onclose = function () {
console.log("websocket disconnected");
};
websocket.onmessage = function (evt) {
var image = new Image();
console.log("recd image");
image.onload = function () {
ctx.drawImage(image, 0, 0);
};
evt.data.text().then((data) => {
image.src = "data:image/jpeg;base64," + data;
})
};
websocket.onerror = function (evt) {
console.log('error: ' + evt.data);
websocket.close();
};
</script>
</body>
</html>
"""
Async video streaming server that fetches videos using OpenCV and
serves them over Websockets(default port 8765)
Connect at http://<SERVER_URL>:8765/media/<camera_name> to receive a
video stream as mjpeg format
"""
import websockets
import cv2
import threading
import time
import asyncio
import base64
# prefilled for demo purposes, load these from db for actual uses
STREAMS = {
"corner": "http://145.232.236.102/axis-cgi/mjpg/video.cgi",
"zurich": "http://176.127.42.138/axis-cgi/mjpg/video.cgi"
}
cameras = {camera: Camera(url) for camera, url in STREAMS.items()}
class Camera(threading.Thread):
def __init__(self, url):
threading.Thread.__init__(self)
self.url = url
self.cap = cv2.VideoCapture(url)
ret, self.frame = self.cap.read()
self.last_frame_time = time.time()
print(ret)
def run(self):
print("Started streaming from " + self.url)
self.frame_n = 0
while True:
self.frame_n += 1
ret, frame = self.cap.read()
self.last_frame_time = time.time()
if not ret:
print("error fetching stream")
time.sleep(1)
continue
frame = cv2.resize(frame, (640, 480))
retval, buffer_img = cv2.imencode('.jpg', frame)
self.frame = base64.b64encode(buffer_img)
async def media_server(websocket, path):
# accepted path: /media/<camera>
if not path.startswith("/media/"):
await websocket.send("invalid path")
return
camera = path.replace("/media/", "")
print(f"streaming {camera} to {websocket}")
last_frame_time = 0
if camera in cameras:
stream = cameras[camera]
while True:
# check for new frame
if stream.last_frame_time > last_frame_time:
last_frame_time = stream.last_frame_time
# await websocket.send(f"recd frame {stream.frame_n}")
await websocket.send(stream.frame)
await asyncio.sleep(0.04)
await websocket.send(f"bye bye {camera}")
if __name__ == "__main__":
for cam in cameras.values():
cam.start()
start_server = websockets.serve(media_server, "0.0.0.0", 8765)
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
loop.run_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment