diff --git a/README.md b/README.md index f77d73e..ea0ae6a 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ The Arduino ESP32-Camera test sketch only lets you use the stream on your local This collection of scripts consists of: - Arduino code for ESP32 camera module (AI Thinker CAM) `websocket_camera_stream.ino` - - Python code to receive the images via websockets with `receive_stream.py` - - Python code to push the most recent image to a website with `send_image_stream.py` + - Python code to receive and serve the images via websockets with `run_in_memory.py`, using `receive_stream.py`, `send_image_stream.py` and a queue system `queueu.py` # Why is this cool? @@ -22,10 +21,7 @@ Upload the code to you ESP32 AI Thinker Cam board. Please test the Arduino camer 2. Install the missing python requirements using pip: `pip install pillow websockets flask asyncio` -3. Run `python receive_stream.py` -You should get a constant stream of numbers (sizes of images). The image.jpg in the directory is always the latest received image. - -4. Open a second terminal and run `python send_image_stream.py` +3. Run `run_in_memory.py` You should get a response by flask with an IP and port to enter in your browser. Now enjoy your fresh live stream! 📺 @@ -33,6 +29,10 @@ Now enjoy your fresh live stream! 📺 # Known Issues + +### Security +While this should be fine to run on a private (trusted LAN) network, exposing it to the public internet will allow others to receive and interrupt the stream. Only do this is you have sufficient knowledge. + ### Browsers don't like broken images. This is solved using the placeholder.jpg. It just replaces the image, if the backend receives a broken frame to prevent the browser from freezing the stream. diff --git a/python/queueue.py b/python/queueue.py new file mode 100644 index 0000000..fc28af7 --- /dev/null +++ b/python/queueue.py @@ -0,0 +1,3 @@ +from multiprocessing import Queue + +q = Queue(maxsize=100) \ No newline at end of file diff --git a/python/receive_stream.py b/python/receive_stream.py index 814d96a..b72a482 100644 --- a/python/receive_stream.py +++ b/python/receive_stream.py @@ -5,6 +5,10 @@ from PIL import Image, UnidentifiedImageError +from multiprocessing import Queue + +from queueue import q + def is_valid_image(image_bytes): try: Image.open(BytesIO(image_bytes)) @@ -18,19 +22,16 @@ async def handle_connection(websocket, path): while True: try: message = await websocket.recv() - print(len(message)) + #print(len(message)) # This made the IP to connect to go away if len(message) > 5000: if is_valid_image(message): #print(message) - with open("image.jpg", "wb") as f: - f.write(message) + q.put(message) # puts message into queue - print() + #print() except websockets.exceptions.ConnectionClosed: break async def main(): server = await websockets.serve(handle_connection, '0.0.0.0', 3001) await server.wait_closed() - -asyncio.run(main()) diff --git a/python/run_in_memory.py b/python/run_in_memory.py new file mode 100644 index 0000000..3329a08 --- /dev/null +++ b/python/run_in_memory.py @@ -0,0 +1,25 @@ +import asyncio +from multiprocessing import Process, Queue + +import receive_stream, send_image_stream + +from queueue import q + +def receiver(q): + asyncio.run(receive_stream.main()) + +def server(q): + send_image_stream.app.run(host='0.0.0.0', port=5000, debug=False, threaded=True) + +if __name__ == '__main__': + + receiver_process = Process(target=receiver, args=(q,)) + server_process = Process(target=server, args=(q,)) + + # Start the processes + receiver_process.start() + server_process.start() + + # Join the processes + receiver_process.join() + server_process.join() \ No newline at end of file diff --git a/python/send_image_stream.py b/python/send_image_stream.py index 0a1ea48..fc71f2b 100644 --- a/python/send_image_stream.py +++ b/python/send_image_stream.py @@ -6,6 +6,8 @@ from flask import Flask, Response from base64 import b64encode +from queueue import q + app = Flask(__name__) @@ -17,8 +19,7 @@ def index(): def get_image(): while True: try: - with open("image.jpg", "rb") as f: - image_bytes = f.read() + image_bytes = q.get(timeout=10) image = Image.open(BytesIO(image_bytes)) img_io = BytesIO() image.save(img_io, 'JPEG') @@ -41,5 +42,3 @@ def get_image(): yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + img_bytes + b'\r\n') continue - -app.run(host='0.0.0.0', debug=False, threaded=True)