python Axis IP摄像机的视频流广播导致async_lock错误

yqyhoc1h  于 2023-05-21  发布在  Python
关注(0)|答案(1)|浏览(174)

我有一个AXIS IP摄像机,我可以用httprtsp协议检查它。

http://ca.me.ra.ip/axis-cgi/mjpg/video.cgi?resolution=1024x768&dummy=1520

rtsp://ca.me.ra.ip:554/axis-media/media.amp?videocodec=h264&resolution=640x480

我尝试做的是获取流,在我的服务器中缓冲它,然后广播它,这样通过一个连接到相机,我就可以预览它给多个用户。
我用这段代码计算出来:

#!/usr/bin/python
'''
    Author: Igor Maculan - n3wtron@gmail.com
    A Simple mjpg stream http server
'''
import cv2
from PIL import Image
import threading
from http.server import BaseHTTPRequestHandler,HTTPServer
from socketserver import ThreadingMixIn
from io import StringIO,BytesIO
import time
capture=None

class CamHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.endswith('.mjpg'):
            self.send_response(200)
            self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
            self.end_headers()
            while True:
                try:
                    rc,img = capture.read()
                    if not rc:
                        continue
                    imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
                    jpg = Image.fromarray(imgRGB)
                    tmpFile = BytesIO()
                    jpg.save(tmpFile,'JPEG')
                    self.wfile.write("--jpgboundary".encode())
                    self.send_header('Content-type','image/jpeg')
                    self.send_header('Content-length',str(tmpFile.getbuffer().nbytes))
                    self.end_headers()
                    jpg.save(self.wfile,'JPEG')
                    time.sleep(0.05)
                except KeyboardInterrupt:
                    break
            return
        if self.path.endswith('.html'):
            self.send_response(200)
            self.send_header('Content-type','text/html')
            self.end_headers()
            self.wfile.write('<html><head></head><body>'.encode())
            self.wfile.write('<img src="http://127.0.0.1:8080/cam.mjpg"/>'.encode())
            self.wfile.write('</body></html>'.encode())
            return

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""

def main():
    global capture
    capture = cv2.VideoCapture("rtsp://ca.me.ra.ip:554/axis-media/media.amp?videocodec=h264&resolution=640x480")
    capture.set(cv2.CAP_PROP_FRAME_WIDTH, 320); 
    capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 240);
    capture.set(cv2.CAP_PROP_SATURATION,0.2);
    global img
    try:
        server = ThreadedHTTPServer(('0.0.0.0', 8080), CamHandler)
        print( "server started")
        server.serve_forever()
    except KeyboardInterrupt:
        capture.release()
        server.socket.close()

if __name__ == '__main__':
    main()

然后我可以在浏览器中使用此地址检查本地机器中的摄像头:

http://ca.me.ra.ip:8080/cam.mjpg

问题是,如果我在两个浏览器中打开它,服务器会崩溃并出现以下错误:
Assertfctx->async_lock在libavcodec/pthread_frame.c:155失败
链接到pthread_frame.c

152 static void async_unlock(FrameThreadContext *fctx)
  153 {
  154     pthread_mutex_lock(&fctx->async_mutex);
  155     av_assert0(fctx->async_lock);   <-- This is the line which throws error
  156     fctx->async_lock = 0;
  157     pthread_cond_broadcast(&fctx->async_cond);
  158     pthread_mutex_unlock(&fctx->async_mutex);
  159 }

如果我在本地机器上将摄像头地址更改为capture = cv2.VideoCapture(0)来广播我的笔记本电脑摄像头,无论我运行多少个浏览器,一切都正常。
有没有一种方法可以解决这个问题,而不改变相机地址如上所示?

qij5mzcb

qij5mzcb1#

我认为这个问题源于两个不同的'do_GET()'在争夺相机的帧数据。
一个解决方案是将相机捕获代码从CamHandler移回main或其自己的线程中,同时将服务器代码移到其自己的线程中。
将相机帧馈送到collections.deque中,您可以使用partial将链接传递到HTTPRequestHandler,并允许任何连接的设备使用deque的第一个元素。
这是从这里的信息整理出来的:https://stackoverflow.com/a/46224191https://stackoverflow.com/a/52046062
下面是我为此所做的松散代码。

import collections
import cv2
from http.server import BaseHTTPRequestHandler, HTTPServer
import socket
import threading

camtoview = collections.deque(maxlen=5)

class MyServer(BaseHTTPRequestHandler):
  def __init__(self,camtoview, *args, **kwargs):
    self.camtoview = camtoview
    super().__init__(*args,**kwargs)
  def do_GET(self):
    if self.path.endswith('.mjpg'):
      self.send_response(200)
      self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
      self.end_headers()
      try:
        while True:
          if len(self.camtoview):
            curfram = self.camtoview[0]
            flag, curfram = cv2.imencode(".jpg",curfram)
            self.wfile.write(b'--FRAME\r\n')
            self.send_header('Content-Type','image/jpeg')
            self.send_header('Content-Length', len(curfram))
            send.end_headers()
            self.wfile.write(curfram)
      except Exception as exception:
        pass

class webThread(threading.Thread):
  def __init__(self,sock,addr,cam):
    threading.Thread.__init__(self)
    self.camtoview = cam
    self.sock = sock
    self.addr = addr
    self.daemon = True
    self.start()
  def run(self):
    camhanded = partial(CamHandler, self.camtoview)
    httpd = HTTPServer((self.addr[0],self.addr[1]),camhanded,False)
    httpd.socket = self.sock
    httpd.server_bind = self.server_close = lambda self: None
    httpd.serve_forever()

if __name__ == "__main__":
  addr = ("localhost",8080)
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  sock.bind(addr)
  sock.listen(5)
  [webThread(sock,addr,camtoview) for i in range(50)]
  ###
  #infinite camera loop down here#
  while True:
    #camera code
    camtoview.append(newcameraframe)

相关问题