130 lines
3.3 KiB
Python
130 lines
3.3 KiB
Python
import asyncio
|
|
import requests
|
|
from livekit import rtc
|
|
|
|
import wave
|
|
import numpy as np
|
|
from livekit.rtc import AudioSource, AudioFrame, LocalAudioTrack
|
|
|
|
TOKEN_URL = "http://localhost:8000/getToken"
|
|
WS_URL = "wss://esp32-vt80c4y6.livekit.cloud" # 你的 LiveKit Server 地址
|
|
|
|
ROOM_NAME = "test-room20"
|
|
import uuid
|
|
IDENTITY = f"uv-{uuid.uuid4().hex[:6]}"
|
|
# IDENTITY = "test-user0"
|
|
|
|
|
|
def get_token():
|
|
resp = requests.get(
|
|
TOKEN_URL,
|
|
params={
|
|
"room": ROOM_NAME,
|
|
"identity": IDENTITY,
|
|
"agent_name": "my-agent", # 关键!!!
|
|
},
|
|
)
|
|
data = resp.json()
|
|
return data["token"]
|
|
|
|
|
|
async def main():
|
|
token = get_token()
|
|
|
|
room = rtc.Room()
|
|
|
|
@room.on("participant_connected")
|
|
def on_participant_connected(participant):
|
|
print(f"✅ 有人加入房间: {participant.identity}")
|
|
|
|
@room.on("participant_disconnected")
|
|
def on_participant_disconnected(participant):
|
|
print(f"❌ 有人离开房间: {participant.identity}")
|
|
|
|
print("🔌 正在连接房间...")
|
|
await room.connect(WS_URL, token)
|
|
|
|
print("✅ 已连接房间:", ROOM_NAME)
|
|
print("当前房间成员:")
|
|
for p in room.remote_participants.values():
|
|
print(" -", p.identity)
|
|
|
|
@room.on("data_received")
|
|
def on_data_received(data, participant, kind, topic):
|
|
try:
|
|
msg = data.decode()
|
|
print(f"📩 来自 {participant.identity}: {msg}")
|
|
except:
|
|
print("📩 收到二进制数据")
|
|
|
|
@room.on("track_subscribed")
|
|
def on_track_subscribed(track, publication, participant):
|
|
print(f"🎧 订阅轨道: {participant.identity}")
|
|
|
|
if track.kind == rtc.TrackKind.KIND_AUDIO:
|
|
print("👉 TTS 音频来了")
|
|
|
|
# 等一下确保连接稳定
|
|
await asyncio.sleep(1)
|
|
await room.local_participant.publish_data(
|
|
b"hello",
|
|
reliable=True,
|
|
topic="chat"
|
|
)
|
|
# 上传 wav
|
|
await publish_wav(room, "2food.wav")
|
|
|
|
await room.disconnect()
|
|
|
|
|
|
async def publish_wav(room, wav_path):
|
|
print("🎵 开始上传本地 wav:", wav_path)
|
|
|
|
wf = wave.open(wav_path, "rb")
|
|
|
|
sample_rate = wf.getframerate()
|
|
num_channels = wf.getnchannels()
|
|
sample_width = wf.getsampwidth()
|
|
|
|
print(f"📊 WAV信息: {sample_rate}Hz, {num_channels}ch, {sample_width*8}bit")
|
|
|
|
# 创建音频源
|
|
source = AudioSource(sample_rate, num_channels)
|
|
|
|
# 创建本地音轨
|
|
track = LocalAudioTrack.create_audio_track("mic", source)
|
|
|
|
# 发布轨道
|
|
await room.local_participant.publish_track(track)
|
|
print("📡 已发布音轨")
|
|
|
|
frame_duration = 0.02 # 20ms
|
|
samples_per_frame = int(sample_rate * frame_duration)
|
|
|
|
while True:
|
|
data = wf.readframes(samples_per_frame)
|
|
if not data:
|
|
break
|
|
|
|
# 用于计算长度
|
|
audio = np.frombuffer(data, dtype=np.int16)
|
|
|
|
if len(audio) == 0:
|
|
continue
|
|
|
|
samples_per_channel = len(audio) // num_channels
|
|
|
|
frame = AudioFrame(
|
|
data=data, # ✅ 关键:用 bytes
|
|
sample_rate=sample_rate,
|
|
num_channels=num_channels,
|
|
samples_per_channel=samples_per_channel,
|
|
)
|
|
|
|
await source.capture_frame(frame)
|
|
await asyncio.sleep(frame_duration)
|
|
print("✅ wav 推流结束")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main()) |