最近要实现一个项目,就是使用Jetson nano,进行人体识别,同时推流视频到公网。
最初的版本是,在Nano跑YOLOV5,再将处理后的图片经过ffmpeg推送到自己搭建的nginx直播服务器,服务器转成hls流,可在网页端实时观看直播。但问题很多:视频帧数低,直播延迟大,大到20秒~40秒,体验效果极差。
针对视频帧数低的问题,解决方案是使用一台GPU服务器专门进行视频处理。
针对直播延迟的问题,解决方案是使用UDP直接传输经过压缩的视频帧,同时使用ws协议对连进来的网页进行转发,达到0.5s以内的延迟。
Nano沦为了一个图片采集、转发的工具。
效果图:
客户端、服务端、网页端的视频几乎同步。
服务端代码(GPU服务)
import socket
import asyncio
import numpy as np
import cv2 as cv
import binascii
import threading
import copy
import websockets
from queue import Queue
import time
import sys
import os
import random
# 连接断开标志位
conn = False
q = Queue(50)
USERS = []
# UDP
socketserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
localhost = "服务器本地IP" # 改成服务器本地IP,不能是回环地址
ws_port = 5766
tcp_port = 9092
socketserver.bind((localhost, tcp_port))
def restart_program():
python = sys.executable
os.execl(python, python, * sys.argv)
def S():
print('打开了UDP服务~但不知道有没有连接,看情况咯')
print('1、UDP阻塞式读取')
print('2、异步WS协议视频帧转发')
print('3、MQTT消息交互')
while True:
try:
data, _ = socketserver.recvfrom(921600)
if len(USERS) > 0:
q.put(data)
receive_data = np.frombuffer(data, dtype='uint8')
# 在这进行图片处理
r_img = cv.imdecode(receive_data, 1)
cv.imshow('server', r_img)
if cv.waitKey(1) & 0xFF == ord('q'):
break
except ConnectionResetError as e:
print(e.args)
print("重新打开ING。。")
threading.Thread(target=S).start()
break
finally:
pass
t1 = threading.Thread(target=S)
t1.start()
async def recv_msg(websocket):
USERS.append(websocket)
print('连了一条东西进来', USERS, len(USERS))
while True:
try:
if q.qsize() > 0:
await websocket.send(q.get())
finally:
pass
async def main_logic(websocket, path):
await recv_msg(websocket)
start_server = websockets.serve(main_logic, localhost,ws_port)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
客户端(Nano)代码
import asyncio
import socket
import websockets
import cv2 as cv
import threading
import time
cap = cv.VideoCapture(0)
print(cap)
cap.set(3,480)
cap.set(4,640)
def putDate(frame):
font = cv.FONT_HERSHEY_SIMPLEX
datet = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
cv.rectangle(frame, (0,0), (320, 22), (0, 0, 0), -1)
frame = cv.putText(frame, datet, (0, 20), cv.FONT_HERSHEY_COMPLEX_SMALL, 1.3,
(255, 255, 255), 2, cv.LINE_4)
return frame
def getJPG():
try:
ret, frame = cap.read()
if not ret:
print("Fking无法抓帧")
return cv.imencode('.jpg', frame)[1].tobytes() # cv2转Bytes
except Exception as e:
print(e.args)
def MAIN():
try:
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
host = "localhost" #改成服务器公网IP
port = 9092 #服务器开放的对应的端口
print('正在连接。。。')
client.connect((host, port))
print("连接成功,WORKING~")
while True:
time.sleep(0.1)
# img = getJPG()
_, img = cap.read()
# img = cv.flip(img, 1)
img = putDate(img)
cv.imshow('JNano', img)
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 压缩图片
_, send_data = cv.imencode('.jpg', img, [cv.IMWRITE_JPEG_QUALITY, 50]) # jpg编码压缩
client.sendto(send_data, (host, port))
except Exception as e:
print("寄掉了")
print(e.args)
print("即将重试")
time.sleep(3)
threading.Thread(target=MAIN).start()
client.close()
threading.Thread(target=MAIN).start()
其中网页端代码比较多,放个链接。
点此跳转
敬告:以上代码不能直接使用,需要根据自身情况修改IP地址