您现在的位置是:首页 >技术教程 >基于Django websocket实现视频画面的实时传输案例网站首页技术教程

基于Django websocket实现视频画面的实时传输案例

cyl-2002 2024-09-12 12:01:04
简介基于Django websocket实现视频画面的实时传输案例

基于Django websocket实现视频画面的实时传输案例

?本案例是基于B/S架构的视频监控画面的实时传输,使用django作为服务端的开发框架。

Django Channels 是一个用于在 Django 框架中实现实时、异步通信的扩展库。传统的 Django 是基于请求-响应模式的,每个请求都会经过 Django 的视图函数进行处理并返回响应。而 Channels 提供了基于事件驱动的编程模型,使得开发者可以处理实时的事件,如 WebSocket 连接、消息队列、定时任务等。

Channels 的主要特性包括:

  • 支持 WebSocket:Channels 可以轻松地处理 WebSocket 连接,实现实时的双向通信;
  • 异步处理:Channels 使用异步方式处理请求和事件,可以提高应用的性能和并发能力;
  • Channels 可以与诸如 Celery 等任务队列库集成,处理后台任务和定时任务。

Channels可以跟Django无缝衔接,常用于开发聊天室、实时通知、实时数据更新等。

WebSocket 是一种在客户端和服务器之间进行双向通信的网络协议。与传统的 HTTP 请求-响应模式不同,WebSocket 只需要完成一次握手就可以创建持久性的连接,允许服务器主动向客户端推送数据,而不需要客户端发起请求。

在使用 WebSocket 进行通信时,涉及到异步和同步两个概念:

在异步模式下,websocket使用异步的方式操作i/o,允许同时处理多个连接或事件。

在同步模式下,每个 WebSocket 连接会被分配给一个线程或进程进行处理,消息的接收和发送是同步的操作。同步方式适用于一对一的连接事件。

环境配置

1、下载安装channels库

pip install channels

2、添加 Channels 到 Django 项目的安装应用列表中:打开 Django 项目的 settings.py 文件,在 INSTALLED_APPS 设置中添加 'channels',确保它出现在其他应用的前面:

INSTALLED_APPS = [
    ...
    'channels',
    ...
]

并且在settings.py 文件添加以下内容:

ASGI_APPLICATION = "projetc2.asgi.application" #project2为项目名称

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

Django Channels 运行于 ASGI(Asynchronous Server Gateway Interface)协议上。ASGI 是一个用于处理异步 Python Web 应用程序的协议,它提供了一种标准的方式来与 Web 服务器和应用程序框架之间进行通信。

3、配置 Channels 的 ASGI 应用程序:在 Django 项目的根目录下有一个名为 asgi.py 的文件,然后将文件中修改为以下内容:

import os

from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from projetc2 import routing 

os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名称.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": URLRouter(routing.websocket_urlpatterns)
})


4、配置 WebSocket 路由:在 Django 项目 project的目录下创建一个名为 routing.py 的文件,然后定义您的 WebSocket 路由。以下是一个示例:

from django.urls import re_path
from app import consumers
websocket_urlpatterns = [
    re_path(r'ws/some-path1/$', consumers.TailfConsumer.as_asgi()),
]

5、创建 WebSocket 消费者:在 myapp 应用的目录下创建一个名为 consumers.py 的文件,定义您的 WebSocket 消费者类。

使用 Django WebSocket 实现循环发送图像数据

当使用 Django WebSocket 实现循环发送数据时,可能会遇到 “took too long to shut down and was killed” 错误。这个错误通常是因为循环发送数据的操作没有正确终止导致的。

AsyncWebsocketConsumer 是 Django Channels 中用于处理 WebSocket 连接的异步消费者类。它是一个基于协程的类,用于处理 WebSocket 连接的生命周期、接收和发送消息等操作。

创建一个继承AsyncWebsocketConsumer的自定义消费者类


class TailfConsumer(AsyncWebsocketConsumer):
    async def connect(self):
    
        """
        这里定义连接建立时的逻辑
        """
        
        # 接受客户端连接        
        await self.accept()
        # 开启循环发送数据
        asyncio.ensure_future(self.send_data_loop())


    async def disconnect(self, close_code):
        # 连接断开时的逻辑
        pass
        
     
    async def send_data_loop(self):
        while True:
            # 获取数据
            
            data = await self.get_data()
            await self.send(json.dumps(data))
            await asyncio.sleep(1)  # 假设每秒发送一次数据

   async def get_data(self):
        """
        监听端口5000 ,实时获取视频图像数据
        """
        import pickle
        import socket
        # 创建客户端 socket 对象
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置服务器地址和端口号
        server_address = ('192.168.1.106', 5000)
        # 连接服务器
        client_socket.connect(server_address)
        decoded_data = {"leftimg":""}
        try:
            # 接收数据
            received_data = client_socket.recv(600000000)
            # 如果接收到数据,则进行处理
            if received_data:
                try:
                    decoded_data = pickle.loads(received_data)
                    # 在这里处理解码后的数据
                except pickle.UnpicklingError as e:
                    # 处理解码错误
                    print(f"Failed to decode pickle data: {e}")
                client_socket.close()
        except Exception as e:
            print('接收数据时出错:', str(e))
        return decoded_data


asyncawait 是 Python 中用于定义和处理异步操作的关键字。它们与协程(coroutine)一起使用,以实现更高效的并发和非阻塞的编程。

创建一个脚本打开摄像头模拟通过socket发送图像数据,由于直接打开摄像头不能被多个用户同时获取数据,所以采用这种方式实时发送数据,脚本如下:

import pickle
import socket
import threading
import cv2
import base64

video_source = 0
# 创建视频捕获对象
cap = cv2.VideoCapture(video_source)
# 获取视频的宽度和高度
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 用于存储连接的客户端 Socket
client_sockets = []
import random

# 生成一个包含19个随机整数的列表


# 创建 socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定服务器地址和端口
server_address = ('192.168.1.106', 5000)
server_socket.bind(server_address)

# 监听客户端连接
server_socket.listen(1)


def image_to_base64(image):
    # 将图像数据编码为 Base64 字符串
    image_base64 = base64.b64encode(image).decode('utf-8')
    return image_base64



# 定义发送视频数据的函数
def send_video_data():
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        # 将图像编码为 JPEG 格式
        _, image_data = cv2.imencode('.jpg', frame)
        # 遍历所有客户端连接,发送视频数据
        for client_socket in client_sockets:
            try:
                # 发送图像数据
                encoded_data = pickle.dumps({
                    "leftimg": image_to_base64(image_data),
                })

                client_socket.sendall(encoded_data)
            except Exception as e:
                print(f"Error sending video data to client: {e}")
                client_socket.close()
                client_sockets.remove(client_socket)



# 定义处理客户端连接的函数
def handle_client_connection(client_socket):
    while True:
        try:
            # 接收客户端请求
            data = client_socket.recv(1024)
            if not data:
                break
            # 处理客户端请求
            # 在此可以添加其他自定义逻辑
        except Exception as e:
            print(f"Error handling client connection: {e}")
            break

    # 关闭客户端连接
    client_socket.close()
    client_sockets.remove(client_socket)


# 接受客户端连接,并启动视频发送线程
def accept_client_connections():
    while True:
        client_socket, _ = server_socket.accept()
        client_sockets.append(client_socket)

# 启动视频发送线程和客户端连接接受线程
send_thread = threading.Thread(target=send_video_data)
accept_thread = threading.Thread(target=accept_client_connections)

send_thread.start()
accept_thread.start()

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<img  width="800" height="500" id="image">

<script>
    var socket = new WebSocket("ws://192.168.1.106:8000/ws/some-path1/");
    socket.onopen = function(event) {
        console.log("WebSocket连接已打开");
    };

    socket.onmessage = function(event) {
        // 接收到消息时的处理
        const imageData = event.data; // 从事件中获取图像数据
        // 在前端显示图像数据,这里假设有一个img元素来显示图像
        const imgElement = document.getElementById('image');
        imgElement.src = "data:image/jpeg;base64," + JSON.parse(imageData)["leftimg"];
    };

    socket.onclose = function(event) {
        console.log("WebSocket连接已关闭");
    };

</script>

</body>
</html>
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。