由于通过流媒体服务器传输画面延迟太高的问题,不知道是没有调试到合适的参数还是其他什么问题。诞生了这篇博客。
RK3588板端上接摄像头,采集画面,通过网口实时传输给上位机并显示。
第一代版本
RK3588代码
python
import cv2
import socket
import struct
# 配置
SERVER_IP = '192.168.137.1' # 上位机的IP地址
PORT = 5000 # 端口号
# 创建一个socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 打开摄像头
cap = cv2.VideoCapture(23)
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
# 读取摄像头帧
ret, frame = cap.read()
if not ret:
print("无法读取帧")
break
frame = cv2.resize(frame,(320,240))
# 对帧进行编码
encoded, buffer = cv2.imencode('.jpg', frame)
if not encoded:
print("编码帧失败")
break
# 发送数据
data = buffer.tobytes()
print(len(data))
print(len(struct.pack('L', len(data))))
print(struct.pack('L', len(data)))
# 发送数据大小
client_socket.sendto(struct.pack('L', len(data)), (SERVER_IP, PORT))
# 发送数据
client_socket.sendto(data, (SERVER_IP, PORT))
# 释放资源
cap.release()
client_socket.close()
上位机代码(windows系统)
python
import cv2
import socket
import numpy as np
# 配置
PORT = 5000 # 端口号
# 创建一个socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('0.0.0.0', PORT))
# 创建窗口并设置为全屏模式
cv2.namedWindow('Video Stream', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('Video Stream', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
while True:
# 接收数据大小
data_size, _ = server_socket.recvfrom(8)
# 接收的视频数据长度
data_size = int.from_bytes(data_size, byteorder='little')
# 接收视频数据
data, _ = server_socket.recvfrom(data_size)
# 解码
np_data = np.frombuffer(data, dtype=np.uint8)
frame = cv2.imdecode(np_data, cv2.IMREAD_COLOR)
if frame is None:
print("解码帧失败")
continue
# 显示帧
cv2.imshow('Video Stream', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
server_socket.close()
cv2.destroyAllWindows()
测试视频
遗留问题:视频每帧只能一次传输完毕,且每帧的大小不能过大,不然会报错。
第二代版本
通过分组发送帧图像的方式,即segment_size,优化了帧图像必须要一次传输的问题。
并且把b'\xff\xff'当作一帧画面传输完成的表中,暂时还没发现什么问题。
RK3588代码
python
import cv2
import socket
# 配置
SERVER_IP = '192.168.137.1' # 上位机的IP地址
PORT = 5000 # 端口号
# 创建一个socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 打开摄像头
cap = cv2.VideoCapture(23)
if not cap.isOpened():
print("无法打开摄像头")
exit()
frame_count = 0
segment_size = 65000 #32678 # 一组数据的大小
while True:
# 读取摄像头帧
ret, frame = cap.read()
if not ret:
print("无法读取帧")
break
# 对帧进行编码
encoded, buffer = cv2.imencode('.jpg', frame)
if not encoded:
print("编码帧失败")
break
# 发送数据
data = buffer.tobytes()
data_len = len(data) # 一帧图像的数据大小
print(data_len)
# 发送数据
time = data_len // segment_size # 需要发多少组
left = data_len % segment_size # 剩下多少个
if time > 0:
for i in range(time): # 遍历发送所有的组
start_pos = i*segment_size
end_pos = (i+1)*segment_size
client_socket.sendto(data[start_pos:end_pos], (SERVER_IP, PORT))
# 发送剩下的,并且加上结束帧标识符
client_socket.sendto(data[time*segment_size:data_len] + b'\xff\xff', (SERVER_IP, PORT))
frame_count +=1
# 释放资源
cap.release()
client_socket.close()
上位机代码(windows系统)
python
import cv2
import socket
import numpy as np
# 配置
PORT = 5000 # 端口号
# 创建一个socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('0.0.0.0', PORT))
# 一组数据的大小
segment_size = 65000 #32678
while True:
data_byte = b''
# 接收视频数据
data, _ = server_socket.recvfrom(segment_size)
while (b'\xff\xff' not in data):
data_byte += data
data, _ = server_socket.recvfrom(segment_size)
# 把分组发送的字节相加
data_byte += data
#去除最后两个结束帧标识符
rev_data = data_byte[:-2]
# 解码
np_data = np.frombuffer(rev_data, dtype=np.uint8)
frame = cv2.imdecode(np_data, cv2.IMREAD_COLOR)
if frame is None:
print("解码帧失败")
continue
# 显示帧
cv2.imshow('Video Stream', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
server_socket.close()
cv2.destroyAllWindows()
后续优化方向:可以通过多线程的方式去发送每组的数据,这样能进一步增加实时性。