UDP 实时视频传输
- 采用组播组的方式
- 视频编/解码核心是 OpenCV
实现思路
- 视频采集:
- 使用 OpenCV 读取视频流(从文件或摄像头)。
- 视频编码:
- 将视频帧编码为字节流(例如使用 JPEG 压缩)。
- UDP 组播发送:
- 使用
socket
模块将编码后的帧数据发送到组播地址。
- UDP 组播接收:
- 在接收端加入组播帧,接收视频帧并解码显示。
代码实现
发送端:发送视频帧到组播地址
import cv2
import socket
import struct
import time
# 组播地址和端口
MULTICAST_ADDRESS = "226.0.0.80"
PORT = 5004
def video_sender(video_source=0):
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 设置组播 TTL
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
# 打开视频源 (摄像头或文件)
cap = cv2.VideoCapture(video_source)
if not cap.isOpened():
print("Error: Cannot open video souce.")
return
print("Start sending video...")
while True:
ret, frame = cap.read()
if not ret:
print("End of video stream.")
break
# 将帧编码为 JPEG 格式
_, encoded_frame = cv2.imencode('.jpg', frame)
data = encoded_frame.tobytes()
# 分片发送
max_packet_size = 65000 # UDP 数据包的最大有效负载
for i in range(0, len(data), max_packet_size):
packet = data[i:i + max_packet_size]
sock.sendto(packet, (MULTICAST_ADDRESS, PORT))
time.sleep(0.03) # 控制帧率(约 30 fps)
cap.release()
sock.close()
if __name__ == "__main__":
video_sender(0) # 参数 0 表示默认摄像头
接收端:接收视频并显示
import cv2
import socket
import struct
import numpy as np
# 组播地址和端口
MULTICAST_ADDRESS = "226.0.0.80"
PORT = 5004
def video_receiver():
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("", PORT))
# 加入组播组
group = socket.inet_aton(MULTICAST_ADDRESS)
mreq = struct.pack("4sL", group, socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print("Start receiving video...")
buffer = b""
while True:
try:
# 接收数据
packet, _ = sock.recvfrom(65536)
buffer += packet
# 判断是否为完整帧(简单方式:检查 JPEG 文件结束标记)
if buffer.endswith(b'\xff\xd9'): # JPEG 结束标志
# 解码为图像
frame = cv2.imdecode(np.frombuffer(buffer, dtype=np.uint8), cv2.IMREAD_COLOR)
buffer = b"" # 清空缓冲区
# 显示图像
if frame is not None:
cv2.imshow("Multicast Video", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except Exception as e:
print("Error:", e)
break
sock.close()
cv2.destroyAllWindows()
if __name__ == "__main__":
video_receiver()
注意事项
- 组播地址范围:
- 有效的 IPv4 组播地址范围为
224.0.0.0
至239.255.255.255
。 - 避免使用
224.0.0.x
范围的地址(通常为本地管理地址)
- 网络配置:
- 确保路由器或防火墙允许组播流量。
- 如果在本地网络使用多个接口,可以通过
setsockopt
明确绑定接口。
- 分片机制:
- UDP 数据包的最大负载通常为 65535 字节。
- 如果帧数据超过该限制,需要进行分片处理。
- 帧率控制:
- 使用
time.sleep()
控制发送帧率,避免占用过多宽带。
- 优化:
- **压缩:**可以使用更高效的视频编解码器(如 H.264)代替 JPEG。
- **带宽限制:**根据网络条件调整分辨率和压缩质量。
运行方法
- 运行接收端:
- 确保接收端在同一网络中,运行
video_receiver()
。
- 运行发送端:
- 从摄像头或文件读取视频,运行
video_sender()
。
如果需要进一步优化(如更高效的编解码、网络抖动处理等),可以考虑引入如
FFmpeg
或GStreamer
的工具库。