Web接收实时视频流并显示(flask创建路由接收)| 解决延时不同步问题

在开发B/S架构的检测算法时,需要对摄像头实时视频流进行检测处理。
下文代码能实现接收实时视频流到web端指定路由下进行播放。在后续开发中可继续将该路由视频流引入到相应的前端Html文件中的img指定位置进行播放。
在这里插入图片描述
其中红框位置为flask创建的路由名

第1种

第一种实现方式转载于:flask实现rtsp视频流的读取并在web界面展示。简单来说就是代码简洁清晰,足够满足单视频流的需求。但是时间久了会纯在延时,画面不同步的情况。

app.py

from flask import Flask, Response,render_template
import cv2

class VideoCamera(object):
    def __init__(self):
    	#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
        self.video=cv2.VideoCapture('rtsp://admin:[email protected]//Streaming/Channels/101')
    def __del__(self):
        self.video.release()
    def get_frame(self):
        success,image=self.video.read()
        ret,jpeg=cv2.imencode('.jpg',image)
        return jpeg.tobytes()

app = Flask(__name__)


def genWeb(camera):
    """Video streaming generator function."""
    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(genWeb(VideoCamera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    #    Run locally
    # app.run(debug=True, host='127.0.0.1', port=5000)
    #Run on the server
    app.run(debug=True, host = '0.0.0.0', port=5000)

第2种

第二种启用线程,解决视频流延时及不同步问题。效果可参看文章最后对比视频。

目录结构:
在这里插入图片描述

base_camera.py

import time
import threading
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


class CameraEvent(object):
    """An Event-like class that signals all active clients when a new frame is
    available.
    """
    def __init__(self):
        self.events = {
    
    }

    def wait(self):
        """Invoked from each client's thread to wait for the next frame."""
        ident = get_ident()
        if ident not in self.events:
            # this is a new client
            # add an entry for it in the self.events dict
            # each entry has two elements, a threading.Event() and a timestamp
            self.events[ident] = [threading.Event(), time.time()]
        return self.events[ident][0].wait()

    def set(self):
        """Invoked by the camera thread when a new frame is available."""
        now = time.time()
        remove = None
        for ident, event in self.events.items():
            if not event[0].isSet():
                # if this client's event is not set, then set it
                # also update the last set timestamp to now
                event[0].set()
                event[1] = now
            else:
                # if the client's event is already set, it means the client
                # did not process a previous frame
                # if the event stays set for more than 5 seconds, then assume
                # the client is gone and remove it
                if now - event[1] > 5:
                    remove = ident
        if remove:
            del self.events[remove]

    def clear(self):
        """Invoked from each client's thread after a frame was processed."""
        self.events[get_ident()][0].clear()


class BaseCamera(object):
    thread = None  # background thread that reads frames from camera
    frame = None  # current frame is stored here by background thread
    last_access = 0  # time of last client access to the camera
    event = CameraEvent()

    def __init__(self):
        """Start the background camera thread if it isn't running yet."""
        if BaseCamera.thread is None:
            BaseCamera.last_access = time.time()

            # start background frame thread
            BaseCamera.thread = threading.Thread(target=self._thread)
            BaseCamera.thread.start()

            # wait until frames are available
            while self.get_frame() is None:
                time.sleep(0)

    def get_frame(self):
        """Return the current camera frame."""
        BaseCamera.last_access = time.time()

        # wait for a signal from the camera thread
        BaseCamera.event.wait()
        BaseCamera.event.clear()

        return BaseCamera.frame

    @staticmethod
    def frames(path):
        """"Generator that returns frames from the camera."""
        raise RuntimeError('Must be implemented by subclasses.')

    @classmethod
    def _thread(cls):
        """Camera background thread."""
        print('Starting camera thread.')
        frames_iterator = cls.frames()
        for frame in frames_iterator:
            BaseCamera.frame = frame
            BaseCamera.event.set()  # send signal to clients
            time.sleep(0)

            # if there hasn't been any clients asking for frames in
            # the last 10 seconds then stop the thread
            if time.time() - BaseCamera.last_access > 60:
                frames_iterator.close()
                print('Stopping camera thread due to inactivity.')
                break
        BaseCamera.thread = None

url_stream.py

import time
from threading import Thread
import cv2


class LoadStreams:  # multiple IP or RTSP cameras
    def __init__(self, sources):
        sources = [sources]
        n = len(sources)
        # print("______++++____",n)
        self.imgs = [None] * n

        for i, s in enumerate(sources):
            # Start the thread to read frames from the video stream
            print(f'{
      
      i + 1}/{
      
      n}: {
      
      s}... ', end='')
            cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
            assert cap.isOpened(), f'Failed to open {
      
      s}'
            w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            fps = cap.get(cv2.CAP_PROP_FPS) % 100
            # print(w,h,fps)
            _, self.imgs[i] = cap.read()  # guarantee first frame
            thread = Thread(target=self.update, args=([i, cap]), daemon=True)
            print(f' success ({
      
      w}x{
      
      h} at {
      
      fps:.2f} FPS).')
            thread.start()
        print('')  # newline

    def update(self, index, cap):
        # Read next stream frame in a daemon thread
        n = 0
        while cap.isOpened():
            n += 1
            cap.grab()
            if n == 4:  # read every 4th frame
                success, im = cap.retrieve()
                self.imgs[index] = im if success else self.imgs[index] * 0
                n = 0
            time.sleep(0.01)  # wait time

    def __iter__(self):
        self.count = -1
        return self

    def __next__(self):
        self.count += 1
        img0 = self.imgs.copy()
        if cv2.waitKey(1) == ord('q'):  # q to quit
            cv2.destroyAllWindows()
            raise StopIteration
        return img0

    def __len__(self):
        return 0  # 1E12 frames = 32 streams at 30 FPS for 30 years

app.py

from flask import Flask, Response
from cam.base_camera import BaseCamera
from cam.url_stream import LoadStreams
import cv2

app = Flask(__name__)

class Camera(BaseCamera):
    @staticmethod
    def frames():
    	#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
    	#例如
        source = 'rtsp://admin:[email protected]//Streaming/Channels/101'
        dataset = LoadStreams(source)
        for im0s in dataset:
            im0 = im0s[0].copy()
            frame = cv2.cvtColor(im0, cv2.COLOR_BGR2RGB)
            result = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
            yield cv2.imencode('.jpg', result)[1].tobytes()

@app.route('/url')
def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(genWeb(Camera()) ,
                    mimetype='multipart/x-mixed-replace; boundary=frame')

def genWeb(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

if __name__ == '__main__':
    #    Run locally
    # app.run(debug=True, host='127.0.0.1', port=5000)
    #Run on the server
    app.run(debug=True, host = '0.0.0.0', port=5000)

如果是本地读取的话:

import cv2
from cam.url_stream import LoadStreams

#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
source= 'rtsp://admin:[email protected]//Streaming/Channels/101'
dataset = LoadStreams(source)

for im0s in dataset:
    im0 = im0s[0].copy()
    cv2.imshow("mainwindows", im0)
    cv2.waitKey(1)

flask接收实时视频流url效果对比


视频地址: https://www.bilibili.com/video/BV1f34y1Q7of/

おすすめ

転載: blog.csdn.net/weixin_48994268/article/details/120356995