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

# Quickstart

> Use SMeeting Android 2.0.39 to initialize MeetingEngine, create a meeting, register in-meeting events, enter and exit the meeting, publish the local camera and mic, and subscribe to remote video. Read this after adding the dependency.

## Prerequisites

* Add `cn.seastart.meeting:meeting:2.0.39` as described in [Integration](/en/meeting/android/integration).
* Get a `meetToken` from your backend. Don't store the secret key used to generate tokens in the client.
* Request the camera and audio recording runtime permissions in your app.
* Prepare the `streamVendor` agreed for your deployment. The example uses `wangsucdn`; the actual value depends on your backend configuration.

## 1. Create and initialize the Engine

Your app should hold a single `MeetingEngine` in one place. Callbacks stay on the thread they actually come from; switch to the main thread before any UI operation.

```kotlin theme={null}
private lateinit var meetingEngine: MeetingEngine

fun initMeeting(application: Application, meetToken: String) {
    meetingEngine = MeetingEngine.create(application)
    meetingEngine.initSdk(
        meetToken,
        null,
        object : MeetingResultCallback {
            override fun onSuccess() {
                // The SDK is ready
            }

            override fun onFailure(errorCode: Int, message: String?) {
                // message is for diagnostics only; build user-facing text from errorCode
            }
        }
    )
}
```

Release the Engine when you no longer use the SDK:

```kotlin theme={null}
fun releaseMeeting() {
    meetingEngine.release()
}
```

## 2. Create a meeting

When an instant meeting is created successfully, `MeetingCreatedBean` is returned directly; the `Data<T>` network wrapper is no longer exposed.

```kotlin theme={null}
val option = CreateImmediateMeetingOption(
    content = "Product review",
    attendType = AttendType.ATTEND_NOT_LIMIT,
    mode = MeetingMode.Normal,
    entryMutePolicy = MuteState.MuteState3
)

meetingEngine.createImmediateMeeting(
    "Weekly project sync",
    option,
    object : MeetingValueResultCallback<MeetingCreatedBean> {
        override fun onSuccess(value: MeetingCreatedBean) {
            val meetingId = value.meetingId
            val roomNo = value.roomNo
        }

        override fun onFailure(errorCode: Int, message: String?) {
            // Creation failed
        }
    }
)
```

For scheduled meetings, use `createScheduleMeeting()`, where `planTime` is a Unix timestamp in seconds and `planDur` is in minutes.

## 3. Register in-meeting events

Assign room, member, message, and media events before entering the meeting so you don't miss the initial events; they are cleared automatically after you exit the meeting.

```kotlin theme={null}
meetingEngine.roomEvent = object : MeetingRoomSimpleEvent() {
    override fun onDisconnected(
        reason: LeaveReason,
        statusCode: Int,
        message: String?
    ) {
        // The meeting is actually disconnected
    }
}

meetingEngine.userEvent = object : MeetingUserSimpleEvent() {
    override fun onUserEnter(uid: String) {
        val member = meetingEngine.infosManager.getMemberByUid(uid)
        // Refresh the member list
    }

    override fun onTrackAdded(uid: String, trackInfo: TrackInfo) {
        // Decide whether to subscribe based on trackInfo.desc
    }
}
```

For the full list of events, see [Meeting events overview](/zh/meeting/android/api-reference/meeting-events) (Chinese).

## 4. Enter and exit the meeting

A successful entry returns `MeetingEnterInfo`. In-meeting APIs are still called on `MeetingEngine`; don't reference the SDK's internal `MeetingSession`.

```kotlin theme={null}
meetingEngine.enterMeeting(
    activity = this,
    roomNo = "10000001",
    password = null,
    nick = "Alice",
    avatar = "",
    streamVendor = "wangsucdn",
    isAudience = false,
    extendInfo = null,
    callback = object : MeetingValueResultCallback<MeetingEnterInfo> {
        override fun onSuccess(value: MeetingEnterInfo) {
            val meetingId = value.meetingId
            val myUid = value.uid
            // Server-side meeting entry and SRTC join are complete
        }

        override fun onFailure(errorCode: Int, message: String?) {
            // Failed to enter the meeting
        }
    }
)
```

To enter by meeting ID, use `enterMeetingByMeetingId()`; the other parameters are the same. To exit the meeting, call:

```kotlin theme={null}
meetingEngine.exitMeeting()
```

## 5. Open and publish the camera and mic

`openCamera()` / `openMic()` only do local capture before the meeting; after entering, you must use `openCameraAndPublish()` / `openMicAndPublish()` to publish to the meeting.

```kotlin theme={null}
meetingEngine.openCameraAndPublish(
    localPreview,
    PreOptionCamera._480P,
    object : MeetingResultCallback {
        override fun onSuccess() {
            // The camera is capturing and published
        }

        override fun onFailure(errorCode: Int, message: String?) {
            // This publish and capture have been rolled back
        }
    }
)

meetingEngine.openMicAndPublish(
    PreOptionMic.def,
    object : MeetingResultCallback {
        override fun onSuccess() {
            // The mic is capturing and published
        }

        override fun onFailure(errorCode: Int, message: String?) {
            // This publish and capture have been rolled back
        }
    }
)
```

Close the devices:

```kotlin theme={null}
meetingEngine.closeCamera()
meetingEngine.closeMic()
```

Remote video must be rendered with `VcsPlayerGlTextureView` or `VcsPlayerGlSurfaceView`; a plain Android `TextureView` / `SurfaceView` doesn't work.

## 6. Subscribe to remote video

Look up the track from `InfosManager`, then get the `RemoteVideoTrack` through the asynchronous result callback.

```kotlin theme={null}
val targetUid = "remote-user-001"
val trackInfo = meetingEngine.infosManager
    .getTrackInfoByTrackDesc(targetUid, TrackDesc.TRACK_MAIN.value)
    ?: return

meetingEngine.startPlayRemoteVideo(
    uid = targetUid,
    trackDesc = trackInfo.desc,
    view = remoteView,
    event = object : MeetingRemoteVideoSimpleEvent() {
        override fun onReceiveStreamStatusChange(
            uid: String,
            trackDesc: String,
            isChoke: Boolean
        ) {
            // Update the stutter indicator
        }
    },
    callback = object : MeetingValueResultCallback<RemoteVideoTrack> {
        override fun onSuccess(value: RemoteVideoTrack) {
            // Subscribed; you can keep using value to manage the render view
        }

        override fun onFailure(errorCode: Int, message: String?) {
            // Subscription failed
        }
    }
)
```

Unsubscribe:

```kotlin theme={null}
meetingEngine.stopPlayRemoteVideo(targetUid, trackInfo.desc)
```

## Next steps

* [MeetingEngine](/zh/meeting/android/api-reference/MeetingEngine) (Chinese): all public methods, parameters, and return values
* [Model types](/zh/meeting/android/types) (Chinese): configuration and result models
* [Error codes](/en/meeting/android/error-codes): handling `202xxx` and passed-through errors
* [Camera presets](/zh/meeting/android/presets/camera) (Chinese): choosing resolution and bitrate
* [Audio routing](/zh/meeting/android/advanced/audio-routing) (Chinese): speaker, earpiece, Bluetooth, and wired headsets
