@tencent-rtc/mcp 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +4 -4
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/resource/doc/chatuikit/android/features/theme.md +363 -0
- package/resource/doc/chatuikit/{android/client_api_Android.md → faq/android_sdk/client-api.md} +0 -2
- package/resource/doc/chatuikit/faq/common/product.md +8 -0
- package/resource/doc/chatuikit/faq/flutter_sdk/faq.md +53 -0
- package/resource/doc/chatuikit/{iOS/client_api_iOS.md → faq/ios_sdk/client-api.md} +0 -2
- package/resource/doc/chatuikit/flutter/features/theme.md +379 -0
- package/resource/doc/chatuikit/flutter/integration.md +413 -0
- package/resource/doc/chatuikit/flutter/workflow.md +941 -0
- package/resource/doc/chatuikit/iOS/features/theme.md +416 -0
- package/resource/doc/chatuikit/react/components/chat-setting.md +86 -0
- package/resource/doc/chatuikit/react/components/contact-list.md +1093 -0
- package/resource/doc/chatuikit/react/components/search.md +745 -0
- package/resource/doc/chatuikit/vue/components/chat-setting.md +64 -0
- package/resource/doc/chatuikit/vue/components/contact-list.md +679 -0
- package/resource/doc/chatuikit/vue/components/search.md +1384 -0
- package/resource/doc/livekit/live/android/workflow.md +137 -6
- package/resource/doc/livekit/live/ios/live-audience-list.md +339 -0
- package/resource/doc/livekit/live/ios/live-audio-effect.md +370 -0
- package/resource/doc/livekit/live/ios/live-barrage.md +517 -0
- package/resource/doc/livekit/live/ios/live-base-beauty.md +247 -0
- package/resource/doc/livekit/live/ios/live-co-guest-interaction.md +518 -0
- package/resource/doc/livekit/live/ios/live-gift.md +783 -0
- package/resource/doc/livekit/live/ios/live-host-interaction.md +815 -0
- package/resource/doc/livekit/live/ios/live-integration.md +931 -0
- package/resource/doc/livekit/live/ios/live-live-list.md +485 -0
- package/resource/doc/livekit/live/ios/workflow.md +454 -0
- package/resource/doc/rtcengine/web/plugin/20-advanced-customized-capture-rendering.md +87 -0
- package/resource/doc/rtcengine/web/plugin/21-advanced-auto-play-policy.md +87 -0
- package/resource/doc/rtcengine/web/plugin/22-advanced-audio-mixer.md +175 -0
- package/resource/doc/rtcengine/web/plugin/23-advanced-support-detection.md +221 -0
- package/resource/doc/rtcengine/web/plugin/24-advanced-network-quality.md +140 -0
- package/resource/doc/rtcengine/web/plugin/25-advanced-device-change.md +99 -0
- package/resource/doc/rtcengine/web/plugin/26-advanced-publish-cdn-stream.md +451 -0
- package/resource/doc/rtcengine/web/plugin/27-advanced-small-stream.md +151 -0
- package/resource/doc/rtcengine/web/plugin/28-advanced-beauty.md +322 -0
- package/resource/doc/rtcengine/web/plugin/29-advanced-water-mark.md +149 -0
- package/resource/doc/rtcengine/web/plugin/30-advanced-cross-room-link.md +96 -0
- package/resource/doc/rtcengine/web/plugin/31-advanced-plugins-overview.md +330 -0
- package/resource/doc/rtcengine/web/plugin/34-advanced-proxy.md +248 -0
- package/resource/doc/rtcengine/web/plugin/35-advanced-ai-denoiser.md +81 -0
- package/resource/doc/rtcengine/web/plugin/36-advanced-virtual-background.md +242 -0
- package/resource/doc/rtcengine/web/plugin/37-advanced-voice-changer.md +99 -0
- package/resource/doc/rtcengine/web/plugin/39-advanced-video-decoder.md +33 -0
- package/resource/doc/rtcengine/web/plugin/40-advanced-video-mixer.md +379 -0
- package/resource/doc/rtcengine/web/plugin/41-advanced-small-stream-auto-switcher.md +91 -0
- package/resource/doc/rtcengine/web/plugin/42-advanced-realtime-transcriber.md +315 -0
- package/resource/doc/rtcengine/web/plugin/43-advanced-face-detection.md +180 -0
- package/resource/doc/tuicallkit/android/workflow.md +0 -0
- package/resource/doc/tuicallkit/flutter/workflow.md +331 -0
- package/tools/call/get-native-call-integration.js +27 -11
- package/tools/call/get-native-call-integration.js.map +1 -1
- package/tools/chat/get-native-chat-feature-detail.js +30 -0
- package/tools/chat/get-native-chat-feature-detail.js.map +1 -0
- package/tools/chat/get-native-chat-integration.js +30 -12
- package/tools/chat/get-native-chat-integration.js.map +1 -1
- package/tools/common/get-faq.js +2 -4
- package/tools/common/get-faq.js.map +1 -1
- package/tools/definitions/get_native_call_uikit_integration_def.js +17 -7
- package/tools/definitions/get_native_call_uikit_integration_def.js.map +1 -1
- package/tools/definitions/get_native_chat_feature_detail_def.js +32 -0
- package/tools/definitions/get_native_chat_feature_detail_def.js.map +1 -0
- package/tools/definitions/get_native_chat_uikit_integration_def.js +19 -7
- package/tools/definitions/get_native_chat_uikit_integration_def.js.map +1 -1
- package/tools/definitions/{get_native_live_extra_features_def.js → get_native_live_uikit_extra_features_def.js} +3 -3
- package/tools/definitions/get_native_live_uikit_extra_features_def.js.map +1 -0
- package/tools/definitions/{get_native_live_interaction_def.js → get_native_live_uikit_interaction_def.js} +3 -3
- package/tools/definitions/get_native_live_uikit_interaction_def.js.map +1 -0
- package/tools/definitions/get_web_chat_uikit_component_detail_def.js +49 -10
- package/tools/definitions/get_web_chat_uikit_component_detail_def.js.map +1 -1
- package/tools/index.js +6 -4
- package/tools/index.js.map +1 -1
- package/tools/live/{get_native_live_extra_features.js → get_native_live_uikit_extra_features.js} +4 -4
- package/tools/live/get_native_live_uikit_extra_features.js.map +1 -0
- package/tools/live/{get_native_live_interaction.js → get_native_live_uikit_interaction.js} +4 -4
- package/tools/live/get_native_live_uikit_interaction.js.map +1 -0
- package/tools/rtcengine/get-rtcengine-web-sdk-integration.js.map +1 -1
- package/tools/rtcengine/get-trtc-sdk-faq.js +32 -0
- package/tools/rtcengine/get-trtc-sdk-faq.js.map +1 -0
- package/tools/rtcengine/get-trtc-web-sdk-faq.js +1 -1
- package/tools/rtcengine/get-trtc-web-sdk-integration.js.map +1 -1
- package/tools/rtcengine/get-trtc-web-sdk-plugin.js +45 -0
- package/tools/rtcengine/get-trtc-web-sdk-plugin.js.map +1 -0
- package/utils/handle-component-name.js +0 -16
- package/utils/handle-component-name.js.map +1 -1
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
- package/resource/doc/chatuikit/android/generate_UserSig.md +0 -306
- package/resource/doc/chatuikit/android/integrarte_only_tuichat_Android.md +0 -290
- package/resource/doc/chatuikit/android/quick_start_Android.md +0 -138
- package/resource/doc/chatuikit/android/show_typing_Android.md +0 -43
- package/resource/doc/chatuikit/flutter/generate_UserSig.md +0 -306
- package/resource/doc/chatuikit/flutter/integrate_tuikit_Flutter.md +0 -133
- package/resource/doc/chatuikit/iOS/generate_UserSig.md +0 -306
- package/resource/doc/chatuikit/iOS/integrate_only_tuichat_iOS.md +0 -748
- package/resource/doc/chatuikit/iOS/quick_start_iOS.md +0 -163
- package/resource/doc/chatuikit/iOS/show_typing_iOS.md +0 -52
- package/resource/doc/tuicallkit/flutter/how_to_run_flutter_callkit_demo.md +0 -101
- package/resource/doc/tuicallkit/iOS/how_to_run_iOS_callkit_demo.md +0 -98
- package/tools/definitions/get_native_live_extra_features_def.js.map +0 -1
- package/tools/definitions/get_native_live_interaction_def.js.map +0 -1
- package/tools/live/get_native_live_extra_features.js.map +0 -1
- package/tools/live/get_native_live_interaction.js.map +0 -1
- /package/resource/doc/chatuikit/android/{integrate_tuikit_Android.md → integration.md} +0 -0
- /package/resource/doc/chatuikit/{faq/native_sdk/faq.md → android/workflow.md} +0 -0
- /package/resource/doc/chatuikit/{flutter/client_api_Flutter.md → faq/flutter_sdk/client-api.md} +0 -0
- /package/resource/doc/chatuikit/iOS/{integrate_tuikit_iOS.md → integration.md} +0 -0
- /package/resource/doc/{tuicallkit/android/.gitkeep → chatuikit/iOS/workflow.md} +0 -0
- /package/resource/doc/chatuikit/react/components/{chatheader.md → chat-header.md} +0 -0
- /package/resource/doc/chatuikit/vue/components/{chatheader.md → chat-header.md} +0 -0
- /package/resource/doc/tuicallkit/android/{how_to_integrated_android_callkit.md → integration.md} +0 -0
- /package/resource/doc/tuicallkit/{android/api_TUICallObserver.md → faq/android_sdk/call-observer-api.md} +0 -0
- /package/resource/doc/tuicallkit/{android/api_TUICallKit.md → faq/android_sdk/callkit-api.md} +0 -0
- /package/resource/doc/tuicallkit/{flutter/api_TUICallDefine.md → faq/flutter_sdk/call-define-api.md} +0 -0
- /package/resource/doc/tuicallkit/{flutter/api_TUICallObserver.md → faq/flutter_sdk/call-observer-api.md} +0 -0
- /package/resource/doc/tuicallkit/{flutter/api_TUICallKit.md → faq/flutter_sdk/callkit-api.md} +0 -0
- /package/resource/doc/tuicallkit/{iOS/api_TUICallObserver.md → faq/ios_sdk/call-observer-api.md} +0 -0
- /package/resource/doc/tuicallkit/{iOS/api_TUICallKit.md → faq/ios_sdk/callkit-api.md} +0 -0
- /package/resource/doc/tuicallkit/flutter/{how_to_integrated_flutter_callkit.md → integration.md} +0 -0
- /package/resource/doc/tuicallkit/iOS/{how_to_integrated_iOS_callkit.md → integration.md} +0 -0
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
This document guides you through building a basic live streaming app with host broadcasting and audience viewing capabilities using the core component [LiveCoreView](https://tencent-rtc.github.io/TUIKit_Android/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-live-core-view/index.html) from the **AtomicXCore SDK**.
|
|
2
|
+
|
|
3
|
+
### Core Features
|
|
4
|
+
|
|
5
|
+
**LiveCoreView** is a lightweight `View` component purpose-built for live streaming scenarios. It serves as the foundation of your live streaming implementation, encapsulating all complex streaming technologies—including stream publishing and playback, co-hosting, and audio/video rendering. Use LiveCoreView as the "canvas" for your live video, enabling you to focus on developing your custom UI and interactions.
|
|
6
|
+
|
|
7
|
+
The following view hierarchy diagram illustrates the position and role of **LiveCoreView** within the live streaming interface:
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Core Concepts
|
|
13
|
+
<table>
|
|
14
|
+
<tr>
|
|
15
|
+
<td rowspan="1" colSpan="1" >**Core Concept**</td>
|
|
16
|
+
|
|
17
|
+
<td rowspan="1" colSpan="1" >**Core Responsibility**</td>
|
|
18
|
+
|
|
19
|
+
<td rowspan="1" colSpan="1" >**Key API / Property**</td>
|
|
20
|
+
</tr>
|
|
21
|
+
|
|
22
|
+
<tr>
|
|
23
|
+
<td rowspan="1" colSpan="1" >LiveCoreView</td>
|
|
24
|
+
|
|
25
|
+
<td rowspan="1" colSpan="1" >Handles publishing, playing, and rendering audio/video streams. Provides adapter interfaces for integrating custom UI components (e.g., user info, PK progress bar).</td>
|
|
26
|
+
|
|
27
|
+
<td rowspan="1" colSpan="1" >- `viewType`:<br> - `.pushView` (host publishing view)<br> - `.playView` (audience playback view)<br>- `setLiveId()`: Binds the live room ID for this view.<br>- `videoViewDelegate`: Adapter for custom video display UI slots.</td>
|
|
28
|
+
</tr>
|
|
29
|
+
|
|
30
|
+
<tr>
|
|
31
|
+
<td rowspan="1" colSpan="1" >LiveListStore</td>
|
|
32
|
+
|
|
33
|
+
<td rowspan="1" colSpan="1" >Manages the complete live room lifecycle (create, join, leave), synchronizes state, and listens for passive events (e.g., live ended, kicked out).</td>
|
|
34
|
+
|
|
35
|
+
<td rowspan="1" colSpan="1" >- `createLive()`: Start live stream as host.<br>- `endLive()`: End live stream as host.<br>- `joinLive()`: Audience joins live room.<br>- `leaveLive()`: Leave live room.</td>
|
|
36
|
+
</tr>
|
|
37
|
+
|
|
38
|
+
<tr>
|
|
39
|
+
<td rowspan="1" colSpan="1" >LiveInfo</td>
|
|
40
|
+
|
|
41
|
+
<td rowspan="1" colSpan="1" >Defines room parameters before going live, such as room ID, seat layout mode, max co-hosts, etc.</td>
|
|
42
|
+
|
|
43
|
+
<td rowspan="1" colSpan="1" >- `liveID`: Unique room identifier.<br>- `seatLayoutTemplateID`: Layout template ID (e.g., 600 for dynamic grid).</td>
|
|
44
|
+
</tr>
|
|
45
|
+
</table>
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Preparation
|
|
49
|
+
|
|
50
|
+
### Step 1: Activate the Service
|
|
51
|
+
|
|
52
|
+
See [Activate Service](https://write.woa.com/document/141936672617742336) to obtain either the trial or paid version of the SDK.Then, go to [the Console](https://console.trtc.io/app) for application management, and get the following:
|
|
53
|
+
- `SDKAppID`: Application identifier (required). Tencent Cloud uses `SDKAppID` for billing and details.
|
|
54
|
+
|
|
55
|
+
- `SDKSecretKey`: Application secret key, used to initialize the configuration file with secret information.
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Step 2: Import AtomicXCore into Your Project
|
|
61
|
+
1. **Install the Component**: Add `pod 'AtomicXCore'` to your `Podfile`, then run `pod install`.
|
|
62
|
+
|
|
63
|
+
``` ruby
|
|
64
|
+
target 'xxxx' do
|
|
65
|
+
pod 'AtomicXCore'
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
2. **Configure App Permissions**: Add camera and microphone usage descriptions to your app's `Info.plist` file.
|
|
69
|
+
|
|
70
|
+
``` xml
|
|
71
|
+
<key>NSCameraUsageDescription</key>
|
|
72
|
+
<string>TUILiveKit needs camera access to enable video recording with picture</string>
|
|
73
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
74
|
+
<string>TUILiveKit needs microphone permission to enable sound in recorded videos</string>
|
|
75
|
+
```
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
### Step 3: Implement Login Logic
|
|
80
|
+
|
|
81
|
+
Call `LoginStore.shared.login` in your project to complete authentication. **This is required before using any functionality of AtomicXCore**.
|
|
82
|
+
|
|
83
|
+
> **Note:**
|
|
84
|
+
>
|
|
85
|
+
|
|
86
|
+
> We recommend calling LoginStore.shared.login only after your app's own user authentication is successful, to ensure clear and consistent login logic.
|
|
87
|
+
>
|
|
88
|
+
|
|
89
|
+
``` swift
|
|
90
|
+
import AtomicXCore
|
|
91
|
+
|
|
92
|
+
// AppDelegate.swift
|
|
93
|
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
94
|
+
LoginStore.shared.login(sdkAppID: 1400000001, // Replace with your SDKAppID
|
|
95
|
+
userID: "test_001", // Replace with your UserID
|
|
96
|
+
userSig: "xxxxxxxxxxx") { result in // Replace with your UserSig
|
|
97
|
+
switch result {
|
|
98
|
+
case .success(let info):
|
|
99
|
+
debugPrint("login success")
|
|
100
|
+
case .failure(let error):
|
|
101
|
+
debugPrint("login failed code:\(error.code), message:\(error.message)")
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Login API Parameter Description**
|
|
109
|
+
<table>
|
|
110
|
+
<tr>
|
|
111
|
+
<td rowspan="1" colSpan="1" >**Parameter**</td>
|
|
112
|
+
|
|
113
|
+
<td rowspan="1" colSpan="1" >**Type**</td>
|
|
114
|
+
|
|
115
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
116
|
+
</tr>
|
|
117
|
+
|
|
118
|
+
<tr>
|
|
119
|
+
<td rowspan="1" colSpan="1" >`SDKAppID`</td>
|
|
120
|
+
|
|
121
|
+
<td rowspan="1" colSpan="1" >`Int`</td>
|
|
122
|
+
|
|
123
|
+
<td rowspan="1" colSpan="1" >Get this from [TRTC console](https://console.trtc.io/app).</td>
|
|
124
|
+
</tr>
|
|
125
|
+
|
|
126
|
+
<tr>
|
|
127
|
+
<td rowspan="1" colSpan="1" >`UserID`</td>
|
|
128
|
+
|
|
129
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
130
|
+
|
|
131
|
+
<td rowspan="1" colSpan="1" >The unique ID for the current user. Must contain only English letters, numbers, hyphens, and underscores.</td>
|
|
132
|
+
</tr>
|
|
133
|
+
|
|
134
|
+
<tr>
|
|
135
|
+
<td rowspan="1" colSpan="1" >`userSig`</td>
|
|
136
|
+
|
|
137
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
138
|
+
|
|
139
|
+
<td rowspan="1" colSpan="1" >A ticket for Tencent Cloud authentication. Please note:<br>- Development Environment: You can use the local `GenerateTestUserSig.genTestSig` function to generate a `UserSig` or generate a temporary UserSig via the [UserSig Generation Tool](https://console.trtc.io/usersig).<br>- Production Environment: To prevent key leakage, you must use a server-side method to generate `UserSig`. For details, see [Generating UserSig on the Server](https://write.woa.com/document/89644530319802368).</td>
|
|
140
|
+
</tr>
|
|
141
|
+
</table>
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
## Building a Basic Live Room
|
|
145
|
+
|
|
146
|
+
### Step 1: Implement Broadcaster Video Streaming
|
|
147
|
+
|
|
148
|
+
Follow these steps to quickly set up broadcaster video streaming:
|
|
149
|
+
|
|
150
|
+

|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
> **Note:**
|
|
154
|
+
>
|
|
155
|
+
|
|
156
|
+
> For a complete broadcaster streaming implementation, refer to [TUILiveRoomAnchorViewController.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/LiveStream/TUILiveRoomAnchorViewController.swift) in the TUILiveKit open source project.
|
|
157
|
+
>
|
|
158
|
+
|
|
159
|
+
1. **Initialize the Broadcaster Streaming View**
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
In your broadcaster view controller, create a `LiveCoreView` instance and set `viewType` to `.pushView`.
|
|
163
|
+
|
|
164
|
+
``` swift
|
|
165
|
+
import AtomicXCore
|
|
166
|
+
|
|
167
|
+
// YourAnchorViewController represents your broadcaster streaming view controller
|
|
168
|
+
class YourAnchorViewController: UIViewController {
|
|
169
|
+
|
|
170
|
+
private let liveId: String
|
|
171
|
+
// 1. Add LiveCoreView as a property of your view controller
|
|
172
|
+
private let coreView = LiveCoreView(viewType: .pushView, frame: UIScreen.main.bounds)
|
|
173
|
+
|
|
174
|
+
// 2. Add a convenience initializer for instance initialization
|
|
175
|
+
// - liveId: The ID of the live room to start streaming
|
|
176
|
+
public init(liveId: String) {
|
|
177
|
+
self.liveId = liveId
|
|
178
|
+
super.init(nibName: nil, bundle: nil)
|
|
179
|
+
self.coreView.setLiveID(liveId)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
required init?(coder: NSCoder) {
|
|
183
|
+
fatalError("init(coder:) has not been implemented")
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public override func viewDidLoad() {
|
|
187
|
+
super.viewDidLoad()
|
|
188
|
+
// 3. Add the broadcaster streaming view to your view
|
|
189
|
+
view.addSubview(coreView)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
2. **Open Camera and Microphone**
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
Call `DeviceStore.shared.openLocalCamera` and `DeviceStore.shared.openLocalMicrophone` to open the camera and microphone. **No further action is required—LiveCoreView automatically previews the current camera video stream**.
|
|
197
|
+
|
|
198
|
+
``` swift
|
|
199
|
+
import AtomicXCore
|
|
200
|
+
|
|
201
|
+
class YourAnchorViewController: UIViewController {
|
|
202
|
+
// ... other code ...
|
|
203
|
+
public override func viewDidLoad() {
|
|
204
|
+
super.viewDidLoad()
|
|
205
|
+
// Enable devices
|
|
206
|
+
openDevices()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private func openDevices() {
|
|
210
|
+
// 1. Enable front camera
|
|
211
|
+
DeviceStore.shared.openLocalCamera(isFront: true, completion: nil)
|
|
212
|
+
// 2. Enable microphone
|
|
213
|
+
DeviceStore.shared.openLocalMicrophone(completion: nil)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
3. **Start Live Streaming**
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
Call `LiveListStore.shared.createLive` to start streaming.
|
|
221
|
+
|
|
222
|
+
``` swift
|
|
223
|
+
import AtomicXCore
|
|
224
|
+
|
|
225
|
+
class YourAnchorViewController: UIViewController {
|
|
226
|
+
|
|
227
|
+
// ... other code ...
|
|
228
|
+
|
|
229
|
+
// Call the start streaming interface to begin live streaming
|
|
230
|
+
private func startLive() {
|
|
231
|
+
var liveInfo = LiveInfo()
|
|
232
|
+
// 1. Set the live room ID
|
|
233
|
+
liveInfo.liveID = self.liveId
|
|
234
|
+
// 2. Set the live room name
|
|
235
|
+
liveInfo.liveName = "test live"
|
|
236
|
+
// 3. Configure layout template, default: 600 dynamic grid layout
|
|
237
|
+
liveInfo.seatLayoutTemplateID = 600
|
|
238
|
+
// 4. Keep the broadcaster always on the seat
|
|
239
|
+
liveInfo.keepOwnerOnSeat = true
|
|
240
|
+
// 5. Call LiveListStore.shared.createLive to start streaming
|
|
241
|
+
LiveListStore.shared.createLive(liveInfo) { [weak self] result in
|
|
242
|
+
guard let self = self else { return }
|
|
243
|
+
switch result {
|
|
244
|
+
case .success(let info):
|
|
245
|
+
debugPrint("startLive success")
|
|
246
|
+
case .failure(let error):
|
|
247
|
+
debugPrint("startLive error:\(error.message)")
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`LiveInfo` Parameter Descriptions:
|
|
255
|
+
|
|
256
|
+
<table>
|
|
257
|
+
<tr>
|
|
258
|
+
<td rowspan="1" colSpan="1" >**Parameter Name**</td>
|
|
259
|
+
|
|
260
|
+
<td rowspan="1" colSpan="1" >**Type**</td>
|
|
261
|
+
|
|
262
|
+
<td rowspan="1" colSpan="1" >**Attribute**</td>
|
|
263
|
+
|
|
264
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
265
|
+
</tr>
|
|
266
|
+
|
|
267
|
+
<tr>
|
|
268
|
+
<td rowspan="1" colSpan="1" >`liveID`</td>
|
|
269
|
+
|
|
270
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
271
|
+
|
|
272
|
+
<td rowspan="1" colSpan="1" >Required</td>
|
|
273
|
+
|
|
274
|
+
<td rowspan="1" colSpan="1" >Unique identifier for the live room.</td>
|
|
275
|
+
</tr>
|
|
276
|
+
|
|
277
|
+
<tr>
|
|
278
|
+
<td rowspan="1" colSpan="1" >`liveName`</td>
|
|
279
|
+
|
|
280
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
281
|
+
|
|
282
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
283
|
+
|
|
284
|
+
<td rowspan="1" colSpan="1" >Title of the live room.</td>
|
|
285
|
+
</tr>
|
|
286
|
+
|
|
287
|
+
<tr>
|
|
288
|
+
<td rowspan="1" colSpan="1" >`notice`</td>
|
|
289
|
+
|
|
290
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
291
|
+
|
|
292
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
293
|
+
|
|
294
|
+
<td rowspan="1" colSpan="1" >Announcement for the live room.</td>
|
|
295
|
+
</tr>
|
|
296
|
+
|
|
297
|
+
<tr>
|
|
298
|
+
<td rowspan="1" colSpan="1" >`isMessageDisable`</td>
|
|
299
|
+
|
|
300
|
+
<td rowspan="1" colSpan="1" >`Bool`</td>
|
|
301
|
+
|
|
302
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
303
|
+
|
|
304
|
+
<td rowspan="1" colSpan="1" >Whether to mute chat (true: yes, false: no).</td>
|
|
305
|
+
</tr>
|
|
306
|
+
|
|
307
|
+
<tr>
|
|
308
|
+
<td rowspan="1" colSpan="1" >`isPublicVisible`</td>
|
|
309
|
+
|
|
310
|
+
<td rowspan="1" colSpan="1" >`Bool`</td>
|
|
311
|
+
|
|
312
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
313
|
+
|
|
314
|
+
<td rowspan="1" colSpan="1" >Whether the room is publicly visible (true: yes, false: no).</td>
|
|
315
|
+
</tr>
|
|
316
|
+
|
|
317
|
+
<tr>
|
|
318
|
+
<td rowspan="1" colSpan="1" >`isSeatEnabled`</td>
|
|
319
|
+
|
|
320
|
+
<td rowspan="1" colSpan="1" >`Bool`</td>
|
|
321
|
+
|
|
322
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
323
|
+
|
|
324
|
+
<td rowspan="1" colSpan="1" >Whether to enable seat functionality (true: yes, false: no).</td>
|
|
325
|
+
</tr>
|
|
326
|
+
|
|
327
|
+
<tr>
|
|
328
|
+
<td rowspan="1" colSpan="1" >`keepOwnerOnSeat`</td>
|
|
329
|
+
|
|
330
|
+
<td rowspan="1" colSpan="1" >`Bool`</td>
|
|
331
|
+
|
|
332
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
333
|
+
|
|
334
|
+
<td rowspan="1" colSpan="1" >Whether to keep the owner on the seat.</td>
|
|
335
|
+
</tr>
|
|
336
|
+
|
|
337
|
+
<tr>
|
|
338
|
+
<td rowspan="1" colSpan="1" >`maxSeatCount`</td>
|
|
339
|
+
|
|
340
|
+
<td rowspan="1" colSpan="1" >`Int`</td>
|
|
341
|
+
|
|
342
|
+
<td rowspan="1" colSpan="1" >Required</td>
|
|
343
|
+
|
|
344
|
+
<td rowspan="1" colSpan="1" >Maximum number of seats.</td>
|
|
345
|
+
</tr>
|
|
346
|
+
|
|
347
|
+
<tr>
|
|
348
|
+
<td rowspan="1" colSpan="1" >`seatMode`</td>
|
|
349
|
+
|
|
350
|
+
<td rowspan="1" colSpan="1" >`TakeSeatMode`</td>
|
|
351
|
+
|
|
352
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
353
|
+
|
|
354
|
+
<td rowspan="1" colSpan="1" >Seat mode (.free: free to take seat, .apply: apply to take seat).</td>
|
|
355
|
+
</tr>
|
|
356
|
+
|
|
357
|
+
<tr>
|
|
358
|
+
<td rowspan="1" colSpan="1" >`seatLayoutTemplateID`</td>
|
|
359
|
+
|
|
360
|
+
<td rowspan="1" colSpan="1" >`UInt`</td>
|
|
361
|
+
|
|
362
|
+
<td rowspan="1" colSpan="1" >Required</td>
|
|
363
|
+
|
|
364
|
+
<td rowspan="1" colSpan="1" >Seat layout template ID.</td>
|
|
365
|
+
</tr>
|
|
366
|
+
|
|
367
|
+
<tr>
|
|
368
|
+
<td rowspan="1" colSpan="1" >`coverURL`</td>
|
|
369
|
+
|
|
370
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
371
|
+
|
|
372
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
373
|
+
|
|
374
|
+
<td rowspan="1" colSpan="1" >Cover image URL for the live room.</td>
|
|
375
|
+
</tr>
|
|
376
|
+
|
|
377
|
+
<tr>
|
|
378
|
+
<td rowspan="1" colSpan="1" >`backgroundURL`</td>
|
|
379
|
+
|
|
380
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
381
|
+
|
|
382
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
383
|
+
|
|
384
|
+
<td rowspan="1" colSpan="1" >Background image URL for the live room.</td>
|
|
385
|
+
</tr>
|
|
386
|
+
|
|
387
|
+
<tr>
|
|
388
|
+
<td rowspan="1" colSpan="1" >`categoryList`</td>
|
|
389
|
+
|
|
390
|
+
<td rowspan="1" colSpan="1" >`NSNumber`</td>
|
|
391
|
+
|
|
392
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
393
|
+
|
|
394
|
+
<td rowspan="1" colSpan="1" >Category tag list for the live room.</td>
|
|
395
|
+
</tr>
|
|
396
|
+
|
|
397
|
+
<tr>
|
|
398
|
+
<td rowspan="1" colSpan="1" >`activityStatus`</td>
|
|
399
|
+
|
|
400
|
+
<td rowspan="1" colSpan="1" >`Int`</td>
|
|
401
|
+
|
|
402
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
403
|
+
|
|
404
|
+
<td rowspan="1" colSpan="1" >Live activity status.</td>
|
|
405
|
+
</tr>
|
|
406
|
+
|
|
407
|
+
<tr>
|
|
408
|
+
<td rowspan="1" colSpan="1" >`isGiftEnabled`</td>
|
|
409
|
+
|
|
410
|
+
<td rowspan="1" colSpan="1" >`Bool`</td>
|
|
411
|
+
|
|
412
|
+
<td rowspan="1" colSpan="1" >Optional</td>
|
|
413
|
+
|
|
414
|
+
<td rowspan="1" colSpan="1" >Whether to enable gift functionality (true: yes, false: no).</td>
|
|
415
|
+
</tr>
|
|
416
|
+
</table>
|
|
417
|
+
|
|
418
|
+
4. **End Live Streaming**
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
After the live stream ends, call `LiveListStore.shared.endLive `to stop streaming and destroy the room. The SDK handles cleanup automatically.
|
|
422
|
+
|
|
423
|
+
``` swift
|
|
424
|
+
import AtomicXCore
|
|
425
|
+
|
|
426
|
+
class YourAnchorViewController: UIViewController {
|
|
427
|
+
|
|
428
|
+
// ... other code ...
|
|
429
|
+
|
|
430
|
+
// End live streaming
|
|
431
|
+
private func stopLive() {
|
|
432
|
+
LiveListStore.shared.endLive { [weak self] result in
|
|
433
|
+
guard let self = self else { return }
|
|
434
|
+
switch result {
|
|
435
|
+
case .success(let data):
|
|
436
|
+
debugPrint("endLive success")
|
|
437
|
+
case .failure(let error):
|
|
438
|
+
debugPrint("endLive error: \(error.message)")
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Step 2: Implement Audience Join and Watch
|
|
446
|
+
|
|
447
|
+
Enable audience viewing with the following steps:
|
|
448
|
+
|
|
449
|
+

|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
> **Note:**
|
|
453
|
+
>
|
|
454
|
+
|
|
455
|
+
> For a complete audience playback implementation, refer to [TUILiveRoomAudienceViewController.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/LiveStream/TUILiveRoomAudienceViewController.swift) in the TUILiveKit open source project.
|
|
456
|
+
>
|
|
457
|
+
|
|
458
|
+
1. **Implement the Audience Playback Page**
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
In your audience view controller, create a `LiveCoreView` instance and set `viewType` to `.playView`.
|
|
462
|
+
|
|
463
|
+
``` swift
|
|
464
|
+
import AtomicXCore
|
|
465
|
+
|
|
466
|
+
class YourAudienceViewController: UIViewController {
|
|
467
|
+
|
|
468
|
+
// 1. Initialize the audience playback page
|
|
469
|
+
private let coreView = LiveCoreView(viewType: .playView, frame: UIScreen.main.bounds)
|
|
470
|
+
private let liveId: String
|
|
471
|
+
|
|
472
|
+
public init(liveId: String) {
|
|
473
|
+
self.liveId = liveId
|
|
474
|
+
super.init(nibName: nil, bundle: nil)
|
|
475
|
+
// 2. Bind the live ID
|
|
476
|
+
self.coreView.setLiveID(liveId)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
required init?(coder: NSCoder) {
|
|
480
|
+
fatalError("init(coder:) has not been implemented")
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
public override func viewDidLoad() {
|
|
484
|
+
super.viewDidLoad()
|
|
485
|
+
// 3. Add the playback view to your view
|
|
486
|
+
view.addSubview(coreView)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
2. **Join the Live Room to Watch**
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
Call `LiveListStore.shared.joinLive` to join the live stream. **No further action is required—LiveCoreView will automatically play the current room's video stream**.
|
|
494
|
+
|
|
495
|
+
``` swift
|
|
496
|
+
import AtomicXCore
|
|
497
|
+
|
|
498
|
+
class YourAudienceViewController: UIViewController {
|
|
499
|
+
// ... other code ...
|
|
500
|
+
|
|
501
|
+
public override func viewDidLoad() {
|
|
502
|
+
super.viewDidLoad()
|
|
503
|
+
view.addSubview(coreView)
|
|
504
|
+
// 4. Join the live room
|
|
505
|
+
joinLive()
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private func joinLive() {
|
|
509
|
+
// Call LiveListStore.shared.joinLive to enter the live room
|
|
510
|
+
// - liveId: Same liveId as the broadcaster
|
|
511
|
+
LiveListStore.shared.joinLive(liveID: liveId) { [weak self] result in
|
|
512
|
+
guard let self = self else { return }
|
|
513
|
+
switch result {
|
|
514
|
+
case .success(let info):
|
|
515
|
+
debugPrint("joinLive success")
|
|
516
|
+
case .failure(let error):
|
|
517
|
+
debugPrint("joinLive error \(error.message)")
|
|
518
|
+
// If joining fails, exit the page
|
|
519
|
+
// self.dismiss(animated: true)
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
3. **Leave the Live Room**
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
When the audience leaves, call `LiveListStore.shared.leaveLive` to exit. The SDK will automatically stop playback and leave the room.
|
|
529
|
+
|
|
530
|
+
``` swift
|
|
531
|
+
import AtomicXCore
|
|
532
|
+
|
|
533
|
+
class YourAudienceViewController: UIViewController {
|
|
534
|
+
|
|
535
|
+
// ... other code ...
|
|
536
|
+
|
|
537
|
+
// Leave the live room
|
|
538
|
+
private func leaveLive() {
|
|
539
|
+
LiveListStore.shared.leaveLive { [weak self] result in
|
|
540
|
+
guard let self = self else { return }
|
|
541
|
+
switch result {
|
|
542
|
+
case .success:
|
|
543
|
+
debugPrint("leaveLive success")
|
|
544
|
+
case .failure(let error):
|
|
545
|
+
debugPrint("leaveLive error \(error.message)")
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Step 3: Listen for Live Events
|
|
553
|
+
|
|
554
|
+
After joining a live room, handle "passive" events such as the broadcaster ending the stream or the audience being removed. If you do not listen for these events, the audience UI may remain on a black screen, impacting user experience.
|
|
555
|
+
|
|
556
|
+
Subscribe to `liveListEventPublisher` from `LiveListStore` to listen for events:
|
|
557
|
+
``` swift
|
|
558
|
+
import AtomicXCore
|
|
559
|
+
import Combine // 1. Import the Combine framework
|
|
560
|
+
|
|
561
|
+
class YourAudienceViewController: UIViewController {
|
|
562
|
+
|
|
563
|
+
// ... other code (coreView, liveId, init, deinit, joinLive, leaveLive, etc.) ...
|
|
564
|
+
|
|
565
|
+
// 2. Define cancellableSet to manage subscription lifecycle
|
|
566
|
+
private var cancellableSet: Set<AnyCancellable> = []
|
|
567
|
+
|
|
568
|
+
public override func viewDidLoad() {
|
|
569
|
+
super.viewDidLoad()
|
|
570
|
+
view.addSubview(coreView)
|
|
571
|
+
// 3. Listen for live events
|
|
572
|
+
setupLiveEventListener()
|
|
573
|
+
// 4. Join the live room
|
|
574
|
+
joinLive()
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// 5. Add a method to set up event listeners
|
|
578
|
+
private func setupLiveEventListener() {
|
|
579
|
+
LiveListStore.shared.liveListEventPublisher
|
|
580
|
+
.receive(on: RunLoop.main) // Ensure UI updates are handled on the main thread
|
|
581
|
+
.sink { [weak self] event in
|
|
582
|
+
guard let self = self else { return }
|
|
583
|
+
switch event {
|
|
584
|
+
case .onLiveEnded(let liveID, let reason, let message):
|
|
585
|
+
// Live stream ended
|
|
586
|
+
debugPrint("Live ended. liveID: \(liveID), reason: \(reason.rawValue), message: \(message)")
|
|
587
|
+
// Handle logic to exit the live room, e.g., close the current page
|
|
588
|
+
// self.dismiss(animated: true)
|
|
589
|
+
case .onKickedOutOfLive(let liveID, let reason, let message):
|
|
590
|
+
// Kicked out of live stream
|
|
591
|
+
debugPrint("Kicked out of live. liveID: \(liveID), reason: \(reason.rawValue), message: \(message)")
|
|
592
|
+
// Handle logic to exit the live room
|
|
593
|
+
// self.dismiss(animated: true)
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
.store(in: &cancellableSet) // Manage subscription
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ... joinLive() and leaveLive() methods ...
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Run and Test
|
|
604
|
+
|
|
605
|
+
After integrating `LiveCoreView`, you will have a pure video rendering view with full live streaming capabilities, but without any interactive UI. To add interactive features, see [Enriching the Live Streaming Scene](https://write.woa.com/#enriching-the-live-streaming-scene).
|
|
606
|
+
<table>
|
|
607
|
+
<tr>
|
|
608
|
+
<td rowspan="1" colSpan="1" ></td>
|
|
609
|
+
|
|
610
|
+
<td rowspan="1" colSpan="1" >**Dynamic Grid Layout**</td>
|
|
611
|
+
|
|
612
|
+
<td rowspan="1" colSpan="1" >**Floating Window Layout**</td>
|
|
613
|
+
|
|
614
|
+
<td rowspan="1" colSpan="1" >**Fixed Grid Layout**</td>
|
|
615
|
+
|
|
616
|
+
<td rowspan="1" colSpan="1" >**Fixed Window Layout**</td>
|
|
617
|
+
</tr>
|
|
618
|
+
|
|
619
|
+
<tr>
|
|
620
|
+
<td rowspan="1" colSpan="1" >Template ID</td>
|
|
621
|
+
|
|
622
|
+
<td rowspan="1" colSpan="1" >600</td>
|
|
623
|
+
|
|
624
|
+
<td rowspan="1" colSpan="1" >601</td>
|
|
625
|
+
|
|
626
|
+
<td rowspan="1" colSpan="1" >800</td>
|
|
627
|
+
|
|
628
|
+
<td rowspan="1" colSpan="1" >801</td>
|
|
629
|
+
</tr>
|
|
630
|
+
|
|
631
|
+
<tr>
|
|
632
|
+
<td rowspan="1" colSpan="1" >Description</td>
|
|
633
|
+
|
|
634
|
+
<td rowspan="1" colSpan="1" >Default layout; grid size adjusts dynamically based on number of co-hosts.</td>
|
|
635
|
+
|
|
636
|
+
<td rowspan="1" colSpan="1" >Co-hosts are displayed as floating windows.</td>
|
|
637
|
+
|
|
638
|
+
<td rowspan="1" colSpan="1" >Fixed number of co-hosts; each occupies a fixed grid.</td>
|
|
639
|
+
|
|
640
|
+
<td rowspan="1" colSpan="1" >Fixed number of co-hosts; guests are displayed as fixed windows.</td>
|
|
641
|
+
</tr>
|
|
642
|
+
|
|
643
|
+
<tr>
|
|
644
|
+
<td rowspan="1" colSpan="1" >Example</td>
|
|
645
|
+
|
|
646
|
+
<td rowspan="1" colSpan="1" ><br></td>
|
|
647
|
+
|
|
648
|
+
<td rowspan="1" colSpan="1" ><br></td>
|
|
649
|
+
|
|
650
|
+
<td rowspan="1" colSpan="1" ><br></td>
|
|
651
|
+
|
|
652
|
+
<td rowspan="1" colSpan="1" ><br></td>
|
|
653
|
+
</tr>
|
|
654
|
+
</table>
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
## Advanced Features
|
|
658
|
+
|
|
659
|
+
### Synchronizing Custom Status in Live Streaming Room
|
|
660
|
+
|
|
661
|
+
In live streaming room, the host may need to synchronize custom information to all audience members, such as "current room topic" or "background music info". Use the `metaData` feature of LiveListStore to achieve this.
|
|
662
|
+
|
|
663
|
+
#### Implementation
|
|
664
|
+
1. On the host side, set custom information (recommended as JSON) to one or more keys using the `updateLiveMetaData` API. AtomicXCore will synchronize these changes in real time to all audience members.
|
|
665
|
+
|
|
666
|
+
2. On the audience side, subscribe to `LiveListState.currentLive` and listen for changes in `metaData`. When a relevant key is updated, parse its value and update your business state.
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
#### Code Example
|
|
670
|
+
``` swift
|
|
671
|
+
import AtomicXCore
|
|
672
|
+
import Combine
|
|
673
|
+
|
|
674
|
+
// 1. Define a background music model (recommended: Codable)
|
|
675
|
+
struct MusicModel: Codable {
|
|
676
|
+
let musicId: String
|
|
677
|
+
let musicName: String
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// 2. Host side: Push background music info
|
|
681
|
+
func updateBackgroundMusic(music: MusicModel) {
|
|
682
|
+
guard let jsonData = try? JSONEncoder().encode(music),
|
|
683
|
+
let jsonString = String(data: jsonData, encoding: .utf8) else { return }
|
|
684
|
+
|
|
685
|
+
let metaData = ["music_info": jsonString]
|
|
686
|
+
|
|
687
|
+
LiveListStore.shared.updateLiveMetaData(metaData) { result in
|
|
688
|
+
if case .success = result {
|
|
689
|
+
print("Background music \(music.musicName) pushed successfully")
|
|
690
|
+
} else if case .failure(let error) = result {
|
|
691
|
+
print("Background music push failed: \(error.message)")
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// 3. Audience side: Subscribe and update business logic
|
|
697
|
+
private func subscribeToDataUpdates() {
|
|
698
|
+
LiveListStore.shared.state
|
|
699
|
+
// Listen for metaData changes in the current room
|
|
700
|
+
.subscribe(StatePublisherSelector(keyPath: \LiveListState.currentLive))
|
|
701
|
+
.map { $0.metaData["music_info"] }
|
|
702
|
+
.removeDuplicates()
|
|
703
|
+
.receive(on: DispatchQueue.main)
|
|
704
|
+
.sink { jsonString in
|
|
705
|
+
guard let jsonString = jsonString,
|
|
706
|
+
let data = jsonString.data(using: .utf8),
|
|
707
|
+
let music = try? JSONDecoder().decode(MusicModel.self, from: data) else {
|
|
708
|
+
return
|
|
709
|
+
}
|
|
710
|
+
// Update business state, play new music
|
|
711
|
+
// ... (e.g.: playMusic(music))
|
|
712
|
+
}
|
|
713
|
+
.store(in: &cancellables)
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
## Enriching the Live Streaming Scene
|
|
718
|
+
|
|
719
|
+
Once the basic live streaming functionality is in place, refer to the following guides to add interactive features to your live stream:
|
|
720
|
+
<table>
|
|
721
|
+
<tr>
|
|
722
|
+
<td rowspan="1" colSpan="1" >**Feature**</td>
|
|
723
|
+
|
|
724
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
725
|
+
|
|
726
|
+
<td rowspan="1" colSpan="1" >**Store**</td>
|
|
727
|
+
|
|
728
|
+
<td rowspan="1" colSpan="1" >**Implementation Guide**</td>
|
|
729
|
+
</tr>
|
|
730
|
+
|
|
731
|
+
<tr>
|
|
732
|
+
<td rowspan="1" colSpan="1" >Enable Audience Audio/Video Co-hosting</td>
|
|
733
|
+
|
|
734
|
+
<td rowspan="1" colSpan="1" >Audience can apply to join the host and interact via real-time video.</td>
|
|
735
|
+
|
|
736
|
+
<td rowspan="1" colSpan="1" >[CoGuestStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cogueststore)</td>
|
|
737
|
+
|
|
738
|
+
<td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/194571607036174336)</td>
|
|
739
|
+
</tr>
|
|
740
|
+
|
|
741
|
+
<tr>
|
|
742
|
+
<td rowspan="1" colSpan="1" >Enable Host Cross-room PK</td>
|
|
743
|
+
|
|
744
|
+
<td rowspan="1" colSpan="1" >Hosts from different rooms can connect for interaction or PK.</td>
|
|
745
|
+
|
|
746
|
+
<td rowspan="1" colSpan="1" >[CoHostStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cohoststore)</td>
|
|
747
|
+
|
|
748
|
+
<td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/194571607415758848)</td>
|
|
749
|
+
</tr>
|
|
750
|
+
|
|
751
|
+
<tr>
|
|
752
|
+
<td rowspan="1" colSpan="1" >Add Bullet Chat Feature</td>
|
|
753
|
+
|
|
754
|
+
<td rowspan="1" colSpan="1" >Audience can send and receive real-time text messages in the live room.</td>
|
|
755
|
+
|
|
756
|
+
<td rowspan="1" colSpan="1" >[BarrageStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/barragestore)</td>
|
|
757
|
+
|
|
758
|
+
<td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/194571607781711872)</td>
|
|
759
|
+
</tr>
|
|
760
|
+
|
|
761
|
+
<tr>
|
|
762
|
+
<td rowspan="1" colSpan="1" >Build a Gift Giving System</td>
|
|
763
|
+
|
|
764
|
+
<td rowspan="1" colSpan="1" >Audience can send virtual gifts to the host, increasing engagement and fun.</td>
|
|
765
|
+
|
|
766
|
+
<td rowspan="1" colSpan="1" >[GiftStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/giftstore)</td>
|
|
767
|
+
|
|
768
|
+
<td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/194571602052481024)</td>
|
|
769
|
+
</tr>
|
|
770
|
+
</table>
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
## API Documentation
|
|
774
|
+
<table>
|
|
775
|
+
<tr>
|
|
776
|
+
<td rowspan="1" colSpan="1" >**Store/Component**</td>
|
|
777
|
+
|
|
778
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
779
|
+
|
|
780
|
+
<td rowspan="1" colSpan="1" >**API Reference**</td>
|
|
781
|
+
</tr>
|
|
782
|
+
|
|
783
|
+
<tr>
|
|
784
|
+
<td rowspan="1" colSpan="1" >LiveCoreView</td>
|
|
785
|
+
|
|
786
|
+
<td rowspan="1" colSpan="1" >Core view component for displaying and interacting with live video streams. Handles video rendering and view widgets, supports host streaming, audience co-hosting, host connections, and more.</td>
|
|
787
|
+
|
|
788
|
+
<td rowspan="1" colSpan="1" >[LiveCoreView](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/livecoreview/)</td>
|
|
789
|
+
</tr>
|
|
790
|
+
|
|
791
|
+
<tr>
|
|
792
|
+
<td rowspan="1" colSpan="1" >LiveListStore</td>
|
|
793
|
+
|
|
794
|
+
<td rowspan="1" colSpan="1" >Manages the full lifecycle of live rooms: create/join/leave/destroy rooms, query room list, modify live info (name, announcement, etc.), and listen to live status events (e.g., kicked out, ended).</td>
|
|
795
|
+
|
|
796
|
+
<td rowspan="1" colSpan="1" >[LiveListStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/liveliststore)</td>
|
|
797
|
+
</tr>
|
|
798
|
+
|
|
799
|
+
<tr>
|
|
800
|
+
<td rowspan="1" colSpan="1" >DeviceStore</td>
|
|
801
|
+
|
|
802
|
+
<td rowspan="1" colSpan="1" >Controls audio/video devices: microphone (on/off, volume), camera (on/off, switch, quality), screen sharing, and real-time device status monitoring.</td>
|
|
803
|
+
|
|
804
|
+
<td rowspan="1" colSpan="1" >[DeviceStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/devicestore)</td>
|
|
805
|
+
</tr>
|
|
806
|
+
|
|
807
|
+
<tr>
|
|
808
|
+
<td rowspan="1" colSpan="1" >CoGuestStore</td>
|
|
809
|
+
|
|
810
|
+
<td rowspan="1" colSpan="1" >Manages audience co-hosting: apply/invite/accept/reject co-host requests, member permission control (microphone/camera), and status synchronization.</td>
|
|
811
|
+
|
|
812
|
+
<td rowspan="1" colSpan="1" >[CoGuestStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cogueststore)</td>
|
|
813
|
+
</tr>
|
|
814
|
+
|
|
815
|
+
<tr>
|
|
816
|
+
<td rowspan="1" colSpan="1" >CoHostStore</td>
|
|
817
|
+
|
|
818
|
+
<td rowspan="1" colSpan="1" >Handles host cross-room connections: supports multiple layout templates (dynamic grid, etc.), initiates/accepts/rejects connections, and manages co-host interactions.</td>
|
|
819
|
+
|
|
820
|
+
<td rowspan="1" colSpan="1" >[CoHostStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cohoststore)</td>
|
|
821
|
+
</tr>
|
|
822
|
+
|
|
823
|
+
<tr>
|
|
824
|
+
<td rowspan="1" colSpan="1" >BattleStore</td>
|
|
825
|
+
|
|
826
|
+
<td rowspan="1" colSpan="1" >Manages host PK battles: initiate PK (set duration/opponent), manage PK status (start/end), synchronize scores, and listen for battle results.</td>
|
|
827
|
+
|
|
828
|
+
<td rowspan="1" colSpan="1" >[BattleStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/battlestore)</td>
|
|
829
|
+
</tr>
|
|
830
|
+
|
|
831
|
+
<tr>
|
|
832
|
+
<td rowspan="1" colSpan="1" >GiftStore</td>
|
|
833
|
+
|
|
834
|
+
<td rowspan="1" colSpan="1" >Handles gift interactions: get gift list, send/receive gifts, and listen for gift events (including sender and gift details).</td>
|
|
835
|
+
|
|
836
|
+
<td rowspan="1" colSpan="1" >[GiftStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/giftstore)</td>
|
|
837
|
+
</tr>
|
|
838
|
+
|
|
839
|
+
<tr>
|
|
840
|
+
<td rowspan="1" colSpan="1" >BarrageStore</td>
|
|
841
|
+
|
|
842
|
+
<td rowspan="1" colSpan="1" >Supports live chat: send text/custom danmaku, maintain danmaku list, and monitor danmaku status in real time.</td>
|
|
843
|
+
|
|
844
|
+
<td rowspan="1" colSpan="1" >[BarrageStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/barragestore)</td>
|
|
845
|
+
</tr>
|
|
846
|
+
|
|
847
|
+
<tr>
|
|
848
|
+
<td rowspan="1" colSpan="1" >LikeStore</td>
|
|
849
|
+
|
|
850
|
+
<td rowspan="1" colSpan="1" >Handles like interactions: send likes, listen for like events, and synchronize total like count.</td>
|
|
851
|
+
|
|
852
|
+
<td rowspan="1" colSpan="1" >[LikeStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/likestore)</td>
|
|
853
|
+
</tr>
|
|
854
|
+
|
|
855
|
+
<tr>
|
|
856
|
+
<td rowspan="1" colSpan="1" >LiveAudienceStore</td>
|
|
857
|
+
|
|
858
|
+
<td rowspan="1" colSpan="1" >Manages audience: get real-time audience list (ID/name/avatar), count audience, and listen for join/leave events.</td>
|
|
859
|
+
|
|
860
|
+
<td rowspan="1" colSpan="1" >[LiveAudienceStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/liveaudiencestore)</td>
|
|
861
|
+
</tr>
|
|
862
|
+
|
|
863
|
+
<tr>
|
|
864
|
+
<td rowspan="1" colSpan="1" >AudioEffectStore</td>
|
|
865
|
+
|
|
866
|
+
<td rowspan="1" colSpan="1" >Audio effects: voice change (child/male), reverb (KTV, etc.), ear return adjustment, and real-time effect switching.</td>
|
|
867
|
+
|
|
868
|
+
<td rowspan="1" colSpan="1" >[AudioEffectStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/audioeffectstore)</td>
|
|
869
|
+
</tr>
|
|
870
|
+
|
|
871
|
+
<tr>
|
|
872
|
+
<td rowspan="1" colSpan="1" >BaseBeautyStore</td>
|
|
873
|
+
|
|
874
|
+
<td rowspan="1" colSpan="1" >Basic beauty filters: adjust smoothing/whitening/rosiness (0-9), reset beauty status, and synchronize effect parameters.</td>
|
|
875
|
+
|
|
876
|
+
<td rowspan="1" colSpan="1" >[BaseBeautyStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/basebeautystore)</td>
|
|
877
|
+
</tr>
|
|
878
|
+
</table>
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
## FAQs
|
|
882
|
+
|
|
883
|
+
### Why is the screen black with no video after the host calls createLive or the audience calls joinLive?
|
|
884
|
+
- **Check **`setLiveID`: Ensure you have set the correct liveID for the LiveCoreView instance before calling start or join methods.
|
|
885
|
+
|
|
886
|
+
- **Check device permissions**: Ensure the app has system permissions for camera and microphone usage.
|
|
887
|
+
|
|
888
|
+
- **Check the host side**: Has the host called `DeviceStore.shared().openLocalCamera(true)`to enable the camera?
|
|
889
|
+
|
|
890
|
+
- **Check the network**: Ensure the device has a stable network connection.
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
### After the host enables the camera, why is there a local video preview after starting the stream, but a black screen before starting?
|
|
894
|
+
- **Check the host side**: Make sure the host streaming view (`LiveCoreView`) has its `viewType` set to `PUSH_VIEW`.
|
|
895
|
+
|
|
896
|
+
- **Check the audience side**: Make sure the audience playback view (`LiveCoreView`) has its `viewType` set to `PLAY_VIEW`.
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
### What restrictions and rules apply to `updateLiveMetaData`?
|
|
900
|
+
|
|
901
|
+
To maintain system stability and efficiency, metaData usage follows these rules:
|
|
902
|
+
- **Permissions**: Only hosts and administrators can call `updateLiveMetaData`. Regular audience members cannot.
|
|
903
|
+
|
|
904
|
+
- **Quantity and Size Limits:**
|
|
905
|
+
|
|
906
|
+
- Each room supports up to **10** keys.
|
|
907
|
+
|
|
908
|
+
- Each key can be up to **50 bytes**; each value up to **2KB**.
|
|
909
|
+
|
|
910
|
+
- The total size of all values in a room cannot exceed **16KB**.
|
|
911
|
+
|
|
912
|
+
- **Conflict Resolution**: The update mechanism is "last write wins". If multiple administrators modify the same key in quick succession, the last change takes effect. Avoid concurrent modifications to the same key in your business logic.
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
### sync Fails to Copy Third-party Library Resources Due to Insufficient Permissions
|
|
916
|
+
|
|
917
|
+
For example, when integrating the SnapKit framework with Xcode, you may encounter the following build error:
|
|
918
|
+
``` plaintext
|
|
919
|
+
rsync(xxxx):1:1: SnapKit.framework/SnapKit_Privacy.bundle/: mkpathat: Operation not permitted
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
**Cause**
|
|
923
|
+
|
|
924
|
+
Xcode's user script sandboxing mechanism restricts the rsync script's write access to `SnapKit.framework` resource files during the build process, causing the `SnapKit_Privacy.bundle` in the framework to fail to copy properly.
|
|
925
|
+
|
|
926
|
+
Solution Steps
|
|
927
|
+
1. Disable user script sandboxing. Open your Xcode project, go to **Build Settings**, search for User Script Sandboxing (or `ENABLE_USER_SCRIPT_SANDBOXING`), and change its value from YES to NO.
|
|
928
|
+
|
|
929
|
+
2. Clean and rebuild the project. Run **Product → Clean** Build Folder to clear the project cache, then rebuild and run the project.
|
|
930
|
+
|
|
931
|
+
|