bottle - streamen von .jpg

Django, Flask, Bottle, WSGI, CGI…
Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

@lackschuh: woran merkst Du, dass mjpeg nicht verlassen wird? Bei mir tut das einwandfrei.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

@Sirius3

An der Kamera LED bzw die Kamera läuft weiter und bei einem wiederholten Aufruf der /stream url bzw test.mjpg kommt folgende Fehlermeldung:

Code: Alles auswählen

mmal: mmal_vc_component_enable: failed to enable component: ENOSPC
Error: 500 Internal Server Error

Sorry, the requested URL 'http://10.0.2.114:8080/test.mjpg' caused an error:

Unhandled exception
Exception:

PiCameraMMALError(u"Camera component couldn't be enabled: Out of resources (other than memory)",)
Traceback:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/bottle.py", line 923, in _cast
    first = next(iout)
  File "app.py", line 108, in mjpeg
    camera = picamera.PiCamera()
  File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 419, in __init__
    self.STEREO_MODES[stereo_mode], stereo_decimate)
  File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 551, in _init_camera
    prefix="Camera component couldn't be enabled")
  File "/usr/lib/python2.7/dist-packages/picamera/exc.py", line 133, in mmal_check
    raise PiCameraMMALError(status, prefix)
PiCameraMMALError: Camera component couldn't be enabled: Out of resources (other than memory)
Eigentlich schliesst ja das ``with`` Statement automatisch. Nun weiss ich nicht, liegt es am Browser, am gevent-Server oder an der Bottle Funktion...
BlackJack

@lackschuh: Der Traceback passt nicht zum Code. Im Code benutzt Du ``with``, im Traceback ganz offensichtlich nicht.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

@BlackJack

Ups, da hast du recht. Das war aus der falschen Datei kopier... das Problem ist jedoch das selbe

Code: Alles auswählen

@app.route('/test.mjpg')
def mjpeg():
    response.content_type = 'multipart/x-mixed-replace;boundary=%s' % BOUNDARY
    stream = io.BytesIO()
    yield BOUNDARY+'\r\n'
    with picamera.PiCamera() as camera:
        #camera.led = False
        camera.exposure_mode = 'night'
        #camera.exposure_mode ='auto'
        camera.rotation = 180
        camera.resolution = (640, 480)
        camera.start_preview()
        camera.annotate_background = True
        time.sleep(2)
        while True:
            camera.capture(stream,'jpeg')
            camera.annotate_text = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            yield BOUNDARY+'\r\n'
            yield 'Content-Type: image/jpeg\r\nContent-Length: %s\r\n\r\n' % len(stream.getvalue())
            yield stream.getvalue()
            stream.seek(0)
            stream.truncate()
            time.sleep(.1)

Code: Alles auswählen

mmal: mmal_vc_component_enable: failed to enable component: ENOSPC
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 508, in handle_one_response
    self.run_application()
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 495, in run_application
    self.process_result()
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 484, in process_result
    for data in self.result:
  File "app.py", line 111, in mjpeg
    with picamera.PiCamera() as camera:
  File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 419, in __init__
    self.STEREO_MODES[stereo_mode], stereo_decimate)
  File "/usr/lib/python2.7/dist-packages/picamera/camera.py", line 551, in _init_camera
    prefix="Camera component couldn't be enabled")
  File "/usr/lib/python2.7/dist-packages/picamera/exc.py", line 133, in mmal_check
    raise PiCameraMMALError(status, prefix)
PiCameraMMALError: Camera component couldn't be enabled: Out of resources (other than memory)
{'GATEWAY_INTERFACE': 'CGI/1.1',
 'HTTP_ACCEPT': 'image/webp,*/*;q=0.8',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
 'HTTP_ACCEPT_LANGUAGE': 'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4',
 'HTTP_CACHE_CONTROL': 'max-age=0',
 'HTTP_CONNECTION': 'keep-alive',
 'HTTP_COOKIE': 'session_id=1424874671-gryved',
 'HTTP_HOST': '10.0.2.114:8080',
 'HTTP_REFERER': 'http://10.0.2.114:8080/stream',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.4 Safari/537.36',
 'PATH_INFO': '/test.mjpg',
 'QUERY_STRING': '',
 'REMOTE_ADDR': '10.0.2.112',
 'REMOTE_PORT': '56329',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': '10.0.2.114',
 'SERVER_PORT': '8080',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'SERVER_SOFTWARE': 'gevent/1.0 Python/2.7',
 'bottle.app': <bottle.Bottle object at 0xb654cc10>,
 'bottle.raw_path': '/test.mjpg',
 'bottle.request': <LocalRequest: GET http://10.0.2.114:8080/test.mjpg>,
 'bottle.request.urlparts': SplitResult(scheme='http', netloc='10.0.2.114:8080', path='/test.mjpg', query='', fragment=''),
 'bottle.route': <GET '/test.mjpg' <function mjpeg at 0xb645ec70>>,
 'route.handle': <GET '/test.mjpg' <function mjpeg at 0xb645ec70>>,
 'route.url_args': {},
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0xb6d090d0>,
 'wsgi.input': <gevent.pywsgi.Input object at 0xb22a0050>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)} failed with PiCameraMMALError

10.0.2.112 - - [2015-02-25 15:52:29] "GET /test.mjpg HTTP/1.1" 200 168 0.097336
10.0.2.112 - - [2015-02-25 15:52:29] "GET /static/fonts/glyphicons-halflings-regular.woff HTTP/1.1" 304 66 0.007707
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das with schließt die Kamera aber auch nur dann, wenn das with jemals verlassen wird. Du hast da allerdings ein schönes while-True, in welchem keine Abbruchbedingung definiert ist.
Das Leben ist wie ein Tennisball.
BlackJack

@EyDu: Die Frage ist also wie man in dem Generator mitbekommt das niemals mehr etwas davon abgefragt werden wird.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich kenne mich mit den Interna von Bottle nicht besonders aus und weiß nicht, ob für so etwas ein Mechanismus vorgesehen ist, aber im Prinzip sollte doch ein Thread für die Kamera genügen, welcher immer das letzte (oder die letzten k) zeitgestempelten Bilder vorhält. Daraus könnte sich dann die mjpeg-Methode bedienen. Klar es gibt ein paar Randfälle zu beachten, aber zumindest stört es dann nicht weiter, wenn ein Client mal nicht mehr antwortet.
Das Leben ist wie ein Tennisball.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Nach ca. 15min bricht die Verbindung ab - sofern kein Client mehr da ist. Geht das von Bottle oder von gevent aus (laut Traceback eher vom gevent Server):

Code: Alles auswählen

10.0.2.112 - - [2015-02-25 16:33:58] "GET /home HTTP/1.1" 200 6392 0.055557
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 508, in handle_one_response
    self.run_application()
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 495, in run_application
    self.process_result()
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 486, in process_result
    self.write(data)
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 376, in write
    self._write(data)
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 369, in _write
    self._sendall(data)
  File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 355, in _sendall
    self.socket.sendall(data)
  File "/usr/local/lib/python2.7/dist-packages/gevent/socket.py", line 458, in sendall
    data_sent += self.send(_get_memory(data, data_sent), flags)
  File "/usr/local/lib/python2.7/dist-packages/gevent/socket.py", line 443, in send
    return sock.send(data, flags)
error: [Errno 110] Connection timed out
{'GATEWAY_INTERFACE': 'CGI/1.1',
 'HTTP_ACCEPT': 'image/webp,*/*;q=0.8',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
 'HTTP_ACCEPT_LANGUAGE': 'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4',
 'HTTP_CONNECTION': 'keep-alive',
 'HTTP_COOKIE': 'session_id=1424874671-gryved',
 'HTTP_HOST': '10.0.2.114:8080',
 'HTTP_REFERER': 'http://10.0.2.114:8080/stream',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.4 Safari/537.36',
 'PATH_INFO': '/test.mjpg',
 'QUERY_STRING': '',
 'REMOTE_ADDR': '10.0.2.112',
 'REMOTE_PORT': '56821',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': '10.0.2.114',
 'SERVER_PORT': '8080',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'SERVER_SOFTWARE': 'gevent/1.0 Python/2.7',
 'bottle.app': <bottle.Bottle object at 0xb654cc10>,
 'bottle.raw_path': '/test.mjpg',
 'bottle.request': <LocalRequest: GET http://10.0.2.114:8080/test.mjpg>,
 'bottle.request.urlparts': SplitResult(scheme='http', netloc='10.0.2.114:8080', path='/test.mjpg', query='', fragment=''),
 'bottle.route': <GET '/test.mjpg' <function mjpeg at 0xb645ec70>>,
 'route.handle': <GET '/test.mjpg' <function mjpeg at 0xb645ec70>>,
 'route.url_args': {},
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0xb6d090d0>,
 'wsgi.input': <gevent.pywsgi.Input object at 0xb63576b0>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)} failed with error

10.0.2.112 - - [2015-02-25 16:46:09] "GET /test.mjpg HTTP/1.1" socket 74372860 1249.888520
Kann man dieses ``error: [Errno 110] Connection timed out``auch manuell auf zB ca. 1min runtersetzen oder was wäre der einfachste Weg?
BlackJack

Apache hat einen Timeout von 5 Sekunden(!) an dieser Stelle. :-)

Vielleicht hilft es ja wenn Du den Header ``Connection: close`` mitsendest. Wenn der Browser daraufhin die Verbindung abbrechen sollte, dann merkt der Server das schneller.
Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

@lackschuh: Bei meinen Tests mit gunicorn bricht die Methode in weniger als 1 Sekunde ab, wenn das Browserfenster geschlossen wird.

Der Traceback ist fast identisch, der Fehler aber "[Errno 32] Broken pipe" statt "[Errno 110] Connection timed out":

Code: Alles auswählen

Traceback (most recent call last):
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 508, in handle_one_response
    self.run_application()
  File "/../geventwebsocket/handler.py", line 84, in run_application
    return super(WebSocketHandler, self).run_application()
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 495, in run_application
    self.process_result()
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 486, in process_result
    self.write(data)
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 376, in write
    self._write(data)
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 369, in _write
    self._sendall(data)
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/pywsgi.py", line 355, in _sendall
    self.socket.sendall(data)
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/socket.py", line 458, in sendall
    data_sent += self.send(_get_memory(data, data_sent), flags)
  File "/../gevent-1.0.1-py2.7-macosx-10.8-intel.egg/gevent/socket.py", line 435, in send
    return sock.send(data, flags)
error: [Errno 32] Broken pipe
{'GATEWAY_INTERFACE': 'CGI/1.1',
 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, sdch',
 'HTTP_ACCEPT_LANGUAGE': 'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4',
 'HTTP_CONNECTION': 'keep-alive',
 'HTTP_HOST': 'localhost:8081',
 'HTTP_USER_AGENT': 'Mozilla/5.0',
 'PATH_INFO': '/xy',
 'QUERY_STRING': '',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_PORT': '60606',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': '1.0.0.127.in-addr.arpa',
 'SERVER_PORT': '8081',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'SERVER_SOFTWARE': 'gevent/1.0.1 gunicorn/18.0',
 'bottle.app': <bottle.Bottle object at 0x10177ff50>,
 'bottle.raw_path': '/xy',
 'bottle.request': <LocalRequest: GET http://localhost:8081/xy>,
 'bottle.request.urlparts': SplitResult(scheme='http', netloc='localhost:8080', path='/xy', query='', fragment=''),
 'bottle.route': <GET '/xy' <function xy at 0x101935230>>,
 'route.handle': <GET '/xy' <function xy at 0x101935230>>,
 'route.url_args': {},
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0x1010f91e0>,
 'wsgi.input': <gevent.pywsgi.Input object at 0x101948a50>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)} failed with error
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo Sirius3

Ich hab mir gunicorn mal installiert aber der Server hat eine Reaktion wie eine Schlaftablette. Für das Login brauch der schon mindestens 2 min.

Code: Alles auswählen

run(app, debug=True, reloader=True, host='10.0.2.114', server='gunicorn', port=8080)
Könntest du mir ggf. sagen, wie ich da vorzugehen habe?

mfg

PS:
- Die Methode bricht wirklich sehr schnell ab. Jedoch dauert ein Aufruf viel zu lange....

- Mit CherryPyServer dauert der Abbruch 7 Sekunden. Dies wäre noch zu verkraften.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

@snafu (eigentlich an alle ;) )
snafu hat geschrieben: Ich würde da eigentlich nur das aktuelle Bild puffern, sofern ein Live-Zugriff auf die Kamera angedacht ist. In einer Schleife wird dann halt alle 100 Millisekunden ein neues Bild abgelegt und die verschiedenen Clients fragen deinen Kamera-Wrapper nach dem aktuellen Bild. Dann muss sich die Kamera an sich nicht weiter damit beschäftigen, sondern sie liefert stumpf in gleichbleibenden Abständen das Bildmaterial, welches von der Wrapper-Klasse erfragt wird. Wenn gerade kein Client ein Bild sehen will, dann wird das ungenutzte Bild halt wieder verworfen.
Könnte mir jemand diesbezüglich einen Denkanstoß geben? Wie könnte im Groben so eine "Wrapper-Klasse" aussehen?
Antworten