> ## 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.

# Subscribing and receiving

> C SDK functions for subscribing to remote audio and video tracks (auto-subscribe vs. manual subscription in the track event callback), unsubscribing, and rtc_request_key_frame, including when you should and shouldn't request a keyframe.

There are two ways to subscribe:

* **Auto-subscribe**—call `rtc_set_auto_subscribe` before joining, and the SDK subscribes to every remote track automatically. Use this for "take everything" scenarios such as recording, audio mixing, and AI integration
* **Manual subscription**—pick the tracks you need in the track event callback and subscribe to them one by one

Either way, media data goes through the same `rtc_set_track_sample_callback` callback.

***

## rtc\_subscribe\_audio

```c theme={null}
int rtc_subscribe_audio(void* handle, const char* uid, const char* track_id);
```

Subscribes to a specific user's audio track.

| Parameter | Description |
| - | - |
| `uid` | Publisher uid. Pass `RTC_MCU_PUBLISHER_UID` to subscribe to the audio composite stream |
| `track_id` | Track ID. Pass `RTC_TRACK_AMCU_ID` to subscribe to the audio composite stream |

**Returns**

| Return value | Meaning |
| - | - |
| `RTC_OK` | Subscribed successfully |
| `RTC_INVALID_PARAM` | Invalid handle |
| `RTC_NOT_CONNECTED` | Not yet joined to the channel |
| `RTC_ERROR` | Subscribe failed (track doesn't exist, rejected by the engine, etc.) |

## rtc\_subscribe\_video

```c theme={null}
int rtc_subscribe_video(void* handle, const char* uid, const char* track_id);
```

Subscribes to a specific user's video track. Parameters and return values are the same as `rtc_subscribe_audio`; to subscribe to the video composite stream, pass `RTC_MCU_PUBLISHER_UID` / `RTC_TRACK_MCU_ID`.

<Note>
  There's no separate function for subscribing to the composite stream (program); use the two general functions above with the reserved constants. See [Composite stream (program)](/en/rtc/capi/advanced/mcu).
</Note>

## rtc\_unsubscribe

```c theme={null}
int rtc_unsubscribe(void* handle, const char* uid, const char* track_id);
```

Unsubscribes. For a composite stream, pass the publisher uid plus the corresponding reserved `track_id`. Return values are the same as above.

After you unsubscribe, the SDK automatically stops collecting data for that track, and the corresponding `track_sample` callbacks stop.

***

## Manual subscription example

```c theme={null}
static void* g_rtc = NULL;

// Pick the tracks to subscribe to in the track event
static void on_track_event(void* ctx, const char* uid,
                           rtc_track_info_t* track, int event_type) {
    if (event_type != 0) return;   // Only handle "added"

    // Only subscribe to the target user's video
    if (strcmp(uid, "target_user_id") == 0 && track->kind == 1) {
        rtc_subscribe_video(g_rtc, uid, track->track_id);
    }
    // Subscribe to everyone's audio
    if (track->kind == 0) {
        rtc_subscribe_audio(g_rtc, uid, track->track_id);
    }
}

int main() {
    g_rtc = rtc_create();
    rtc_set_track_event_callback(g_rtc, on_track_event, NULL);
    rtc_set_track_sample_callback(g_rtc, on_track_sample, NULL);
    // Auto-subscribe is not turned on
    rtc_join_channel_sync(g_rtc, token, 10000);
    // ...
}
```

***

## rtc\_request\_key\_frame

```c theme={null}
int rtc_request_key_frame(void* handle, const char* uid, const char* track_id);
```

Asks a remote video track to send a keyframe immediately (sends RTCP PLI to the publisher).

| Parameter | Description |
| - | - |
| `uid` | Publisher uid |
| `track_id` | ID of a subscribed video track |

**Returns**

| Return value | Meaning |
| - | - |
| `RTC_OK` | Request sent |
| `RTC_INVALID_PARAM` | Invalid handle, an empty parameter, or the track doesn't support it (not video / not ready) |
| `RTC_NOT_CONNECTED` | Not yet joined to the channel |

**When to call it**

* You just subscribed to a video track and want video to appear as soon as possible
* The decoder reports errors, or the video is corrupted or gray, and you need to refresh the reference frame

**When not to call it**

<Note>
  **You don't need to request keyframes for packet loss.** Light packet loss is recovered automatically by the SDK's NACK retransmission and doesn't corrupt the video. So the SDK doesn't request keyframes based on packet loss rate, and we don't recommend that you do—it only triggers unnecessary keyframes, which push up the bitrate and make congestion worse.

  Only call it when **your decoder actually reports an error** or the video is already corrupted.

  The function has built-in rate limiting (at most once per second), so calling it repeatedly on per-frame decode errors won't flood requests.
</Note>

***

## Related

* How the publisher responds to keyframe requests: [Publishing](/en/rtc/capi/api-reference/publish)
* Multi-layer simulcast subscription and manual layer switching: [SeaStart advanced features](/en/rtc/capi/advanced/seastart)
