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

# C++ 快速开始

> Windows SRTC C++ 的 10 分钟最小可跑流程：初始化 IRTCEngine、创建并配置 IRTCChannel、注册回调后 join、发布摄像头/麦克风/屏幕轨道、播放远端音频、渲染远端视频、本地录制与退出。

调用顺序上有两层对象，先分清楚：

* [IRTCEngine](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCEngine.md) —— 进程级，一个就够。负责创建频道、枚举设备、IM、日志上传
* [IRTCChannel](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCChannel.md) —— 一个频道一个对象，成员查询、轨道、推拉流、本地录制都在它上面

### 初始化SDK

```cpp theme={null}
SRTC::RTCEngineOptions opt;
opt.enable_log = 1;              // 0 或者干脆不传 opt，就完全不写 SDK 日志
opt.log_path   = "D:/log/";      // 为空则用「文档/应用名/logs/」

SRTC::IRTCEngine* _irtc = nullptr;
SRTC::StatusCode ret = SRTC::RTCEngine_Init(&_irtc, &opt);
if ((ret != SRTC::StatusCode::OK) || !_irtc)
{
    return false;
}
```

日志开关和日志目录只能在这里设置，[RTCEngineOptions](https://docs.stmlink.com/zh/rtc/windows/types.md#引擎初始化参数（RTCEngineOptions）)。

### 设置引擎级消息回调

```cpp theme={null}
_irtc->setEventHandler(this);
```

这里只有**不属于任何频道**的回调：设备变化、网络探测、IM。
更多内容[查看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCEngineEvent.md)。频道级回调见下一步。

### 加入频道

分两步：`createChannel` 只创建对象**不入会**，配置好之后再 `join()`。
之所以要分两步 —— `onJoinChannel` 是在 `join()` 还没返回时就回调出去的，回调设置晚了就收不到。

```cpp theme={null}
std::string token = "";
SRTC::IRTCChannel* _ch = nullptr;
SRTC::StatusCode ret = _irtc->createChannel(token.c_str(), &_ch);
if ((ret != SRTC::StatusCode::OK) || !_ch)
{
    return ret;                  // 失败时 _ch 一定是 nullptr
}

// 1) 配置。stream_model / simple / mcu_track / enable_audio_record
//    是在 join() 阶段读取的，必须在这里设置
SRTC::IRTCChannelSetting* set = nullptr;
_ch->getSetting(&set);
set->set_stream_model(1);

// 2) 频道级回调，必须在 join() 之前
_ch->setEventHandler(this);

// 3) 真正入会。同步接口，返回前 onJoinChannel 已经回调完毕
ret = _ch->join();
if (ret != SRTC::StatusCode::OK)
{
    _irtc->leaveChannel(_ch->getChannelId());   // 失败的频道对象也要回收
    _ch = nullptr;
    return ret;
}
```

更多参数设置[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCChannelSetting.md)，频道级回调[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCChannelEvent.md)。

### 开关摄像头

```cpp theme={null}
    SRTC::IRTCLocalCameraTrack* _camStream = nullptr;
    const char* trackKey = "camera";
    SRTC::StatusCode ret = _ch->getCameraTrack(trackKey, &_camStream);
    if ((ret != SRTC::StatusCode::OK) || !_camStream)
    {
        return ret;
    }
    _camStream->addPlayView(callbackView);
    _camStream->startCapture();

    SRTC::RTCVideoPublishOptions* opt = nullptr;   // nullptr 表示用默认推流参数
    _ch->publish(_camStream, opt);
```

```cpp theme={null}
    SRTC::IRTCLocalCameraTrack* _camStream = nullptr;
    const char* trackKey = "camera";
    SRTC::StatusCode ret = _ch->getCameraTrack(trackKey, &_camStream);
    if ((ret != SRTC::StatusCode::OK) || !_camStream)
    {
        return ret;
    }

    _camStream->stopCapture();
    _camStream->removeAllPlayView();
    _ch->unpublish(_camStream);
```

> [!WARNING]
>
> 两个 `publish` 重载不带默认参数，`opt` 必须显式传。而且不能直接写 `publish(tk, nullptr)` ——
> 视频/音频两个重载会二义，要像上面那样用一个**带类型的**空指针变量。

更多本地摄像头方法[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCLocalCameraTrack.md)

### 开关麦克风

```cpp theme={null}
    SRTC::IRTCLocalMicTrack* _micStream = nullptr;
    const char* trackKey = "mic";
    SRTC::StatusCode ret = _ch->getAudioTrack(trackKey, &_micStream);
    if ((ret != SRTC::StatusCode::OK) || !_micStream)
    {
        return ret;
    }
    _micStream->startCapture();

    SRTC::RTCAudioPublishOptions* opt = nullptr;
    _ch->publish(_micStream, opt);
```

```cpp theme={null}
    SRTC::IRTCLocalMicTrack* _micStream = nullptr;
    const char* trackKey = "mic";
    SRTC::StatusCode ret = _ch->getAudioTrack(trackKey, &_micStream);
    if ((ret != SRTC::StatusCode::OK) || !_micStream)
    {
        return ret;
    }

    _micStream->stopCapture();
    _ch->unpublish(_micStream);
```

更多本地麦克风方法[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCLocalMicTrack.md)

### 开关桌面共享

```cpp theme={null}
    SRTC::IRTCLocalScreenTrack* _screenStream = nullptr;
    const char* trackKey = "screen";
    SRTC::StatusCode ret = _ch->getScreenTrack(trackKey, &_screenStream);
    if ((ret != SRTC::StatusCode::OK) || !_screenStream)
    {
        return ret;
    }
    _screenStream->startCapture();

    SRTC::RTCVideoPublishOptions* opt = nullptr;
    _ch->publish(_screenStream, opt);
```

```cpp theme={null}
    SRTC::IRTCLocalScreenTrack* _screenStream = nullptr;
    const char* trackKey = "screen";
    SRTC::StatusCode ret = _ch->getScreenTrack(trackKey, &_screenStream);
    if ((ret != SRTC::StatusCode::OK) || !_screenStream)
    {
        return ret;
    }

    _screenStream->stopCapture();
    _ch->unpublish(_screenStream);
```

更多本地桌面共享方法[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCLocalScreenTrack.md)

### 开关扬声器

```cpp theme={null}
    SRTC::IRTCRemoteAudioTrack* _speakerStream = nullptr;
    SRTC::StatusCode ret = _ch->getRemoteAudioTrack("", "", &_speakerStream);
    if ((ret != SRTC::StatusCode::OK) || !_speakerStream)
    {
        return ret;
    }
    _speakerStream->startPlay(nullptr);
```

```cpp theme={null}
    SRTC::IRTCRemoteAudioTrack* _speakerStream = nullptr;
    SRTC::StatusCode ret = _ch->getRemoteAudioTrack("", "", &_speakerStream);
    if ((ret != SRTC::StatusCode::OK) || !_speakerStream)
    {
        return ret;
    }

    _speakerStream->stopPlay();
```

更多本地扬声器方法[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCRemoteAudioTrack.md)

### 显示关闭远端视频流

```cpp theme={null}
    SRTC::IRTCRemoteVideoTrack* _remoteStream = nullptr;
    std::string uid = "";              //用户uid
    std::string stream_track_id = "";  //用户stream_tracks 对应的流id
    SRTC::StatusCode ret = _ch->getRemoteVideoTrack(uid.c_str(), stream_track_id.c_str(), &_remoteStream);
    if ((ret != SRTC::StatusCode::OK) || !_remoteStream)
    {
        return ret;
    }

    SRTC::RTCHwndView view(hwnd);      // 或者自己实现 IRTCView
    _remoteStream->addPlayView(&view);
    _ch->subscribe(_remoteStream);
```

```cpp theme={null}
    SRTC::IRTCRemoteVideoTrack* _remoteStream = nullptr;
    std::string uid = "";              //用户uid
    std::string stream_track_id = "";  //用户stream_tracks 对应的流id
    SRTC::StatusCode ret = _ch->getRemoteVideoTrack(uid.c_str(), stream_track_id.c_str(), &_remoteStream);
    if ((ret != SRTC::StatusCode::OK) || !_remoteStream)
    {
        return ret;
    }

    _remoteStream->removeAllPlayView();
    _ch->unsubscribe(_remoteStream);
```

> [!NOTE]
>
> `addPlayView` 收的是 [IRTCView\*](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCView.md)。窗口渲染可以直接用头文件里现成的
> `SRTC::RTCHwndView`，注意它的生命周期要覆盖到 `removeAllPlayView` 之后。

### 开关本地录制

```cpp theme={null}
    SRTC::IRTCRecord* _rc = nullptr;
    SRTC::StatusCode ret = _ch->getLocalRecord("", &_rc);
    if ((ret != SRTC::StatusCode::OK) || !_rc)
    {
        return ret;      // 未加入频道或当前流媒体模式不支持录制时返回错误
    }
    //录制指定窗口
    HWND hwnd = (HWND)this->winID();
    _rc->setRecordHwnd(hwnd, 0, 0, 0, 0);
    //录制画面配置
    //_rc->setRecordLayoutMemberView(dt.c_str(), dt.size());
    _rc->startRecord();
```

```cpp theme={null}
    SRTC::IRTCRecord* _rc = nullptr;
    _ch->getLocalRecord("", &_rc);
    _rc->stopRecord();
```

每个频道只有**一个**录制对象，`mid` 参数仅作接口兼容保留，传空串即可。
更多本地录制方法[参看](https://docs.stmlink.com/zh/rtc/windows/api-reference/IRTCRecord.md)

### 退出频道

```cpp theme={null}
_irtc->leaveChannel(_ch->getChannelId());
_ch = nullptr;      // 对象已销毁，必须自己置空
```

`IRTCChannel` 自身没有 `leave()`。要一次退掉全部频道用 `_irtc->leaveAllChannel()`，
之后所有 `IRTCChannel*` 一并失效。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.