waveform80 / picamera

A pure Python interface to the Raspberry Pi camera module
https://picamera.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.57k stars 355 forks source link

Can't create various crop for web streaming #598

Closed clavay closed 3 years ago

clavay commented 5 years ago

Hello,

I'm trying to do web streaming and to set http://my-ip/stream.mjpg and http://my-ip/stream2.mjpg which are 2 different crops of the same live record.

I tried to modify the web streaming example but I don't know if it's the good way to achieve it. Do I have to use numpy array or to use capture instead of start_recording ? If someone have an idea...

Thanks, clavay

6by9 commented 4 years ago

You don't say what FOV changes you want, nor what you've tried. Please be very aware that resize is not a free option - it is a software resize running on the GPU's processor.

The simple bit is creating multiple stream outputs, and StreamingOutputs. Use the basis of the web streaming example, but amend the calling code to:

with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
    output = StreamingOutput()
    output2 = StreamingOutput()
    camera.start_recording(output, format='mjpeg')
    camera.start_recording(output, format='mjpeg', splitter_port=2, resize=(320, 240))
    try:
        address = ('', 8000)
        server = StreamingServer(address, StreamingHandler)
        address2 = ('', 8001)
        server2 = StreamingServer(address2, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()

The more complex bit is that you've got to call both server.serve_forever() and server2.serve_forever(), and both block. You therefore need to look at threads, or otherwise waiting until either server has work to do.

clavay commented 3 years ago

I solve this with this code :

import io
import picamera
import picamera.array
import logging
import socketserver
from threading import Condition
from http import server
import cv2
import numpy as np
import time

PAGE="""\
<html>
<head>
<title>picamera MJPEG streaming demo</title>
</head>
<body>
<h1>PiCamera MJPEG Streaming Demo</h1>
<img src="stream1.mjpg"/>
<img src="stream2.mjpg"/>
</body>
</html>
"""

class StreamingOutput(object):
    def __init__(self, im):
        self.frame0 = None
        self.frame1 = None
        self.frame2 = None
        self.buffer = io.BytesIO()
        self.condition = Condition()
        self.im = im

    def write(self, buf):
        self.buffer.truncate()
        with self.condition:
            self.im = np.frombuffer(buf, dtype=np.uint8)
            im0 = self.im.reshape((1200, 1600, 3))[300:1000, 100:1300]
            im1 = self.im.reshape((1200, 1600, 3))[600:900, 850:1300]
            im2 = self.im.reshape((1200, 1600, 3))[450:600, 100:600]
            is_success0, im_buf_arr0 = cv2.imencode(".jpg", cv2.resize(im0, (600, 350)))
            is_success1, im_buf_arr1 = cv2.imencode(".jpg", im1)
            is_success2, im_buf_arr2 = cv2.imencode(".jpg", im2)
            self.frame0 = im_buf_arr0.tobytes()
            self.frame1 = im_buf_arr1.tobytes()
            self.frame2 = im_buf_arr2.tobytes()
            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(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGE.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/stream0.mjpg':
            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()
                        frame0 = output.frame0
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame0))
                    self.end_headers()
                    self.wfile.write(frame0)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        elif self.path == '/stream1.mjpg':
            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()
                        frame1 = output.frame1
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame1))
                    self.end_headers()
                    self.wfile.write(frame1)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        elif self.path == '/stream2.mjpg':
            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()
                        frame2 = output.frame2
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame2))
                    self.end_headers()
                    self.wfile.write(frame2)
                    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='1600x1200', framerate=24) as camera:
    time.sleep(2)
    im = np.empty((1200 * 1600 * 3,), dtype=np.uint8)
    output = StreamingOutput(im)
    camera.start_recording(output, format='bgr')
    try:
        address = ('', 8000)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()