Metadata-Version: 2.5
Name: srtc
Version: 0.1.0
Summary: SRTC Python SDK - 面向服务端 AI 场景（ASR/LLM/TTS）的实时音视频 SDK
Project-URL: Homepage, https://www.stmlink.com
Project-URL: Documentation, https://docs.stmlink.com
Author-email: Seastart <dev@seastart.cn>
License-Expression: LicenseRef-Proprietary
Keywords: ai,asr,audio,realtime,rtc,tts,webrtc
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Communications :: Conferencing
Classifier: Topic :: Multimedia :: Sound/Audio
Requires-Python: >=3.10
Requires-Dist: av>=12.0
Requires-Dist: cffi>=1.15
Requires-Dist: numpy>=1.24
Provides-Extra: dev
Requires-Dist: httpx>=0.25; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Provides-Extra: pipecat
Requires-Dist: pipecat-ai>=1.0; extra == 'pipecat'
Description-Content-Type: text/markdown

# SRTC Python SDK

[SRTC](https://www.stmlink.com) 是一款支持完全私有化部署的实时音视频通信引擎，覆盖全平台 SDK，支持信创国产化，
兼容传统 SIP/H.323 设备，集成 AI Agent 能力。

本 SDK 面向**服务端 AI 场景**（ASR → LLM → TTS 语音 agent、录音、质检），使用帮助见 [SRTC 文档中心](https://docs.stmlink.com)。

- **收发都是 PCM**：远端音频解码成指定采样率的 PCM 直接喂 ASR；TTS 输出的 PCM 写进去即可，编解码、重采样都在 SDK 内。
- **SDK 控发送节奏**：TTS 生成再快，也按实时节奏每 20ms 发一帧；`clear()` 支持用户插话打断。
- **时间轴连续**：对端 DTX 静音不发包时，SDK 按 RTP 时间戳补静音帧，录音时长、VAD 断句都不会错位。
- **asyncio 原生**：`async with` 自动离开、`async for` 逐帧消费、事件回调可以是 async 函数。
- **原生内核**：信令、断线重连、音视频收发由 SDK 内置的原生库完成，与 SRTC 其它端行为一致，不占 Python GIL。
- **接入 pipecat**：内置 pipecat transport，替换一行即可把现有语音 agent 管线接进 SRTC 频道。

## 安装

```sh
pip install srtc               # 核心
pip install "srtc[pipecat]"    # 同时安装 pipecat 集成
```

支持 Python 3.10+，平台：Linux x86_64 / aarch64（glibc 2.17+）、macOS Apple Silicon、Windows x64。
依赖只有 `cffi`、`av`（自带 ffmpeg 与 libopus，无需系统安装 ffmpeg）、`numpy`。

## 快速开始

```python
import asyncio
import srtc

async def main(token: str):
    async with await srtc.Channel.join(
        token,
        auto_subscribe_audio=True,                  # 自动订阅所有人（含后加入者）的音频
        audio_format=srtc.AudioFormat(16000, 1),    # 收到的 PCM 格式，默认就是 16k 单声道
    ) as ch:
        tts = await ch.publish_audio(desc="tts", audio_format=srtc.AudioFormat(24000, 1))

        async for frame in ch.audio_frames():       # 所有已订阅轨道的音频，按 frame.uid 区分说话人
            text = asr.feed(frame.uid, frame.pcm)   # frame.to_numpy() 得到 int16 数组
            if text:
                async for pcm in tts_engine.synthesize(await llm.chat(text)):
                    await tts.write(pcm)            # 缓冲超上限时 write 会等待（背压）

asyncio.run(main(token))
```

token 由业务服务端调 srvapi `channel/grant` 签发。体验时可用 demo 接口，见 `examples/demo_utils.py`。

### 事件回调

继承 `ChannelHandler`，覆写需要的方法（普通函数或 async 函数都行，都在事件循环线程里调用）：

```python
class Agent(srtc.ChannelHandler):
    def on_user_join(self, user: srtc.UserInfo): ...
    def on_user_leave(self, uid: str): ...
    def on_track_added(self, track: srtc.TrackInfo): ...       # 未开自动订阅时在这里 subscribe_audio
    def on_audio_frame(self, frame: srtc.AudioFrame): ...      # 高频，尽快返回
    def on_active_speakers(self, speakers): ...
    def on_custom_msg(self, msg: srtc.CustomMsg): ...
    async def on_disconnected(self, reason: srtc.DisconnectReason, error): ...

ch = await srtc.Channel.join(token, handler=Agent())
```

> 用户/轨道类事件在底层各自异步投递，**彼此之间不保证先后顺序**（如某用户的 `on_track_added` 可能先于 `on_user_join`）。

### 发送音频（TTS）

```python
tts = await ch.publish_audio(desc="tts", audio_format=srtc.AudioFormat(24000, 1), max_buffer_seconds=30)
await tts.write(pcm)            # 任意长度，SDK 切 20ms 帧按实时节奏发送
tts.clear()                     # 用户插话：丢弃未播完的部分
await tts.wait_for_playout()    # 等待已写入的全部发完
tts.buffered_seconds            # 还剩多少没发
await ch.unpublish(tts)
```

空闲时 SDK 持续发静音帧，保持 RTP 时间戳与墙钟同步，下一句话不会被对端当成迟到包。

### 收视频（可选）

```python
ch = await srtc.Channel.join(token, auto_subscribe_video=True, decode_video=True)
async for f in ch.video_frames():
    f.image         # RGB24 numpy 数组 (h, w, 3)；decode_video=False 时为 None，只有 f.encoded
```

解码出错时 SDK 自动向发布端请求关键帧。本期只收不发视频。

### 其它接口

| 接口 | 说明 |
|---|---|
| `ch.me` / `ch.info` / `ch.users` / `ch.get_user(uid)` | 本端、频道、在线用户信息（本地缓存，不走网络） |
| `await ch.subscribe_audio(uid, track_id)` / `subscribe_video` / `unsubscribe` | 手动订阅；合成流传 `MCU_PUBLISHER_UID` + `TRACK_AMCU_ID`/`TRACK_MCU_ID` |
| `ch.connection_quality` / `on_connection_quality` | 上下行网络质量（SFU 约 1Hz 上报） |
| `await ch.wait_closed()` | 等待频道断开（被踢、顶号、频道销毁…），返回 `DisconnectReason` |
| `ch.request_key_frame` / `await ch.switch_layer` | 视频关键帧请求 / simulcast 切层 |

**客户端只收不发消息**：频道内自定义消息、改用户信息都走服务端 srvapi（`channel/send-custom-msg`、`channel/update-user`）。

### 错误

失败抛 `srtc.SdkError`，`code` 与各端 SDK 一致：`180xxx` 为 SDK 自身错误，≥1000 为后端错误码原样透传
（如 `1033` 并发已达上限、`1035` 节点满载），`msg` 为原因。

## 接入 pipecat

`pip install "srtc[pipecat]"`，用 `SRTCTransport` 替换 pipecat 示例里的 Daily / LiveKit transport 即可：

```python
from srtc.pipecat_transport import SRTCParams, SRTCTransport

transport = SRTCTransport(token, SRTCParams(audio_in_enabled=True, audio_out_enabled=True))

@transport.event_handler("on_first_participant_joined")
async def on_joined(transport, uid):
    await task.queue_frame(TTSSpeakFrame("你好，我是 AI 助手"))

pipeline = Pipeline([transport.input(), stt, context_aggregator.user(), llm, tts,
                     transport.output(), context_aggregator.assistant()])
```

- 输入：自动订阅频道内所有人的音频，以 `UserAudioRawFrame(user_id=uid)` 推出。
- 输出：发布一路音频；`InterruptionFrame` 到达时立即清掉未发出的音频（插话打断）。
- 事件：`on_connected` / `on_disconnected` / `on_first_participant_joined` / `on_participant_connected` /
  `on_participant_disconnected` / `on_custom_msg`。
- `transport.channel` 是底层 `srtc.Channel`，需要更多能力（查用户、订阅视频）时直接用。

## 部署注意

- **不要在已使用 SDK 的进程里 fork**：Go runtime 在子进程中不可用。只 import、未创建过 Channel 就 fork 没问题（gunicorn `--preload` 可用）；fork 后再用会直接报错而不是挂死。multiprocessing 请用 `spawn`。
- **容量参考**（Apple M 系列单进程实测，每个 Channel 收 1 路 + 发 1 路音频）：20 个 Channel 约 61% 单核 CPU、181MB 内存、零丢帧，事件循环 p99 调度延迟 1.4ms。Python 侧编解码约占每路 1.75% CPU，是单进程的上限所在，更多会话请多进程横向扩展。

## License

专有软件，版权归 Seastart 所有。使用需获得 SRTC 服务授权，未经许可不得复制、修改或再分发。
SRTC 提供免费版（并发量受限），超出限额的并发与私有化部署需购买商业授权，详见 [官网](https://www.stmlink.com)。
