Try to broadcast video to multiple clients using cheroot flask and opencv - python

I am trying to create a video streaming from my Raspberry Pi to multiple clients. Flask does not support WSGI server so i use the cheroot.wsgi Server. i have created a ddns server using noip in order to broadcast the video stream over the internet. Till now i manage to serve the video only to one client even if i use a wsgi server.
Here is the video feeder
from flask import Flask, render_template, Response
from camera import VideoCamera
import RPi.GPIO as gpio
gpio.setmode(gpio.BCM)
gpio.setup(21, gpio.OUT)
app = Flask(__name__)
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed')
def video_feed():
return Response(gen(VideoCamera()),mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/background_process_test')
def background_process_test():
gpio.output(21, gpio.HIGH)
print ("Hello")
return ("nothing")
#app.route('/background_process_test2')
def background_process_test2():
gpio.output(21, gpio.LOW)
print ("Hello")
return ("nothing")
if __name__ == '__main__':
app.run()
here is the wsgi server using cheroot
try:
from cheroot.wsgi import Server as WSGIServer, PathInfoDispatcher
except ImportError:
print("OK")
from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer, WSGIPathInfoDispatcher as PathInfoDispatcher
from main import app
d = PathInfoDispatcher({'/': app})
server = WSGIServer(('0.0.0.0', 5000), d)
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()
The opencv module that captures the camera frames
import cv2
class VideoCamera(object):
def __init__(self):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
self.video = cv2.VideoCapture(0)
self.video.set(cv2.CAP_PROP_FRAME_WIDTH, 160)
self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, 200)
# If you decide to use video.mp4, you must have this file in the folder
# as the main.py.
# self.video = cv2.VideoCapture('video.mp4')
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
finally the web page that serves the video feed
<!doctype html>
<html>
<head>
<!-- <link rel="shortcut icon" href="1.ico" type="image/x-icon" />-->
<title>jindeath</title>
</head>
<body>
hello
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
$(function() {
$('a#test').bind('click', function() {
$.getJSON('/background_process_test',
function(data) {
//do nothing
});
return false;
});
});
$(function() {
$('a#test2').bind('click', function() {
$.getJSON('/background_process_test2',
function(data) {
//do nothing
});
return false;
});
});
</script>
<div class='container'>
<h3>Test</h3>
<form>
<img id="bg" src="{{ url_for('video_feed') }}">
<a href=# id=test><button class='btn btn-default'>Test</button></a>
<a href=# id=test2><button class='btn btn-default'>Test</button></a>
</form>
</body>
</html>
Further more when more than one devices connect to the page my rpi uses 100% of its cpu. any suggestions

Related

Problem when accessing Live Stream multiple times in Flask

I am trying to take a RTSP video input and apply analytics and show it as a live stream. I am using Open CV and PyFlask for this purpose.
I can manage to show the output successfully. However, I am having issues when I try to access the host from multiple devices.
If I access from single device, it's working like a charm but when I try to access from multiple devices at the same time, it's breaking.
RTSP = f'''rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4'''
# importing
import cv2
from flask import Flask, render_template, Response
app= Flask(__name__)
cap= cv2.VideoCapture(RTSP)
def gen_frames():
while True:
success, frame = cap.read()
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield(b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video')
def video():
return Response(gen_frames(), mimetype = 'multipart/x-mixed-replace; boundary=frame')
if __name__ == "__main__":
app.run(debug = False, host = '0.0.0.0', port = 8018)
Need some expert advice. Thanks in advance.
Note - index.html code is given below:
<!DOCTYPE html>
<html>
<body>
<h1>Live streaming</h1>
<div>
<img src="{{ url_for('video') }}"/>
</div>
</body>
</html>

Python Websockets handles one time use only

I want to make a python code that browsers can connect to get a video stream.
Problem is that my python code handles only one time use, meaning if you open the browser it will connect to that websocket correctly, but if you refresh the page, or another client want to access a stream from in parallel it doesn't work
My code:
import asyncio
import websockets
import cv2
import os
import signal
async def time1(websocket, path):
while True:
vid = cv2.VideoCapture('V_DRONE_097.mp4')
vid.set(cv2.CAP_PROP_FPS,24)
try:
#v = VideoStreamWidget()
#v.ops()
while(vid.isOpened()):
img, frame = vid.read()
#frame = cv2.resize(frame, (640, 480))
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 65]
man = cv2.imencode('.jpg', frame, encode_param)[1]
#sender(man)
#print(len(man.tobytes()))
#cv2.imshow('img',man)
await websocket.send(man.tobytes())
except :
pass
async def main():
loop = asyncio.get_running_loop()
stop = loop.create_future()
#loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
port = int(os.environ.get("PORT", "8585"))
global stop1
async with websockets.serve(time1, "", port):
await stop
stop1 = False
'''
async def main():
async with websockets.serve(time1, "localhost", 8585):
await asyncio.Future() # run forever
asyncio.run(main())
'''
if __name__ == "__main__":
while True:
asyncio.run(main())
I want the server to keep working even if the client (browser) refreshes the page or close it and comeback later.
This is the code of javascript running in the browser if that will help:
openSocket = () => {
socket = new WebSocket("ws://127.0.0.1:8585/");
let msg = document.getElementById("msg");
socket.addEventListener('open', (e) => {
document.getElementById("status").innerHTML = "Opened";
});
socket.addEventListener('message', (e) => {
let ctx = msg.getContext("2d");
let image = new Image();
image.src = URL.createObjectURL(e.data);
image.addEventListener("load", (e) => {
ctx.drawImage(image, 0, 0, msg.width, msg.height);
});
});
}
and this is the HTML code:
<!DOCTYPE html>
<html>
<head>
<title>Python_Websocket_Live_Streaming</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="script.js" ></script>
</head>
<body onload="openSocket()">
<div id="status">
Connection failed. Somebody may be using the socket.
</div>
<div style="text-align: center">
<canvas id="msg" width="960" height="720" style="display:inline-block" />
</div>
</body>
</html>
I've searched and tried many solutions but non worked.
My final goal is to integrate this with flask API
Update 1:
yes it is stuck in the time1 function
I've known from wrapping the "send" function in a try catch:
try:
await websocket.send(man.tobytes())
except websockets.exceptions.ConnectionClosedError:
print("clean up")
break
when the browser closes/refresh the page, clean up is printed infinitely
Edit 2
It was stuck in the outer loop, that while true loop. Solved now Thanks
There are a few culprits, but ultimately I think that while(vid.isOpened()) prevents the async from ever being free
edit
or stuck in the outer while loop

How can I send data though socket-io without the client requesting first with python and flask

My goal is for my Flask server to send the client data either every three seconds, or when a function is called. To do this I am using SocketIO. However based on some example code I am working with, it seems that I can only send data after a client requests something. I don't want the client to have to 'poll' to find if there is new data, so I want the server to push it when it is ready.
Here is what I tried so far. (some of the code is unnecessary since it is based off an example) This should use socketio to push the time to the client every few seconds.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
socket.on('my_response', function(msg, cb) {
$('#log').text( $('<div/>').text(msg.data).html());
if (cb)
cb();
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
Python
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
clients = []
def update():
time.sleep(1)
emit('my_response',
{'data': time.time},
room=clients[0])
t=threading.Thread(target=update)
#socket_.on('connect')
def handle_connect():
print('Client connected')
clients.append(request.sid)
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
#socket_.on('my_event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
#socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': time.time},
broadcast=True)
socket_.run(app,port=8050)
I try to run it but it gives me the error RuntimeError: Working outside of request context.
I fixed my code by following this tutorial: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io/
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
def update():
time.sleep(1)
socket_.emit('my_response',
{'data': time.time()},
namespace='/test')
print("emitted")
update()
t=threading.Thread(target=update)
#socket_.on('connect', namespace='/test')
def handle_connect():
print('Client connected')
if not t.isAlive():
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
socket_.run(app,port=8070)
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
console.log(("test"));
socket.on('my_response', function(msg) {
$('#time').text( $('<div/>').text(msg.data).html());
console.log(msg);
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
I would like to point out that using recursion in this case is not the best choice.
you call the update function inside the update and do not have the completion of this process.
the best option would be to use a loop(as done in the link you attached)
def update():
while True:
time.sleep(1)
socket_.emit('my_response', {'data': time.time()}, namespace='/test')
print("emitted")
t=threading.Thread(target=update)
also, it would be better to write "while is_work_var" instead of "while True"

Capture video stream flask

I have created a simple live video stream from the raspberry camera. It looks like this
server.py
from flask import Flask
from flask import render_template
from flask import Response
import cv2
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed')
def video_feed():
return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
def gen():
camera = cv2.VideoCapture(0)
while True:
ret, img = camera.read()
if ret:
frame = cv2.imencode('.jpg', img)[1].tobytes()
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
else:
break
app.run(host='192.168.0.241', port=7070, debug=True)
index.html
<html>
<head>
<title>PiCamera stream</title>
</head>
<body>
<h1>Streaming</h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
Everything works correct, I enter http://<raspberry_ip>:<port> in the browser and I can see the video.
Right now I need to create mobile app for watching this video, however I am struggling how to do it. Is there a way to capture video stream in iOS app?
Your video is streaming on
http://<raspberry_ip>:<port>/video_feed
http://<raspberry_ip>:<port>
Using Ngrok if you want to push your app to public server!
http://ngrok.com/

How to receive a webcam video stream in flask route?

I'm developing an application that has a client(html&js) and a server(flask)
The client will open the Webcam(HTML5 api) -> send a video stream to server -> server will return other streams with a json/text stream
I don't want to do pooling.
I was researching something about video stream, but every article and example o internet that I found, use the webcam by OpenCV or a local video and does get the real-time webcam's video and send to the server.
Here are the principal examples that I found
Server:
from flask import Flask, render_template, Response
import cv2
app = Flask(__name__)
camera = cv2.VideoCapture(0) # I can't use a local webcam video or a local source video, I must receive it by http in some api(flask) route
def gen_frames(): # generate frame by frame from camera
while True:
success, frame = camera.read() # read the camera frame
if not success:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result
#app.route('/video_feed')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0')
Client:
camera.js
//--------------------
// GET USER MEDIA CODE
//--------------------
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
var video;
var webcamStream;
function startWebcam() {
if (navigator.getUserMedia) {
navigator.getUserMedia (
// constraints
{
video: true,
audio: false
},
// successCallback
function(localMediaStream) {
video = document.querySelector('video');
video.src = window.URL.createObjectURL(localMediaStream);
webcamStream = localMediaStream;
},
// errorCallback
function(err) {
console.log("The following error occured: " + err);
}
);
} else {
console.log("getUserMedia not supported");
}
}
//---------------------
// TAKE A SNAPSHOT CODE
//---------------------
var canvas, ctx;
function init() {
// Get the canvas and obtain a context for
// drawing in it
canvas = document.getElementById("myCanvas");
context = canvas.getContext('2d');
}
function snapshot() {
// Draws current image from the video element into the canvas
context.drawImage(video, 0,0, canvas.width, canvas.height);
webcamStream.stop();
var dataURL = canvas.toDataURL('image/jpeg', 1.0);
document.querySelector('#dl-btn').href = dataURL;
$.ajax({
type: "POST",
contentType: false,
cache: false,
processData: false,
async: false,
url: "/upload",
data: {
imgBase64: dataURL
}
}).done(function(o) {
console.log('saved');
// If you want the file to be visible in the browser
// - please modify the callback in javascript. All you
// need is to return the url to the file, you just saved
// and than put the image in your browser.
});
}
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="camera.js"></script>
</head>
<body onload="init();">
<h1>Take a snapshot of the current video stream</h1>
Click on the Start WebCam button.
<p>
<button onclick="startWebcam();">Start WebCam</button>
<button type="submit" id="dl-btn" href="#" download="participant.jpeg" onclick="snapshot();">Take Snapshot</button>
</p>
<video onclick="snapshot(this);" width=400 height=400 id="video" controls autoplay></video>
<p>
Screenshots : <p>
<canvas id="myCanvas" width="400" height="350"></canvas>
</body>
</html>
Another problem is that I can't make pooling with snapshots, I need to send the video stream to the server to work with frames there.
Someone know how can I send the WebCam video stream to flask?
tks
You should probably use stream_with_context around your stream generator to stream your response. You can't return a regular response because there is nothing that tells the client not to close the connection.
https://flask.palletsprojects.com/en/1.1.x/api/#flask.stream_with_context
Hey this might help you video streaming in flask
#!/usr/bin/env python
from flask import Flask, render_template, Response
from camera import Camera
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
#app.route('/video_feed')
def video_feed():
return Response(gen(Camera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

Categories

Resources