点对点的, 直接通信
想深入了解代码,请查看 GitHub 上的实时视频流媒体项目仓库 (https://github.com/eknathmali/Real-Time-Video-Streaming-with-WebRTC-and-Python)。
接下来是介绍:让我们开始吧
在这篇文章中,我将指导你完成使用Python设置WebRTC连接的过程,以便从一台机器向另一台机器传输摄像头画面。WebRTC(一种Web实时通信技术)支持点对点通讯,非常适合实时视频流等任务。学完这篇教程后,你将拥有一个从远程服务器到本地机器传输视频帧的工作示例。
前提条件:
- 已在发送方和接收方的机器上安装了Python。
- 了解基本的Python编程。
- 能够访问终端或命令行界面。
设置环境指南:
首先,我们需要安装必要的库。可以使用以下命令行来安装所需的依赖:
请运行以下命令来安装aiortc和opencv-python库:
pip install aiortc opencv-python
发送方脚本( 远程计算机)
发送方程序从摄像头获取视频帧并将视频帧发送给接收方。下面是sender.py
代码:
import asyncio
import cv2
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
from aiortc.contrib.signaling import TcpSocketSignaling
from av import VideoFrame
import fractions
from datetime import datetime
class CustomVideoStreamTrack(VideoStreamTrack):
def __init__(self, camera_id):
super().__init__()
self.cap = cv2.VideoCapture(camera_id)
self.frame_count = 0
async def recv(self):
self.frame_count += 1
print(f"发送帧 {self.frame_count}")
ret, frame = self.cap.read()
if not ret:
print("从摄像头读取帧失败")
return None
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
video_frame = VideoFrame.from_ndarray(frame, format="rgb24")
video_frame.pts = self.frame_count
video_frame.time_base = fractions.Fraction(1, 30) # 使用分数来设置时间基准
# 添加时间戳到帧
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # 包含毫秒的当前时间
cv2.putText(frame, timestamp, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
video_frame = VideoFrame.from_ndarray(frame, format="rgb24")
video_frame.pts = self.frame_count
video_frame.time_base = fractions.Fraction(1, 30) # 使用分数来设置时间基准
return video_frame
async def setup_webrtc_and_run(ip_address, port, camera_id):
signaling = TcpSocketSignaling(ip_address, port)
pc = RTCPeerConnection()
video_sender = CustomVideoStreamTrack(camera_id)
pc.addTrack(video_sender)
try:
await signaling.connect()
@pc.on("datachannel")
def on_datachannel(channel):
print(f"数据信道建立: {channel.label}")
@pc.on("connectionstatechange")
async def on_connectionstatechange():
print(f"连接状态是 {pc.connectionState}")
if pc.connectionState == "connected":
print("WebRTC 连接成功建立")
offer = await pc.createOffer()
await pc.setLocalDescription(offer)
await signaling.send(pc.localDescription)
while True:
obj = await signaling.receive()
if isinstance(obj, RTCSessionDescription):
await pc.setRemoteDescription(obj)
print("远端描述设置完成")
elif obj is None:
print("信令结束")
break
print("关闭连接")
finally:
await pc.close()
async def main():
ip_address = "xxx.xxx.xx.xx" # 远端服务器/机器的 IP 地址
port = 9999
camera_id = 2 # 将此更改为适当的摄像头ID
await setup_webrtc_and_run(ip_address, port, camera_id)
if __name__ == "__main__":
asyncio.run(main())
接收脚本:(本地设备)
接收端脚本从发送方接收视频帧并显示这些帧。下面是receiver.py
脚本
import asyncio
import cv2
import numpy as np
from aiortc import RTCPeerConnection, RTCSessionDescription, MediaStreamTrack
from aiortc.contrib.signaling import TcpSocketSignaling
from av import VideoFrame
from datetime import datetime, timedelta
class VideoReceiver:
def __init__(self):
self.track = None
async def handle_track(self, track):
print("处理帧")
self.track = track
frame_count = 0
while True:
try:
print("等待帧...")
frame = await asyncio.wait_for(track.recv(), timeout=5.0)
frame_count += 1
print(f"接收第 {frame_count} 帧")
if isinstance(frame, VideoFrame):
print(f"帧类型: VideoFrame, pts: {frame.pts}, time_base: {frame.time_base}")
frame = frame.to_ndarray(format="bgr24")
elif isinstance(frame, np.ndarray):
print(f"帧类型: numpy 数组")
else:
print(f"意外的帧类型: {type(frame)}")
continue
# 在帧上添加时间戳
current_time = datetime.now()
new_time = current_time - timedelta(seconds=55)
timestamp = new_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
cv2.putText(frame, timestamp, (10, frame.shape[0] - 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imwrite(f"imgs/received_frame_{frame_count}.jpg", frame)
print(f"将帧 {frame_count} 保存至文件")
cv2.imshow("Frame", frame)
# 按 'q' 键退出程序
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except asyncio.TimeoutError:
print("等待帧超时,继续等待...")
except Exception as e:
print(f"handle_track 错误: {str(e)}")
if "Connection" in str(e):
break
print("退出 handle_track")
async def run(pc, signaling):
await signaling.connect()
@pc.on("track")
def on_track(track):
if isinstance(track, MediaStreamTrack):
print(f"接收 {track.kind} 类型的轨道")
asyncio.ensure_future(video_receiver.handle_track(track))
@pc.on("datachannel")
def on_datachannel(channel):
print(f"数据通道 {channel.label} 已建立")
@pc.on("connectionstatechange")
async def on_connectionstatechange():
print(f"连接状态是 {pc.connectionState}")
if pc.connectionState == "connected":
print("WebRTC 连接成功建立")
print("等待发送方的 offer...")
offer = await signaling.receive()
print("接收 offer")
await pc.setRemoteDescription(offer)
print("设置远端描述")
answer = await pc.createAnswer()
print("创建 answer")
await pc.setLocalDescription(answer)
print("设置本地描述")
await signaling.send(pc.localDescription)
print("将 answer 发回给发送方")
print("等待连接建立...")
while pc.connectionState != "connected":
await asyncio.sleep(0.1)
print("连接建立,等待帧...")
await asyncio.sleep(100) # 等待接收帧 35 秒
print("关闭连接")
async def main():
signaling = TcpSocketSignaling("192.168.30.40", 9999)
pc = RTCPeerConnection()
global video_receiver
video_receiver = VideoReceiver()
try:
await run(pc, signaling)
except Exception as e:
print(f"main 出现错误: {str(e)}")
finally:
print("关闭 peer 连接")
await pc.close()
if __name__ == "__main__":
asyncio.run(main())
接下来,运行这些脚本吧:
- 在带有摄像头的远程计算机/设备上运行发送程序命令:
运行Python脚本
python sender.py
2. 在你想要显示视频帧的机器上运行接收脚本程序。
python receiver.py
这条命令是用来运行一个名为receiver.py的Python脚本。
最后:
恭喜!您已成功建立了一个WebRTC连接,使用Python从一台机器向另一台机器实时传输摄像头画面。这个例子可以作为开发更高级应用程序的基础,例如远程监控和视频会议。您可以根据需要对此项目进行自定义和扩展。
更多资源:
WebRTC 是一个开放框架,它让浏览器支持实时通信(RTC)功能。webrtc.org 示例 - aiortc 文档提供了一些示例,这些示例非常适合初学者上手。 OpenCV: OpenCV模块库 - 编辑描述联系方式:
请联系我们一下,如有问题或意见,请您提出。
- 邮箱: malieknath135@gmail.com
- 领英个人资料: 链接
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦