feat: 集成 LiveKit 视频通话与状态指示器
- 添加 LiveKit SDK 依赖及 JitPack 仓库 - 新增 LiveKit 配置界面(URL、房间、Token、自动连接开关) - 实现 LiveKitManager 管理连接状态 - 在 MainActivity 中动态生成 Token 并处理权限申请 - 添加状态指示器(statusIndicator)实时显示 MQTT/LiveKit 连接状态 - 新增监控脚本 monitor.py 用于远程查看视频流 - 更新版本号至 2603131822
This commit is contained in:
106
monitor.py
Normal file
106
monitor.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import logging
|
||||
import asyncio
|
||||
import cv2
|
||||
import numpy as np
|
||||
from time import perf_counter
|
||||
from livekit import rtc, api
|
||||
|
||||
# 直接写死你的 API_KEY 和 API_SECRET
|
||||
API_KEY = "devkey"
|
||||
API_SECRET = "secret"
|
||||
URL = "ws://192.168.2.236:7880" # 直接使用你的 LIVEKIT_URL
|
||||
IDENTITY = "win-client"
|
||||
ROOM_NAME = "temi-room"
|
||||
|
||||
# 生成 token
|
||||
def generate_token():
|
||||
token = api.AccessToken(API_KEY, API_SECRET) \
|
||||
.with_identity(IDENTITY) \
|
||||
.with_name("Monitor Client1") \
|
||||
.with_grants(api.VideoGrants(
|
||||
room_join=True,
|
||||
room=ROOM_NAME,
|
||||
)).to_jwt()
|
||||
return token
|
||||
|
||||
async def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
room = rtc.Room()
|
||||
|
||||
try:
|
||||
# 生成连接房间的 token
|
||||
token = generate_token()
|
||||
|
||||
# 连接到 LiveKit 房间
|
||||
await room.connect(URL, token)
|
||||
logger.info("Connected to room: %s", room.name)
|
||||
|
||||
# 监听参与者连接事件
|
||||
@room.on("participant_connected")
|
||||
def on_participant_connected(participant: rtc.RemoteParticipant):
|
||||
logger.info("Participant connected: %s %s", participant.sid, participant.identity)
|
||||
|
||||
async def receive_frames(stream: rtc.VideoStream, track_sid: str):
|
||||
window_name = f"LiveKit Video Stream ({track_sid})"
|
||||
cv2.namedWindow(window_name, cv2.WINDOW_AUTOSIZE)
|
||||
last_time = perf_counter()
|
||||
frame_count = 0
|
||||
fps = 0.0
|
||||
|
||||
try:
|
||||
async for event in stream:
|
||||
frame = event.frame
|
||||
rgb_frame = frame.convert(rtc.VideoBufferType.RGB24)
|
||||
arr = np.frombuffer(rgb_frame.data, dtype=np.uint8)
|
||||
try:
|
||||
img = arr.reshape((rgb_frame.height, rgb_frame.width, 3))
|
||||
img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
||||
frame_count += 1
|
||||
now = perf_counter()
|
||||
elapsed = now - last_time
|
||||
if elapsed >= 1.0:
|
||||
fps = frame_count / elapsed
|
||||
frame_count = 0
|
||||
last_time = now
|
||||
cv2.putText(
|
||||
img_bgr,
|
||||
f"FPS: {fps:.1f}",
|
||||
(10, 30),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
1.0,
|
||||
(0, 255, 0),
|
||||
2,
|
||||
cv2.LINE_AA,
|
||||
)
|
||||
cv2.imshow(window_name, img_bgr)
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error("Error processing frame: %s", e)
|
||||
finally:
|
||||
cv2.destroyWindow(window_name)
|
||||
|
||||
# 监听 track 订阅事件
|
||||
@room.on("track_subscribed")
|
||||
def on_track_subscribed(track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant):
|
||||
logger.info("Track subscribed: %s", publication.sid)
|
||||
if track.kind == rtc.TrackKind.KIND_VIDEO:
|
||||
video_stream = rtc.VideoStream(track)
|
||||
asyncio.ensure_future(receive_frames(video_stream, publication.sid))
|
||||
|
||||
# 保持连接并处理事件
|
||||
await asyncio.Event().wait()
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to connect or handle events: %s", e)
|
||||
|
||||
finally:
|
||||
# 确保断开连接
|
||||
await room.disconnect()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("连接已被用户中断")
|
||||
Reference in New Issue
Block a user