您现在的位置是:首页 >技术教程 >基于Django websocket实现视频画面的实时传输案例网站首页技术教程
基于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
async
和 await
是 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>