Files
video/rtc/rtc.py

156 lines
4.6 KiB
Python
Raw Normal View History

2025-09-03 10:35:14 +08:00
import queue
2025-09-02 19:46:34 +08:00
import asyncio
import aiohttp
2025-09-03 10:35:14 +08:00
import threading
import time
2025-09-02 19:46:34 +08:00
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCConfiguration
2025-09-03 10:35:14 +08:00
from aiortc.mediastreams import MediaStreamTrack
# 创建一个长度为1的队列用于生产者和消费者之间的通信
frame_queue = queue.Queue(maxsize=1)
class VideoTrack(MediaStreamTrack):
"""自定义视频轨道类继承自MediaStreamTrack"""
kind = "video"
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
def __init__(self, max_frames=100):
super().__init__()
self.frames = queue.Queue(maxsize=max_frames)
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
async def recv(self):
return await super().recv()
def webrtc_producer(webrtc_url):
2025-09-02 19:46:34 +08:00
"""
2025-09-03 10:35:14 +08:00
生产者方法从WEBRTC读取视频帧并放入队列
仅当队列空时才放入新帧否则丢弃
2025-09-02 19:46:34 +08:00
"""
2025-09-03 10:35:14 +08:00
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
# 创建RTCPeerConnection对象不使用ICE服务器
pc = RTCPeerConnection(RTCConfiguration(iceServers=[]))
video_track = VideoTrack()
pc.addTrack(video_track)
2025-09-02 19:46:34 +08:00
@pc.on("track")
async def on_track(track):
if track.kind == "video":
2025-09-03 10:35:14 +08:00
print("接收到视频轨道,开始接收视频帧")
while True:
# 从轨道接收视频帧
2025-09-02 19:46:34 +08:00
frame = await track.recv()
2025-09-03 10:35:14 +08:00
# 转换为BGR24格式的NumPy数组
frame_bgr24 = frame.to_ndarray(format='bgr24')
# 检查队列是否为空,为空则加入,否则丢弃
if frame_queue.empty():
try:
frame_queue.put_nowait(frame_bgr24)
print("帧已放入队列")
except queue.Full:
print("队列已满,丢弃帧")
2025-09-02 19:46:34 +08:00
else:
2025-09-03 10:35:14 +08:00
print("队列非空,丢弃帧")
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
async def main():
# 创建并发送SDP Offer
offer = await pc.createOffer()
print("已创建本地SDP Offer")
await pc.setLocalDescription(offer)
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
# 发送Offer到服务器并接收Answer
async with aiohttp.ClientSession() as session:
print(f"开始向服务器 {webrtc_url} 发送SDP Offer")
2025-09-02 23:15:07 +08:00
async with session.post(
2025-09-03 10:35:14 +08:00
webrtc_url,
2025-09-02 23:15:07 +08:00
data=offer.sdp.encode(),
headers={
"Content-Type": "application/sdp",
"Content-Length": str(len(offer.sdp))
},
ssl=False
) as response:
2025-09-03 10:35:14 +08:00
print("已接收到服务器的响应")
answer_sdp = await response.text()
await pc.setRemoteDescription(RTCSessionDescription(sdp=answer_sdp, type='answer'))
# 保持连接
try:
while True:
await asyncio.sleep(0.1)
except KeyboardInterrupt:
pass
finally:
print("关闭RTCPeerConnection")
await pc.close()
2025-09-02 19:46:34 +08:00
try:
2025-09-03 10:35:14 +08:00
loop.run_until_complete(main())
2025-09-02 19:46:34 +08:00
finally:
2025-09-03 10:35:14 +08:00
loop.close()
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
def frame_consumer():
2025-09-02 19:46:34 +08:00
"""
2025-09-03 10:35:14 +08:00
消费者方法从队列中读取帧并处理
每次处理后休眠200ms模拟延迟
2025-09-02 19:46:34 +08:00
"""
2025-09-03 10:35:14 +08:00
print("消费者启动,开始等待帧...")
try:
while True:
2025-09-02 23:15:07 +08:00
# 阻塞等待队列中的帧
2025-09-03 10:35:14 +08:00
frame = frame_queue.get()
print(f"消费帧,大小: {frame.shape}")
2025-09-02 23:15:07 +08:00
2025-09-03 10:35:14 +08:00
# 模拟处理延迟
time.sleep(0.2) # 200ms
# 标记任务完成
2025-09-02 23:15:07 +08:00
frame_queue.task_done()
2025-09-03 10:35:14 +08:00
except KeyboardInterrupt:
print("消费者退出")
2025-09-02 19:46:34 +08:00
2025-09-03 10:35:14 +08:00
def start_webrtc_stream(webrtc_url):
2025-09-02 21:42:09 +08:00
"""
2025-09-03 10:35:14 +08:00
启动WebRTC视频流处理的主方法
参数: webrtc_url - WebRTC服务器地址
2025-09-02 21:42:09 +08:00
"""
2025-09-03 10:35:14 +08:00
print(f"开始连接到WebRTC服务器: {webrtc_url}")
# 启动生产者线程
producer_thread = threading.Thread(
target=webrtc_producer,
args=(webrtc_url,),
daemon=True,
name="webrtc-producer"
)
# 启动消费者线程
consumer_thread = threading.Thread(
target=frame_consumer,
daemon=True,
name="frame-consumer"
)
producer_thread.start()
consumer_thread.start()
print("生产者和消费者线程已启动")
2025-09-02 23:15:07 +08:00
2025-09-02 20:14:40 +08:00
try:
2025-09-03 10:35:14 +08:00
# 保持主线程运行
while True:
time.sleep(1)
2025-09-02 21:42:09 +08:00
except KeyboardInterrupt:
2025-09-03 10:35:14 +08:00
print("程序正在退出...")
if __name__ == "__main__":
# 示例用法
# 实际使用时替换为真实的WebRTC服务器地址
webrtc_server_url = "http://192.168.110.65:1985/rtc/v1/whep/?app=live&stream=677a4845aa48cb8526c811ad56fc5e60"
start_webrtc_stream(webrtc_server_url)