【学习记录】Django Channels实现websocket异步手势识别视频推流

文章目录

  • 前言
  • 使用环境
  • 实现步骤
  • 推流端
  • 接收端
  • 关键一步
    • 解决方法
      • 修改mysite/setting.py
      • Redis安装
  • 运行程序
  • 总结

前言

感兴趣的可以去看看channels官方chat聊天室案例,这里主要讲解如何实现websocket异步视频推流,来看一下效果图:
【学习记录】Django Channels实现websocket异步手势识别视频推流_第1张图片

使用环境

我使用的是python3.6版本,django3.2.13版本
【学习记录】Django Channels实现websocket异步手势识别视频推流_第2张图片

实现步骤

先cd到对应目录,新建一个django项目

django-admin startproject mysite

新建的项目结构如下:
【学习记录】Django Channels实现websocket异步手势识别视频推流_第3张图片
接下来cd进入mysite目录,创建一个video App,命令如下:

cd mysite
python manage.py startapp video

【学习记录】Django Channels实现websocket异步手势识别视频推流_第4张图片
修改mysite/setting.py,添加ASGI_APPLICATION并且在INSTALLED_APPS中添加’channels’和’video’

ASGI_APPLICATION = 'mysite.asgi.application'

INSTALLED_APPS = [
    'channels',
    'video',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

【学习记录】Django Channels实现websocket异步手势识别视频推流_第5张图片
同时在video/urls.py和mysite/urls.py中添加路由,video目录下如果没有urls.py就先创建一个

video/urls.py

# video/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('/', views.v_name, name='v_name'),
]

mysite/urls.py

# mysite/urls.py
from django.urls import include,path
from django.contrib import admin

urlpatterns = [
    path('video/', include('video.urls')),
    path('admin/', admin.site.urls),
]

添加video/views.py视图:

# video/views.py
from django.shortcuts import render


def index(request):
    return render(request, 'video/index.html')


def v_name(request, v_name):
    return render(request, 'video/video.html', {
        'v_name': v_name
    })

在video目录下新建一个consumers.py:

# video/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer


class VideoConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['v_name']
        self.room_group_name = 'video_%s' % self.room_name
        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'video_message',
                'message': text_data,
            }
        )

    # Receive message from room group
    async def video_message(self, event):
        message = event['message']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message
        }))

在video目录新建路由routing.py:

# video/routing.py
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/video/(?P\w+)/$', consumers.VideoConsumer.as_asgi())
]

修改mysite/asgi.py

# mysite/asgi.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from channels.auth import AuthMiddlewareStack
import video.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

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

推流端

在根目录新建pullVideo.py:

# pullVideo.py
import asyncio
import websockets
import numpy as np
import cv2
import cv2 as cv
import base64
import time
import mediapipe as mp

capture = cv2.VideoCapture(r'./1.mp4')  # cv2.VideoCapture(0)为获取摄像头,因为电脑没有摄像头所以使用视频代替
mpHands = mp.solutions.hands
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils
# 设置点的颜色与大小
handLmsStyle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)
# 设置线的颜色与大小
handConStyle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=10)
if not capture.isOpened():
    print('quit')
    quit()
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 95]


# 开始推流视频
async def pullVideo(websocket):
    while True:
        ret, frame = capture.read()
        if ret:
            frame_RGB = cv.cvtColor(frame, cv.COLOR_BGR2RGB)  # 将RGB转为BGR
            result = hands.process(frame_RGB)
            if result.multi_hand_landmarks:
                for handLms in result.multi_hand_landmarks:
                    mpDraw.draw_landmarks(frame, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle, handConStyle)
        else:
            break
        time.sleep(0.01)
        result, imgencode = cv2.imencode('.jpg', frame, encode_param)
        data = np.array(imgencode)
        img = data.tobytes()
        # base64编码传输
        img = base64.b64encode(img).decode()
        await websocket.send("data:image/jpg;base64," + img)


async def main_logic():
    async with websockets.connect('ws://127.0.0.1:8000/ws/video/wms/', ping_interval=None) as websocket:
        await pullVideo(websocket)


asyncio.get_event_loop().run_until_complete(main_logic())

【学习记录】Django Channels实现websocket异步手势识别视频推流_第6张图片

接收端

在根目录新建getVideo.html:



<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Videotitle>

    <style>
        #img_div
        {
            width:500px;
            border:3px solid cyan;
            border-radius:25px;
        }
    style>
head>
<body>

<div>
    <h1>Videoh1>
div>
<div id="img_div">
    <img id="resImg" src="" align="middle"/>
div>

<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js" >script>
<script>
        const ws = new WebSocket(
            'ws://'
            + '127.0.0.1:8000'
            + '/ws/video/'
            + 'wms'
            + '/'
        );
         console.log("新建websocket")

        ws.onopen=function (ev) {
            console.log('建立连接');
             ws.send("开始");
        }



        ws.onmessage = function(evt) {
            v_data = JSON.parse(evt.data);
            $("#resImg").attr("src", v_data.message);
        };

        ws.onclose = function(evt) {
            console.log("Connection closed.");
        };
script>
body>
html>

关键一步

现在直接开始运行会报错
【学习记录】Django Channels实现websocket异步手势识别视频推流_第7张图片
server rejected WebSocket connection: HTTP 500
这是因为没有开启Redis

解决方法

修改mysite/setting.py

在mysite/setting.py代码底部添加CHANNEL_LAYERS

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

【学习记录】Django Channels实现websocket异步手势识别视频推流_第8张图片
django与Redis直接通信需要安装django-redis
使用命令安装:

pip install django-redis

Redis安装

下载Redis:https://github.com/tporadowski/redis/releases
【学习记录】Django Channels实现websocket异步手势识别视频推流_第9张图片
下载完后解压到c盘,并将文件夹重新命名为 redis
打开cmd cd进入C:\redis 执行下面代码:

redis-server.exe redis.windows.conf

【学习记录】Django Channels实现websocket异步手势识别视频推流_第10张图片
保持cmd窗口在后台,不要关闭

运行程序

打开终端,输入命令:

python manage.py runserver
python pullVideo.py

成功运行后,打开getVideo.html,就可以看到效果了
【学习记录】Django Channels实现websocket异步手势识别视频推流_第11张图片
【学习记录】Django Channels实现websocket异步手势识别视频推流_第12张图片

总结

经历千辛万苦终于实现了websocket异步视频推流,接下来说说
与channels官方chat聊天室案例不同的地方。
channels官方使用Docker安装和运行Redis,我并不了解Docker,所以并没有去使用它,而是直接下载了Redis,然后通过redis-server.exe redis.windows.conf命令运行。

你可能感兴趣的:(学习记录,django,websocket,python)