> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stmlink.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> 对外开放的服务端接口有两组前缀，都用同一套鉴权：`/server/v1/...`（SRTC 与 SMeeting 的主接口）和 `/stm/srvapi/v1/...`（SMeeting 的用户体系，服务端极简对接会用到）。鉴权是 app_id + nonce + timestamp + signature 四个请求头，用 app_key 做 HMAC-SHA256 签名，只能从业务方自己的后端调用。除这两组前缀外的接口均为内部接口，不要建议客户调用。 Public server APIs use two path prefixes with the same authentication: `/server/v1/...` (the main APIs of both SRTC and SMeeting) and `/stm/srvapi/v1/...` (the SMeeting user system, used by server-side low-code integration). Authenticate with four request headers, app_id + nonce + timestamp + signature, where signature is HMAC-SHA256 keyed with app_key; call these APIs only from the customer's own backend. Any other path is internal: never suggest calling it.
> app_key 是服务端密钥，绝不能出现在客户端代码、前端配置或移动 App 里。客户端加入频道用的 token 必须由业务方后端签发后下发（SRTC 走 `/server/v1/channel/grant`，SMeeting 走 `/stm/srvapi/v1/member/grant`）。 app_key is a server-side secret and must never appear in client code, frontend config, or a mobile app. The token a client uses to join must be issued by the customer's backend and passed down to the client (SRTC: `/server/v1/channel/grant`; SMeeting: `/stm/srvapi/v1/member/grant`).
> SRTC 与 SMeeting 是上下两层不同的产品，术语不通用：SRTC 是音视频底座，说「频道 channel」「加入 / 退出」；SMeeting 建在 SRTC 之上，说「房间 room」「会议 meeting」「进入 / 退出」。回答时按用户所在的层用对应术语，不要把「房间」「会议」安到 SRTC 的接口上，也不要用「频道」「加入 / 离开」描述 SMeeting 的概念（接口标识符原样保留）。 SRTC and SMeeting are two separate layers with different terminology. SRTC is the audio/video foundation: it has channels, and users join and leave a channel. SMeeting is built on top of SRTC: it has rooms and meetings, and members enter and exit a meeting. Answer in the terms of the layer the user is working with: never apply "room" or "meeting" to SRTC APIs, and never describe SMeeting concepts in prose with "channel", "join", or "leave" (API identifiers such as `force_join` keep their literal names).
> 同一能力在各端 SDK 里的包名、类名、方法名并不相同。写示例代码时请使用文档中该端自己的 API，不要把一个端的写法套到另一个端上。苹果平台每个产品都有两套 SDK（Swift 原生与 Objective-C），两套 API 不能混用。 Package, class, and method names differ between platform SDKs for the same capability. In sample code, use the API documented for that platform; never carry one platform's code over to another. On Apple platforms each product ships two SDKs (native Swift and Objective-C) whose APIs must not be mixed.

# Mute vs. unpublish

> Compare the lightweight enableLocalTrack / disableLocalTrack (mute) with the heavyweight publishLocalTrack / unpublishLocalTrack in the Web SDK: underlying behavior, remote events, latency, and which to use for mute buttons, stopping screen sharing, and turning off the camera.

### Overview

There are two operations of different granularity for controlling local audio and video publishing. Picking the wrong one causes unnecessary resource waste or broken behavior:

| | `enableLocalTrack` / `disableLocalTrack` | `unpublishLocalTrack` / `publishLocalTrack` |
| - | - | - |
| Acts on | A published track | The whole publishing pipeline |
| Underlying behavior | Pauses/resumes sending media data; the connection stays up | Destroys/rebuilds the publishing path |
| Effect on remote users | Remote users receive the `TRACK_MUTED` / `TRACK_UNMUTED` event | Remote users receive the `USER_TRACK_REMOVE` / `USER_TRACK_ADD` event |
| Time taken | Very fast (\< 10 ms) | Slower (requires renegotiation, \~100 ms+) |
| Typical scenario | Temporarily mute, temporarily turn off video | Stop publishing entirely (e.g., leaving the stage) |

***

### enableLocalTrack / disableLocalTrack

These two methods only control **sending of media data**; the underlying WebRTC connection stays up. They suit scenarios where you need to toggle mute/unmute quickly.

```typescript theme={null}
import { SRTC, LocalMicTrack, MicPresets } from '@seastart/srtc-web-sdk';

const srtc = new SRTC();
// Assume you have joined the channel and localMicTrack is published
let localMicTrack: LocalMicTrack;

// Create and publish the microphone track
localMicTrack = srtc.createLocalMicTrack(MicPresets.music);
await localMicTrack.startCapture();
await srtc.publishLocalTrack(localMicTrack);

// ── Temporarily mute (capture continues, publishing isn't torn down) ──────────
await srtc.disableLocalTrack(localMicTrack);
// Remote users receive the ChannelEventType.TRACK_MUTED event

// ── Unmute ────────────────────────────────────────────────────────────────────
await srtc.enableLocalTrack(localMicTrack);
// Remote users receive the ChannelEventType.TRACK_UNMUTED event
```

> **Note:** After `disableLocalTrack`, microphone capture continues (the indicator stays on); it just stops pushing data to the channel.
> If you want to stop capture entirely to release the microphone, use `unpublishLocalTrack` + `stopCapture`.

***

### unpublishLocalTrack / publishLocalTrack

Stops/restarts publishing entirely, which fires the `USER_TRACK_REMOVE` / `USER_TRACK_ADD` events on the remote side. Suitable for scenarios such as a user leaving the stage or "Stop sharing" during a call.

```typescript theme={null}
// ── Unpublish (stop publishing entirely) ──────────────────────────────────────
await srtc.unpublishLocalTrack(localMicTrack);
localMicTrack.stopCapture();
localMicTrack = undefined;

// ── Publish again (requires creating the track again) ─────────────────────────
localMicTrack = srtc.createLocalMicTrack(MicPresets.music);
await localMicTrack.startCapture();
await srtc.publishLocalTrack(localMicTrack);
```

<Warning>
  Before publishing again, make sure the previous track with the same `desc` has finished `unpublishLocalTrack`—within one channel only one track per `desc` may be published, and publishing the same `desc` again with a different track throws.

  In particular, don't let "microphone off" and "microphone on" actions interleave: if another "microphone on" is started during the await window of the previous `publishLocalTrack`, two microphone tracks publish at the same time, and you only keep a reference to the one created later. The one published first can't be unpublished and its capture can't be stopped, so the remote side keeps hearing audio. Add a serial queue or a button loading state to such entry points.
</Warning>

***

### Recommendations

* **Microphone mute button in a call** → use `disableLocalTrack` / `enableLocalTrack` for fast response and a smooth experience
* **Stop screen sharing** → use `unpublishLocalTrack` + `stopCapture` to release resources entirely
* **Temporarily turn off the camera (video goes black)** → use `disableLocalTrack`
* **Turn off the camera entirely (release the device)** → use `unpublishLocalTrack` + `stopCapture`
