OK啊老铁,今天记录一下在同一局域网下另一台设备接收本机调用摄像头的画面。
首先我们先用本机测试一下
import cv2
import socket
import struct
ip_address = '' #相机ip
port = #摄像头端口
username = '' #摄像头用户名
password = '' #摄像头密码
target_ip = '127.0.0.1'#目标IP
target_port = 12345 #目标端口
'''构建 RTSP URL 并打开视频流'''
rtsp_url = f'rtsp://{username}:{password}@{ip_address}:{port}/live'
cap = cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG)
'''检查摄像头是否成功打开'''
if not cap.isOpened():
print('无法连接摄像头!')
exit()
'''创建 UDP 套接字'''
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
'''设置视频质量参数'''
quality = 50
'''设置 UDP 数据包大小和索引变量'''
packet_size = 1024 #数据包大小
packet_index = 0 #数据包索引
packet_count = 0
end_of_stream_sent = False
try:
'''进入主循环,不断读取摄像头视频帧并发送'''
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
_, jpeg_data = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), quality])
packet_count = (len(jpeg_data) + packet_size - 1) // packet_size
for i in range(packet_count):
start = i * packet_size
end = min(start + packet_size, len(jpeg_data))
packet = jpeg_data[start:end]
if i == packet_count - 1:
is_last = 1
packet_size_actual = len(packet)
else:
is_last = 0
packet_size_actual = packet_size
header = struct.pack('!HIB', i, packet_size_actual, is_last)
packet = jpeg_data[start:end] # 这一步已经确保了packet是bytes类型
packet_bytes = packet.tobytes()
sock.sendto(header + packet_bytes, (target_ip, target_port))
packet_index += 1
# 在JPEG图像的最后一个数据包发送后发送“结束”数据包
if not end_of_stream_sent:
end_header = struct.pack('!HIB', packet_count, 0, 1)
sock.sendto(end_header, (target_ip, target_port))
end_of_stream_sent = True
cv2.imshow('camera', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
sock.close()
cv2.destroyAllWindows()
这段代码使用 RTSP 协议连接到指定 IP 地址和端口上的摄像头,把捕获到的视频帧以 JPEG 格式进行压缩和分包,然后通过 UDP 协议将分包的数据传输到目标 IP (这里假设目标IP是本机IP)和端口上。接收端可以解析这些数据包并将其组合成完整的 JPEG 图像,从而实现实时视频传输。
import socket
import struct
import cv2
import numpy as np
'''设置目标 IP 和端口,创建 UDP 套接字,并绑定到指定的地址'''
target_ip = '127.0.0.1'
target_port = 12345
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((target_ip, target_port))
try:
'''进入主循环,接收 UDP 数据包'''
while True:
'''这里 recvfrom 函数接收 UDP 数据包,921600 是数据包的最大大小。'''
data, addr = sock.recvfrom(921600)
'''
使用 struct.unpack 函数解析数据包头部信息
!HIB 分别代表 unsigned short、unsigned int 和 unsigned byte 类型
packet_index 是数据包的索引
packet_size_actual 是数据包的实际大小
is_last 表示是否为最后一个数据包
jpeg_data 是 JPEG 图像数据
'''
packet_index, packet_size_actual, is_last = struct.unpack('!HIB', data[:7])
jpeg_data = data[7:]
'''将接收到的数据包组合成完整的 JPEG 图像:如果是第一个数据包,则直接赋值给 received_data,否则将数据包拼接到 received_data 后面。'''
if packet_index == 0:
received_data = jpeg_data
else:
received_data += jpeg_data
'''如果接收到了最后一个数据包,则使用 cv2.imdecode 函数将 JPEG 数据解码为图像,并进行大小调整'''
'''
这里使用 NumPy 的 frombuffer 函数将二进制数据转换为 NumPy 数组
然后使用 OpenCV 的 imdecode 函数解码 JPEG 数据为图像
最后,使用 cv2.resize 函数调整图像大小,然后显示图像
'''
if is_last:
img = cv2.imdecode(np.frombuffer(received_data, dtype=np.uint8), cv2.IMREAD_COLOR)
img_resized = cv2.resize(img, (1280, int(1280 / img.shape[1] * img.shape[0])))
cv2.imshow('resized_image', img_resized)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
received_data = b''
finally:
sock.close()
cv2.destroyAllWindows()
'''在 finally 块中关闭 UDP 套接字,并关闭 OpenCV 显示的窗口'''
这段代码用于接收 UDP 数据包,将它们组合成完整的 JPEG 图像,然后使用 OpenCV 显示图像。OK,在我们有了这两段代码,并且所有的库都安装好后就可以在终端开始运行了。
效果展示:
那么这是在本地机器上接收,如果是在别的电脑上接收我这边拍摄的视频呢,很简单
首先修改我们传输代码中的目标IP,改为另一台在同一局域网下的电脑的IP
target_ip = '127.0.0.1'#目标IP
查看电脑上自己的IP也很简单
如果是windows系统,输入以下命令即可查看
ipconfig/ALL
如果是ubuntu系统,输入以下命令
ifconfig
本人没有用过mac,所以不知道,有知道的老铁可以在评论区输出一下
我们查看完接收端的IP地址后,然后更改我们传输端的目标IP为接收端的IP地址,紧接着我们修改我们接收端的代码,同样修改为接收端的IP地址
target_ip = '127.0.0.1'
在循环外添加
received_data = b'' # 在循环外初始化 received_data 变量
在最后加入
received_data = b'' # 重置 received_data,准备接收下一张图片
为了防止有些小伙伴加错或者缩进错误,下面我会贴上接收代码修改后的完整版
import socket
import struct
import cv2
import numpy as np
target_ip = ''#修改为同一局域网下接收端的IP地址
target_port = 12345
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((target_ip, target_port))
received_data = b'' # 在循环外初始化 received_data 变量
try:
while True:
data, addr = sock.recvfrom(921600) # 接收数据
packet_index, packet_size_actual, is_last = struct.unpack('!HIB', data[:7])
jpeg_data = data[7:]
if packet_index == 0:
received_data = jpeg_data
else:
received_data += jpeg_data
if is_last:
print("Received JPEG data size:", len(received_data)) # 打印接收到的 JPEG 数据大小
img = cv2.imdecode(np.frombuffer(received_data, dtype=np.uint8), cv2.IMREAD_COLOR)
if img is not None:
print("Image shape:", img.shape) # 打印图像的形状
img_resized = cv2.resize(img, (1280, int(1280 / img.shape[1] * img.shape[0])))
cv2.imshow('resized_image', img_resized)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
received_data = b'' # 重置 received_data,准备接收下一张图片
finally:
sock.close()
cv2.destroyAllWindows()
那么以上的代码实现无线视频传输仅仅局限于同一局域网下的两台机器或者两台用户进行相互的无线通信,并不支持局域网外的用户和局域网内部的用户进行通信,如果要实现这些功能,就需要通过上传云端来进行数据的中转来实现。