websocket 使用React和Django显示编码的视频帧

iibxawm4  于 2023-08-05  发布在  React
关注(0)|答案(3)|浏览(125)

我是Web开发的新手,一直试图解决一个问题,但没有运气。我使用ReactDjango
问题是,有一个第三方应用程序,使用opencv对视频帧执行一些图像处理,我必须在Web浏览器上显示这些encoded frames

我想使用Django API接收这些帧,解码它们,并使用React JS显示它们,还将每个帧的响应返回给第三方应用

我已经准备了一个流程图,说明事情应该如何运作,但根本无法开始。

流程图:


的数据
浏览器上的结果应该像这样。

结果:



需要知道如何实现这一点,我应该使用WebSockets还是可以直接将编码的帧发送到React,从而将Django从图片中删除。

编辑:

1.这些帧将由第三方应用程序以cv2.imencode('.jpg', frame)编码格式沿着JSON数据包中的一些其他数据提供服务。
1.解码需要由Django或React完成(不确定哪个将或应该处理这个)
1.帧将继续更新,就像实时视频正在播放一样。在接收到新帧的时刻,它必须替换旧帧。帧速率约为25 fps。
1.必须为每个帧返回响应。Django需要执行除了服务帧和发送回响应之外的任何操作。
1.由于会有多个摄像头同时显示,我需要在Django中使用多个端口还是一个就足够了?

eqqqjvef

eqqqjvef1#

创建Django端点:WebSockets在Django中接收编码的帧,解码它们,并返回响应,同时在Web浏览器上不断更新视频帧。

import cv2
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

@csrf_exempt
def process_frames(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        
        # Extract the encoded frames and other data from the JSON packet
        encoded_frames = data['frames']
        # Process other data as needed
        
        # Decode the frames using cv2.imdecode()
        decoded_frames = []
        for encoded_frame in encoded_frames:
            frame = cv2.imdecode(encoded_frame, cv2.IMREAD_COLOR)
            decoded_frames.append(frame)
        
        # Perform any necessary operations with the frames
        
        # Return a response for each frame
        response = {'status': 'success'}
        return JsonResponse(response)

字符串
对于视频流,您可以使用浏览器(HTML)进行视频渲染,也可以使用React(JS)进行视频渲染。两者各有利弊。

<!DOCTYPE html>
<html>
<head>
    <title>Integrating inside HTML</title>
</head>
<body>
    <video id="videoPlayer" autoplay controls></video>

    <script>
        const video = document.getElementById('videoPlayer');

        function updateVideoFrame(frame) {
            const blob = new Blob([frame], { type: 'image/jpeg' });
            const frameURL = URL.createObjectURL(blob);
            video.src = frameURL;
        }

        // Make a request to the Django endpoint to receive the frames
        setInterval(() => {
            fetch('/process_frames', { method: 'POST' })
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'success') {
                        updateVideoFrame(data.frame);
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }, 40); // Adjust the interval to achieve the desired frame rate (25 fps = 40 ms delay)
    </script>
</body>
</html>


在JS中集成

import React, { useEffect, useState } from 'react';

const VideoPlayer = () => {
    const [frame, setFrame] = useState(null);

    useEffect(() => {
        const fetchFrame = async () => {
            try {
                const response = await fetch('/process_frames', { method: 'POST' });
                const data = await response.json();

                if (data.status === 'success') {
                    setFrame(data.frame);
                }
            } catch (error) {
                console.error('Error:', error);
            }
        };

        // Fetch frames at the desired frame rate
        const intervalId = setInterval(fetchFrame, 40); // Adjust the interval to achieve the desired frame rate (25 fps = 40 ms delay)

        return () => {
            clearInterval(intervalId);
        };
    }, []);

    const videoSource = frame ? URL.createObjectURL(new Blob([frame], { type: 'image/jpeg' })) : '';

    return (
        <video src={videoSource} autoPlay controls />
    );
};

export default VideoPlayer;

编辑

使用Django通道的Django端点

# This is a template code for using Django Channels 
import cv2
import json
from channels.generic.websocket import WebsocketConsumer

class FrameProcessingConsumer(WebsocketConsumer):
    def receive(self, text_data=None, bytes_data=None):
        if bytes_data:
            # Extract the encoded frames and other data from the JSON packet
            data = json.loads(bytes_data.decode())
            encoded_frames = data['frames']
            # Process other data as needed
            
            # Decode the frames using cv2.imdecode()
            decoded_frames = []
            for encoded_frame in encoded_frames:
                frame = cv2.imdecode(encoded_frame, cv2.IMREAD_COLOR)
                decoded_frames.append(frame)
            
            # Perform any necessary operations with the frames
            
            # Return a response for each frame
            response = {'status': 'success'}
            self.send(json.dumps(response))

@csrf_exempt
def process_frames(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        
        # Extract the encoded frames and other data from the JSON packet
        encoded_frames = data['frames']
        # Process other data as needed
        
        # Decode the frames using cv2.imdecode()
        decoded_frames = []
        for encoded_frame in encoded_frames:
            frame = cv2.imdecode(encoded_frame, cv2.IMREAD_COLOR)
            decoded_frames.append(frame)
        
        # Perform any necessary operations with the frames
        
        # Return a response for each frame
        response = {'status': 'success'}
        return JsonResponse(response)


根据您的要求进行更改。
希望这对你有帮助…

ykejflvf

ykejflvf2#

您似乎正在处理MJPEG格式(Motion JPEG)的视频流。它是一个没有任何帧间压缩的JPEG帧序列。

仅限前端

您通常可以从前端捕获MJPEG流。但是,如果第三方直接访问IP摄像机而不使用缓存层,您可能会以非常小的流量有效地DDOS它。我设法放慢我的本地主机网络摄像头MJPEG服务器只使用少数接收器。
此外,您直接向用户公开您的第三方源。第三方可以监视您的用户。

后端-前端

通过您自己的后端传递流在资源上的成本更高。您向第三方服务器发出每帧最小请求,但必须向所有客户端提供流。

解析

如果你的前端只供你使用,那就选择无后端的解决方案。如果你有足够的后端资源,你希望有更多的客户端,你不想把你的客户端暴露给第三方,在后端提供MJPEG。
至于技术部分,有很多开箱即用的解决方案。

bwntbbo3

bwntbbo33#

根据我的经验,最好是利用服务器发送的事件- * 如果通信是单向的,不需要将数据发送到后端 ,可以这样做:
我强烈建议尽可能地降低复杂性,这意味着在这个场景中将Django从图片中删除(否则,您将不得不从那里消费event-stream内容并通过一些额外的配置将其中继到客户端)。
1.确保第三方应用程序允许将text/event-stream作为响应类型提供服务,并启用了CORS
1.在React中,安装@microsoft/fetch-event-source,它具有原生Fetch API的所有内置功能,并增强了使用event-stream内容的可用性。
1.在React组件中,从useEffect hook中的fetch-event-source包添加以下逻辑(
其他人做了一个惊人的工作,详细说明了步骤)* 只要确保Content-type头值设置为text/event-stream即可。

**提示:**请确保通过返回关闭EventStream的函数来清理useEffect,以避免任何性能问题或内存泄漏。

如果你需要进一步澄清,请告诉我。

相关问题