如何将实时视频帧从客户端流式传输到 Flask 服务器并返回客户端?

How to stream live video frames from client to flask server and back to the client?

我正在尝试构建一个客户端服务器架构,我在其中使用 getUserMedia() 从用户的网络摄像头捕获实时视频。现在,我不想直接在 <video> 标签中显示视频,而是想将它发送到我的 Flask 服务器,对帧进行一些处理,然后将其返回到我的网页。

我使用 socketio 创建客户端-服务器连接。 这是我的index.html中的脚本。请原谅我的错误或任何错误的代码。

<div id="container">
    <video autoplay="true" id="videoElement">

    </video>
</div>
<script type="text/javascript" charset="utf-8">

    var socket = io('http://127.0.0.1:5000');

    // checking for connection
    socket.on('connect', function(){
      console.log("Connected... ", socket.connected)
    });

    var video = document.querySelector("#videoElement");


    // asking permission to access the system camera of user, capturing live 
    // video on getting true.

    if (navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {

          // instead of showing it directly in <video>, I want to send these frame to server

          //video_t.srcObject = stream

          //this code might be wrong, but this is what I want to do.
          socket.emit('catch-frame', { image: true, buffer: getFrame() });
        })
        .catch(function (err0r) {
          console.log(err0r)
          console.log("Something went wrong!");
        });
    }

    // returns a frame encoded in base64
    const getFrame = () => {
        const canvas = document.createElement('canvas');
        canvas.width = video_t.videoWidth;
        canvas.height = video_t.videoHeight;
        canvas.getContext('2d').drawImage(video_t, 0, 0);
        const data = canvas.toDataURL('image/png');
        return data;
    }


    // receive the frame from the server after processed and now I want display them in either 
    // <video> or <img>
    socket.on('response_back', function(frame){

      // this code here is wrong, but again this is what something I want to do.
      video.srcObject = frame;
    });

</script>

在我的app.py -

from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/', methods=['POST', 'GET'])
def index():
    return render_template('index.html')

@socketio.on('catch-frame')
def catch_frame(data):

    ## getting the data frames

    ## do some processing 

    ## send it back to client
    emit('response_back', data)  ## ??


if __name__ == '__main__':
    socketio.run(app, host='127.0.0.1')

我也想通过 WebRTC 来实现,但我只获取点对点的代码。

那么,有人可以帮我解决这个问题吗? 在此先感谢您的帮助。

所以,我想做的是获取客户端网络摄像头捕获的实时视频流并在后端处理它们。

我的后端代码是用 Python 编写的,我正在使用 SocketIo 将帧从前端发送到后端。您可以查看此设计以更好地了解正在发生的事情 - image

  1. 我的服务器(app.py)将在后端 运行,客户端将访问 index.html
  2. 将建立 SocketIo 连接,使用网络摄像头捕获的视频流将逐帧发送到服务器。
  3. 这些帧随后将在后端处理并发回客户端。
  4. 来自服务器的已处理帧可以显示在 img 标签中。

这是工作代码-

app.py

@socketio.on('image')
def image(data_image):
    sbuf = StringIO()
    sbuf.write(data_image)

    # decode and convert into image
    b = io.BytesIO(base64.b64decode(data_image))
    pimg = Image.open(b)

    ## converting RGB to BGR, as opencv standards
    frame = cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)

    # Process the image frame
    frame = imutils.resize(frame, width=700)
    frame = cv2.flip(frame, 1)
    imgencode = cv2.imencode('.jpg', frame)[1]

    # base64 encode
    stringData = base64.b64encode(imgencode).decode('utf-8')
    b64_src = 'data:image/jpg;base64,'
    stringData = b64_src + stringData

    # emit the frame back
    emit('response_back', stringData)

index.html

<div id="container">
    <canvas id="canvasOutput"></canvas>
    <video autoplay="true" id="videoElement"></video>
</div>

<div class = 'video'>
    <img id="image">
</div>

<script>
    var socket = io('http://localhost:5000');

    socket.on('connect', function(){
        console.log("Connected...!", socket.connected)
    });

    const video = document.querySelector("#videoElement");

    video.width = 500; 
    video.height = 375; ;

    if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
            video.srcObject = stream;
            video.play();
        })
        .catch(function (err0r) {
            console.log(err0r)
            console.log("Something went wrong!");
        });
    }

    let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
    let dst = new cv.Mat(video.height, video.width, cv.CV_8UC1);
    let cap = new cv.VideoCapture(video);

    const FPS = 22;

    setInterval(() => {
        cap.read(src);

        var type = "image/png"
        var data = document.getElementById("canvasOutput").toDataURL(type);
        data = data.replace('data:' + type + ';base64,', ''); //split off junk 
        at the beginning

        socket.emit('image', data);
    }, 10000/FPS);


    socket.on('response_back', function(image){
        const image_id = document.getElementById('image');
        image_id.src = image;
    });

</script>

此外,websockets 在安全源上运行。

我不得不稍微调整一下你的解决方案:-

我注释了三个cv变量和cap.read(src)语句,修改了下面一行

var data = document.getElementById("canvasOutput").toDataURL(type);

        var video_element = document.getElementById("videoElement")
        var frame = capture(video_element, 1)
        var data = frame.toDataURL(type);

使用此处的捕获功能:- http://appcropolis.com/blog/web-technology/using-html5-canvas-to-capture-frames-from-a-video/

我不确定这是否是正确的方法,但碰巧对我有用。

就像我说的那样,我对 javascript 不是很满意,所以与其在 javascript 中处理 base64 字符串,我更愿意从 javascript 发送整个数据并以这种方式 python 解析它

# Important to only split once
headers, image = base64_image.split(',', 1) 

我从中得出的结论是,您不能直接从包含视频元素的 canvas 中提取图像字符串,您需要创建一个新的 canvas 在上面绘制从视频元素捕获的帧的二维图像。