【问题标题】:How to stream live video frames from client to flask server and back to the client?如何将实时视频帧从客户端流式传输到烧瓶服务器并返回到客户端?
【发布时间】:2020-03-14 20:14:23
【问题描述】:

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

我使用 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 来做这件事,但我只是获取点对点的代码。

那么,谁能帮我解决这个问题? 提前感谢您的帮助。

【问题讨论】:

  • 您分享的脚本的哪一部分需要帮助?请添加有关此代码不起作用的详细信息。
  • 我在尝试使用socket.emit('catch-frame', { image: true, buffer: getFrame() }); 将流发送到服务器的部分遇到问题。当我尝试像这样frame = data 获取catch_frame(data) 中的流时,我没有收到任何帧。此外,我必须在处理后将帧从服务器发送回客户端。但由于没有框架,我在socket.on('response_back', function(frame) 中没有得到任何框架,我可以从那里将其源到&lt;video&gt; 标签。
  • 那么data 的值是多少?
  • 它给了我一个价值{image: true, buffer: "data:,"}的对象。没有框架
  • 您是否在客户端打印了data 以确认它实际上包含图像数据?最明显的解释是您的客户端正在发送"data:,",我猜这是一个空帧的数据URL。

标签: javascript python flask webrtc flask-socketio


【解决方案1】:

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

我注释了三个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) 

我的收获是,冒着听起来循环的风险,您不能直接从包含视频元素的画布中拉出图像字符串,您需要创建一个新的画布,在其上绘制 2D您从视频元素中捕获的帧的图像。

【讨论】:

  • 这个解决方案对我有用。不过,您找到减少延迟的方法了吗?
  • 不幸的是,延迟是我无法弄清楚的
【解决方案2】:

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

我的后端代码是用 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 在安全源上运行。

【讨论】:

  • 您好,我无法从客户端获取数据到服务器。你能详细说明一下代码吗? app.py 代码中如何使用 pimg。以及如何将图像打开为 opencv 图像。谢谢
  • 相同的@akan - 我尝试时无法正常工作。您能否详细说明上述内容,和/或发布包含完整工作代码的 GitHub 存储库链接?
  • @zen_of_python @TechieViN 感谢您指出这一点。我已经编辑了我的代码以将图像与 OpenCV 一起使用。希望这能解决问题。 frame = cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)
  • 谢谢.. 还有 sbuf 是干什么用的?
  • 我通过将视频元素从这里传递给捕获函数来使其工作:-appcropolis.com/blog/web-technology/…。不确定 cv 错误,但我能够在没有 cv.js 的情况下为我的用例调整代码
猜你喜欢
  • 1970-01-01
  • 2014-01-19
  • 2020-11-16
  • 2021-09-19
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多