@tencent-rtc/mcp 1.3.4 → 1.3.5

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.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/resource/doc/chatuikit/android/features/theme.md +363 -0
  3. package/resource/doc/chatuikit/{android/client_api_Android.md → faq/android_sdk/client-api.md} +0 -2
  4. package/resource/doc/chatuikit/faq/common/product.md +8 -0
  5. package/resource/doc/chatuikit/faq/flutter_sdk/faq.md +53 -0
  6. package/resource/doc/chatuikit/{iOS/client_api_iOS.md → faq/ios_sdk/client-api.md} +0 -2
  7. package/resource/doc/chatuikit/flutter/features/theme.md +379 -0
  8. package/resource/doc/chatuikit/flutter/integration.md +413 -0
  9. package/resource/doc/chatuikit/flutter/workflow.md +941 -0
  10. package/resource/doc/chatuikit/iOS/features/theme.md +416 -0
  11. package/resource/doc/chatuikit/iOS/workflow.md +0 -0
  12. package/resource/doc/chatuikit/react/components/chat-setting.md +86 -0
  13. package/resource/doc/chatuikit/react/components/contact-list.md +1093 -0
  14. package/resource/doc/chatuikit/react/components/search.md +745 -0
  15. package/resource/doc/chatuikit/vue/components/chat-setting.md +64 -0
  16. package/resource/doc/chatuikit/vue/components/contact-list.md +679 -0
  17. package/resource/doc/chatuikit/vue/components/search.md +1384 -0
  18. package/resource/doc/livekit/live/android/workflow.md +137 -6
  19. package/resource/doc/livekit/live/ios/live-audience-list.md +339 -0
  20. package/resource/doc/livekit/live/ios/live-audio-effect.md +370 -0
  21. package/resource/doc/livekit/live/ios/live-barrage.md +517 -0
  22. package/resource/doc/livekit/live/ios/live-base-beauty.md +247 -0
  23. package/resource/doc/livekit/live/ios/live-co-guest-interaction.md +518 -0
  24. package/resource/doc/livekit/live/ios/live-gift.md +783 -0
  25. package/resource/doc/livekit/live/ios/live-host-interaction.md +815 -0
  26. package/resource/doc/livekit/live/ios/live-integration.md +931 -0
  27. package/resource/doc/livekit/live/ios/live-live-list.md +485 -0
  28. package/resource/doc/livekit/live/ios/workflow.md +454 -0
  29. package/resource/doc/rtcengine/web/plugin/20-advanced-customized-capture-rendering.md +87 -0
  30. package/resource/doc/rtcengine/web/plugin/21-advanced-auto-play-policy.md +87 -0
  31. package/resource/doc/rtcengine/web/plugin/22-advanced-audio-mixer.md +175 -0
  32. package/resource/doc/rtcengine/web/plugin/23-advanced-support-detection.md +221 -0
  33. package/resource/doc/rtcengine/web/plugin/24-advanced-network-quality.md +140 -0
  34. package/resource/doc/rtcengine/web/plugin/25-advanced-device-change.md +99 -0
  35. package/resource/doc/rtcengine/web/plugin/26-advanced-publish-cdn-stream.md +451 -0
  36. package/resource/doc/rtcengine/web/plugin/27-advanced-small-stream.md +151 -0
  37. package/resource/doc/rtcengine/web/plugin/28-advanced-beauty.md +322 -0
  38. package/resource/doc/rtcengine/web/plugin/29-advanced-water-mark.md +149 -0
  39. package/resource/doc/rtcengine/web/plugin/30-advanced-cross-room-link.md +96 -0
  40. package/resource/doc/rtcengine/web/plugin/31-advanced-plugins-overview.md +330 -0
  41. package/resource/doc/rtcengine/web/plugin/34-advanced-proxy.md +248 -0
  42. package/resource/doc/rtcengine/web/plugin/35-advanced-ai-denoiser.md +81 -0
  43. package/resource/doc/rtcengine/web/plugin/36-advanced-virtual-background.md +242 -0
  44. package/resource/doc/rtcengine/web/plugin/37-advanced-voice-changer.md +99 -0
  45. package/resource/doc/rtcengine/web/plugin/39-advanced-video-decoder.md +33 -0
  46. package/resource/doc/rtcengine/web/plugin/40-advanced-video-mixer.md +379 -0
  47. package/resource/doc/rtcengine/web/plugin/41-advanced-small-stream-auto-switcher.md +91 -0
  48. package/resource/doc/rtcengine/web/plugin/42-advanced-realtime-transcriber.md +315 -0
  49. package/resource/doc/rtcengine/web/plugin/43-advanced-face-detection.md +180 -0
  50. package/tools/chat/get-native-chat-feature-detail.js +30 -0
  51. package/tools/chat/get-native-chat-feature-detail.js.map +1 -0
  52. package/tools/chat/get-native-chat-integration.js +30 -12
  53. package/tools/chat/get-native-chat-integration.js.map +1 -1
  54. package/tools/common/get-faq.js +2 -4
  55. package/tools/common/get-faq.js.map +1 -1
  56. package/tools/definitions/get_native_chat_feature_detail_def.js +32 -0
  57. package/tools/definitions/get_native_chat_feature_detail_def.js.map +1 -0
  58. package/tools/definitions/get_native_chat_uikit_integration_def.js +19 -7
  59. package/tools/definitions/get_native_chat_uikit_integration_def.js.map +1 -1
  60. package/tools/definitions/{get_native_live_extra_features_def.js → get_native_live_uikit_extra_features_def.js} +3 -3
  61. package/tools/definitions/get_native_live_uikit_extra_features_def.js.map +1 -0
  62. package/tools/definitions/{get_native_live_interaction_def.js → get_native_live_uikit_interaction_def.js} +3 -3
  63. package/tools/definitions/get_native_live_uikit_interaction_def.js.map +1 -0
  64. package/tools/definitions/get_web_chat_uikit_component_detail_def.js +49 -10
  65. package/tools/definitions/get_web_chat_uikit_component_detail_def.js.map +1 -1
  66. package/tools/index.js +6 -4
  67. package/tools/index.js.map +1 -1
  68. package/tools/live/{get_native_live_extra_features.js → get_native_live_uikit_extra_features.js} +4 -4
  69. package/tools/live/get_native_live_uikit_extra_features.js.map +1 -0
  70. package/tools/live/{get_native_live_interaction.js → get_native_live_uikit_interaction.js} +4 -4
  71. package/tools/live/get_native_live_uikit_interaction.js.map +1 -0
  72. package/tools/rtcengine/get-rtcengine-web-sdk-integration.js.map +1 -1
  73. package/tools/rtcengine/get-trtc-sdk-faq.js +32 -0
  74. package/tools/rtcengine/get-trtc-sdk-faq.js.map +1 -0
  75. package/tools/rtcengine/get-trtc-web-sdk-faq.js +1 -1
  76. package/tools/rtcengine/get-trtc-web-sdk-integration.js.map +1 -1
  77. package/tools/rtcengine/get-trtc-web-sdk-plugin.js +45 -0
  78. package/tools/rtcengine/get-trtc-web-sdk-plugin.js.map +1 -0
  79. package/utils/handle-component-name.js +0 -16
  80. package/utils/handle-component-name.js.map +1 -1
  81. package/utils/report-cls-client.js +1 -1
  82. package/utils/report-es-client.js +1 -1
  83. package/resource/doc/chatuikit/android/generate_UserSig.md +0 -306
  84. package/resource/doc/chatuikit/android/integrarte_only_tuichat_Android.md +0 -290
  85. package/resource/doc/chatuikit/android/quick_start_Android.md +0 -138
  86. package/resource/doc/chatuikit/android/show_typing_Android.md +0 -43
  87. package/resource/doc/chatuikit/flutter/generate_UserSig.md +0 -306
  88. package/resource/doc/chatuikit/flutter/integrate_tuikit_Flutter.md +0 -133
  89. package/resource/doc/chatuikit/iOS/generate_UserSig.md +0 -306
  90. package/resource/doc/chatuikit/iOS/integrate_only_tuichat_iOS.md +0 -748
  91. package/resource/doc/chatuikit/iOS/quick_start_iOS.md +0 -163
  92. package/resource/doc/chatuikit/iOS/show_typing_iOS.md +0 -52
  93. package/tools/definitions/get_native_live_extra_features_def.js.map +0 -1
  94. package/tools/definitions/get_native_live_interaction_def.js.map +0 -1
  95. package/tools/live/get_native_live_extra_features.js.map +0 -1
  96. package/tools/live/get_native_live_interaction.js.map +0 -1
  97. /package/resource/doc/chatuikit/android/{integrate_tuikit_Android.md → integration.md} +0 -0
  98. /package/resource/doc/chatuikit/{faq/native_sdk/faq.md → android/workflow.md} +0 -0
  99. /package/resource/doc/chatuikit/{flutter/client_api_Flutter.md → faq/flutter_sdk/client-api.md} +0 -0
  100. /package/resource/doc/chatuikit/iOS/{integrate_tuikit_iOS.md → integration.md} +0 -0
  101. /package/resource/doc/chatuikit/react/components/{chatheader.md → chat-header.md} +0 -0
  102. /package/resource/doc/chatuikit/vue/components/{chatheader.md → chat-header.md} +0 -0
@@ -0,0 +1,815 @@
1
+ Displaying PK User Score on Video View**AtomicXCore** provides two primary modules: [CoHostStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cohoststore) and [BattleStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/battlestore), which handle cross-room co-hosting and PK battles respectively. This guide walks you through using both modules together to implement the complete workflow from co-hosting to PK in a live streaming scenario.
2
+
3
+ ## Core Scenario
4
+
5
+ A typical "Host Co-hosting PK" session consists of three main stages, as shown below:
6
+ 1. **Cross-room Co-hosting**: Two hosts connect, and both video streams are displayed in a shared view.
7
+
8
+ 2. **Initiate PK**: After the connection is established, either host can start a PK challenge.
9
+
10
+ 3. **PK Battle**: Both hosts compete in a PK battle, with scores updated in real time.
11
+
12
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/096c93dec6df11f0a93d52540044a08e.png)
13
+
14
+
15
+ ## Implementation
16
+
17
+ ### Step 1: Component Integration
18
+
19
+ Refer to [Quick Start](https://write.woa.com/document/194571606121816064) to integrate AtomicXCore and complete the setup of LiveCoreView.
20
+
21
+ ### Step 2: Implement Cross-Room Co-hosting
22
+
23
+ The goal of this step is to display the video streams of two hosts in the same view. Use [CoHostStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cohoststore) to achieve this.
24
+
25
+ #### Inviter (Host A) Implementation
26
+ 1. **Initiate Co-hosting Invitation**
27
+
28
+
29
+ When Host A selects Host B in the UI and initiates a co-hosting request, call the `requestHostConnection` method.
30
+
31
+ ``` swift
32
+ import AtomicXCore
33
+ import Combine
34
+
35
+ // Host A's view controller
36
+ class AnchorAViewController {
37
+ private let liveId = "Host A's Room ID"
38
+ private var cancellables: Set<AnyCancellable> = []
39
+ private lazy var coHostStore: CoHostStore = {
40
+ return CoHostStore.create(liveID: self.liveId)
41
+ }()
42
+
43
+ // User clicks the "Co-host" button and selects Host B
44
+ func inviteHostB(targetHostLiveId: String) {
45
+ let layout: CoHostLayoutTemplate = .hostDynamicGrid // Choose a layout template
46
+ let timeout: TimeInterval = 30.0 // Invitation timeout
47
+
48
+ coHostStore.requestHostConnection(targetHost: targetHostLiveId,
49
+ layoutTemplate: layout,
50
+ timeout: timeout) { result in
51
+ switch result {
52
+ case .success():
53
+ print("Co-hosting invitation sent, waiting for response...")
54
+ case .failure(let error):
55
+ print("Failed to send invitation: \(error.message)")
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+ 2. **Listen for Invitation Result**
62
+
63
+
64
+ Subscribe to `coHostEventPublisher` to receive Host B's response.
65
+
66
+ ``` swift
67
+ // Set up listener during AnchorAViewController initialization
68
+ func setupListeners() {
69
+ coHostStore.coHostEventPublisher
70
+ .sink { [weak self] event in
71
+ switch event {
72
+ case .onCoHostRequestAccepted(let invitee):
73
+ print("Host \(invitee.userName) accepted your co-hosting invitation")
74
+ case .onCoHostRequestRejected(let invitee):
75
+ print("Host \(invitee.userName) rejected your invitation")
76
+ case .onCoHostRequestTimeout:
77
+ print("Invitation timed out, no response from the other party")
78
+ default:
79
+ break
80
+ }
81
+ }
82
+ .store(in: &cancellables)
83
+ }
84
+ ```
85
+
86
+ #### Invitee (Host B) Implementation
87
+ 1. **Receive Co-hosting Invitation**
88
+
89
+
90
+ Host B listens for invitations from Host A via `coHostEventPublisher`.
91
+
92
+ ``` swift
93
+ import AtomicXCore
94
+ import Combine
95
+
96
+ // Host B's view controller
97
+ class AnchorBViewController {
98
+ // ... coHostStore and cancellables initialization ...
99
+
100
+ // Set up listener during initialization
101
+ func setupListeners() {
102
+ coHostStore.coHostEventPublisher
103
+ .sink { [weak self] event in
104
+ if case let .onCoHostRequestReceived(inviter, _) = event {
105
+ print("Received co-hosting invitation from host \(inviter.userName)")
106
+ // self?.showInvitationDialog(from: inviter)
107
+ }
108
+ }
109
+ .store(in: &cancellables)
110
+ }
111
+ }
112
+ ```
113
+ 2. **Respond to Co-hosting Invitation**
114
+
115
+
116
+ After Host B receives the invitation, prompt for “Accept” or “Reject” and call the appropriate method:
117
+
118
+ ``` swift
119
+ // Part of AnchorBViewController
120
+ func acceptInvitation(fromHostLiveId: String) {
121
+ coHostStore.acceptHostConnection(fromHostLiveID: fromHostLiveId, completion: nil)
122
+ }
123
+
124
+ func rejectInvitation(fromHostLiveId: String) {
125
+ coHostStore.rejectHostConnection(fromHostLiveID: fromHostLiveId, completion: nil)
126
+ }
127
+ ```
128
+
129
+ ### Step 3: Implement Host PK
130
+
131
+ After a successful co-hosting connection, either host can initiate a PK. Use [BattleStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/battlestore) for PK functionality.
132
+
133
+ #### Challenger (e.g., Host A) Implementation
134
+ 1. **Initiate PK Challenge**
135
+
136
+
137
+ When Host A clicks the "PK" button, call the `requestBattle` method.
138
+
139
+ ``` swift
140
+ // Part of AnchorAViewController
141
+ private lazy var battleStore: BattleStore = BattleStore.create(liveID: self.liveId)
142
+
143
+ func startPK(with opponentUserId: String) {
144
+ var config = BattleConfig(duration: 300) // PK lasts 5 minutes
145
+ battleStore.requestBattle(config: config, userIDList: [opponentUserId], timeout: 30.0, completion: nil)
146
+ }
147
+ ```
148
+ 2. **Listen for PK Status**
149
+
150
+
151
+ Subscribe to `battleEventPublisher` to monitor key PK events such as start and end.
152
+
153
+ ``` swift
154
+ // Add to AnchorAViewController's setupListeners method
155
+ battleStore.battleEventPublisher
156
+ .sink { [weak self] event in
157
+ switch event {
158
+ case .onBattleStarted:
159
+ print("PK started")
160
+ case .onBattleEnded:
161
+ print("PK ended")
162
+ default:
163
+ break
164
+ }
165
+ }
166
+ .store(in: &cancellables)
167
+ ```
168
+
169
+ #### Opponent (Host B) Implementation
170
+ 1. **Receive PK Challenge**
171
+
172
+
173
+ Listen for PK invitations via `battleEventPublisher`.
174
+
175
+ ``` swift
176
+ // Add to AnchorBViewController's setupListeners method
177
+ battleStore.battleEventPublisher
178
+ .sink { [weak self] event in
179
+ if case let .onBattleRequestReceived(battleId, inviter, _) = event {
180
+ print("Received PK challenge from host \(inviter.userName)")
181
+ // Show dialog for Host B to choose "Accept" or "Reject"
182
+ // self?.showPKChallengeDialog(battleId: battleId)
183
+ }
184
+ }
185
+ .store(in: &cancellables)
186
+ ```
187
+ 2. **Respond to PK Challenge**
188
+
189
+
190
+ Prompt Host B for “Accept” or “Reject” and call the corresponding method:
191
+
192
+ ``` swift
193
+ // Part of AnchorBViewController
194
+ // User clicks "Accept Challenge"
195
+ func acceptPK(battleId: String) {
196
+ battleStore.acceptBattle(battleID: battleId) { result in
197
+ // ...
198
+ }
199
+ }
200
+
201
+ // User clicks "Reject Challenge"
202
+ func rejectPK(battleId: String) {
203
+ battleStore.rejectBattle(battleID: battleId) { result in
204
+ // ...
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Step 4: End PK and Disconnect Co-hosting
210
+
211
+ After the PK session, end the PK and disconnect co-hosting in sequence.
212
+ 1. **End PK Battle**
213
+
214
+
215
+ PK typically ends automatically when the timer expires, but hosts can also end PK early. Use `exitBattle`:
216
+
217
+
218
+ > **Note****:**After PK ends, both hosts remain connected (side-by-side video streams). Only the PK progress bar and score panel are removed.
219
+ >
220
+
221
+ ``` swift
222
+ func stopPK(battleId: String) {
223
+ battleStore.exitBattle(battleID: battleId) { result in
224
+ switch result {
225
+ case .success:
226
+ print("PK ended")
227
+ // UI receives onBattleEnded event; refresh UI in callback
228
+ case .failure(let error):
229
+ print("Failed to end PK: \(error.message)")
230
+ }
231
+ }
232
+ }
233
+ ```
234
+ 2. **Disconnect Cross-room Co-hosting**
235
+
236
+
237
+ To return to solo live streaming, call `exitHostConnection`:
238
+
239
+ ``` swift
240
+ func stopConnection() {
241
+ coHostStore.exitHostConnection { result in
242
+ switch result {
243
+ case .success:
244
+ print("Co-hosting disconnected, back to solo live")
245
+ // UI receives onCoHostUserLeft event
246
+ case .failure(let error):
247
+ print("Failed to disconnect co-hosting: \(error.message)")
248
+ }
249
+ }
250
+ }
251
+ ```
252
+ 3. **Listen for End Events**
253
+
254
+
255
+ Handle UI cleanup in event listeners for consistency:
256
+
257
+ ``` swift
258
+ func setupAdditionalListeners() {
259
+ // Listen for PK end event
260
+ battleStore.battleEventPublisher
261
+ .sink { [weak self] event in
262
+ if case .onBattleEnded(let battleInfo, let reason) = event {
263
+ print("Received PK end event, reason: \(reason)")
264
+ // self?.removeBattleUI()
265
+ }
266
+ }
267
+ .store(in: &cancellables)
268
+
269
+ // Listen for co-host disconnect event
270
+ coHostStore.coHostEventPublisher
271
+ .sink { [weak self] event in
272
+ if case .onCoHostUserLeft(let userInfo) = event {
273
+ print("Anchor \(userInfo.userName) has left co-hosting")
274
+ // self?.resetToSingleStreamLayout()
275
+ }
276
+ }
277
+ .store(in: &cancellables)
278
+ }
279
+ ```
280
+
281
+ ### Run and Test
282
+
283
+ After integrating the above features, use Host A and Host B to test the corresponding operations. The runtime effect is shown below. For UI customization, see [Refine UI Details](https://write.woa.com/#6fe270da-d3b3-4785-bdc2-53b59d6d9cc0).
284
+
285
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/1dbf6b48c6df11f0b011525400bf7822.png)
286
+
287
+
288
+ ## Refine UI Details
289
+
290
+ Use the slot capability in the `LiveCoreView.VideoViewDelegate` interface to overlay custom views on video streams, such as nicknames, avatars, PK progress bars, or placeholder images when the host’s camera is off.
291
+
292
+ ### Display Nicknames on Video Streams
293
+
294
+ #### Implementation Example
295
+
296
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/242d5611c6df11f0b011525400bf7822.png)
297
+
298
+
299
+ #### Implementation
300
+ - **Step 1**: Create a foreground view `CustomSeatView` to display user information above the video stream.
301
+
302
+
303
+ > **Note:**
304
+ >
305
+
306
+ > You can also refer to the open-source TUILiveKit files [AnchorCoHostView.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/Features/AnchorBoardcast/View/LivingView/Video/AnchorCoHostView.swift) and [AnchorEmptySeatView.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/Features/AnchorBoardcast/View/LivingView/Video/AnchorEmptySeatView.swift) for complete implementation logic.
307
+ >
308
+
309
+ ``` swift
310
+ import UIKit
311
+ import SnapKit
312
+
313
+ // Custom floating user info view (foreground)
314
+ class CustomSeatView: UIView {
315
+ lazy var nameLabel: UILabel = {
316
+ let label = UILabel()
317
+ label.textColor = .white
318
+ label.font = .systemFont(ofSize: 14)
319
+ return label
320
+ }()
321
+
322
+ override init(frame: CGRect) {
323
+ super.init(frame: frame)
324
+ backgroundColor = UIColor.black.withAlphaComponent(0.5)
325
+ addSubview(nameLabel)
326
+ nameLabel.snp.makeConstraints { make in
327
+ make.bottom.equalToSuperview().offset(-5)
328
+ make.leading.equalToSuperview().offset(5)
329
+ }
330
+ }
331
+ }
332
+ ```
333
+ - **Step 2**: Create a background view `CustomAvatarView` to use as a placeholder when the user has no video stream.
334
+
335
+ ``` swift
336
+ import UIKit
337
+ import SnapKit
338
+
339
+ // Custom avatar placeholder view (background)
340
+ class CustomAvatarView: UIView {
341
+ lazy var avatarImageView: UIImageView = {
342
+ let imageView = UIImageView()
343
+ imageView.tintColor = .gray
344
+ return imageView
345
+ }()
346
+
347
+ override init(frame: CGRect) {
348
+ super.init(frame: frame)
349
+ backgroundColor = .clear
350
+ layer.cornerRadius = 30
351
+ addSubview(avatarImageView)
352
+ avatarImageView.snp.makeConstraints { make in
353
+ make.center.equalToSuperview()
354
+ make.width.height.equalTo(60)
355
+ }
356
+ }
357
+ }
358
+ ```
359
+ - **Step 3**: Implement the `VideoViewDelegate.createCoHostView` protocol method and return the appropriate view based on `viewLayer`.
360
+
361
+ ``` swift
362
+ import AtomicXCore
363
+ import RTCRoomEngine
364
+
365
+ // 1. In your view controller, conform to the VideoViewDelegate protocol
366
+ class YourViewController: UIViewController, VideoViewDelegate {
367
+
368
+ // ... other code ...
369
+
370
+ // 2. Fully implement the protocol method, handling both viewLayer types
371
+ func createCoHostView(seatInfo: TUISeatFullInfo, viewLayer: ViewLayer) -> UIView? {
372
+ guard let userId = seatInfo.userId, !userId.isEmpty else {
373
+ return nil
374
+ }
375
+ if viewLayer == .foreground {
376
+ let seatView = CustomSeatView()
377
+ seatView.nameLabel.text = seatInfo.userName
378
+ return seatView
379
+ } else { // viewLayer == .background
380
+ let avatarView = CustomAvatarView()
381
+ // Optionally load the user's avatar using seatInfo.userAvatar
382
+ return avatarView
383
+ }
384
+ }
385
+ }
386
+ ```
387
+
388
+ #### Parameter Description:
389
+ <table>
390
+ <tr>
391
+ <td rowspan="1" colSpan="1" >**Parameter**</td>
392
+
393
+ <td rowspan="1" colSpan="1" >**Type**</td>
394
+
395
+ <td rowspan="1" colSpan="1" >**Description**</td>
396
+ </tr>
397
+
398
+ <tr>
399
+ <td rowspan="1" colSpan="1" >`seatInfo`</td>
400
+
401
+ <td rowspan="1" colSpan="1" >`TUISeatFullInfo`</td>
402
+
403
+ <td rowspan="1" colSpan="1" >Seat information object containing user details</td>
404
+ </tr>
405
+
406
+ <tr>
407
+ <td rowspan="1" colSpan="1" >`seatInfo.userId`</td>
408
+
409
+ <td rowspan="1" colSpan="1" >`String?`</td>
410
+
411
+ <td rowspan="1" colSpan="1" >User ID on the seat</td>
412
+ </tr>
413
+
414
+ <tr>
415
+ <td rowspan="1" colSpan="1" >`seatInfo.userName`</td>
416
+
417
+ <td rowspan="1" colSpan="1" >`String?`</td>
418
+
419
+ <td rowspan="1" colSpan="1" >User nickname on the seat</td>
420
+ </tr>
421
+
422
+ <tr>
423
+ <td rowspan="1" colSpan="1" >`seatInfo.userAvatar`</td>
424
+
425
+ <td rowspan="1" colSpan="1" >`String?`</td>
426
+
427
+ <td rowspan="1" colSpan="1" >User avatar URL</td>
428
+ </tr>
429
+
430
+ <tr>
431
+ <td rowspan="1" colSpan="1" >`seatInfo.userMicrophoneStatus`</td>
432
+
433
+ <td rowspan="1" colSpan="1" >`TUIDeviceStatus`</td>
434
+
435
+ <td rowspan="1" colSpan="1" >User microphone status</td>
436
+ </tr>
437
+
438
+ <tr>
439
+ <td rowspan="1" colSpan="1" >`seatInfo.userCameraStatus`</td>
440
+
441
+ <td rowspan="1" colSpan="1" >`TUIDeviceStatus`</td>
442
+
443
+ <td rowspan="1" colSpan="1" >User camera status</td>
444
+ </tr>
445
+
446
+ <tr>
447
+ <td rowspan="1" colSpan="1" >`viewLayer`</td>
448
+
449
+ <td rowspan="1" colSpan="1" >`ViewLayer`</td>
450
+
451
+ <td rowspan="1" colSpan="1" >View layer enum<br>- `.foreground`: Foreground widget view, always displayed on top of the video<br>- `.background`: Background widget view, under the foreground view, displayed only when the user has no video stream (e.g., camera off), typically used for the user's default avatar or a placeholder image</td>
452
+ </tr>
453
+ </table>
454
+
455
+
456
+ ### Displaying PK User Score on Video View
457
+
458
+ When the host starts a PK, you can attach a custom view to the opponent's video to display gift values or other PK-related information.
459
+
460
+ #### Implementation Example
461
+
462
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/2c75755dc6df11f0b011525400bf7822.png)
463
+
464
+
465
+ #### Implementation
466
+ - **Step 1**: Create a custom PK user view. For a complete implementation, refer to [AnchorBattleMemberInfoView.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/Features/AnchorBoardcast/View/HostBattle/View/AnchorBattleMemberInfoView.swift).
467
+
468
+ ``` swift
469
+ import AtomicXCore
470
+ import RTCRoomEngine
471
+ import SnapKit
472
+
473
+ // Custom PK user view
474
+ class CustomBattleUserView: UIView {
475
+ private let scoreView: UIView = {
476
+ let view = UIView()
477
+ view.backgroundColor = .black.withAlphaComponent(0.4)
478
+ view.layer.cornerRadius = 12
479
+ return view
480
+ }()
481
+
482
+ private lazy var scoreLabel: UILabel = {
483
+ let label = UILabel()
484
+ label.textColor = .white
485
+ label.font = .systemFont(ofSize: 14, weight: .bold)
486
+ return label
487
+ }()
488
+
489
+ private var userId: String
490
+ private let battleStore: BattleStore
491
+ private var cancellableSet: Set<AnyCancellable> = []
492
+
493
+ init(liveId: String, battleUser: TUIBattleUser) {
494
+ self.userId = battleUser.userId
495
+ self.battleStore = BattleStore.create(liveID: liveId)
496
+ super.init(frame: .zero)
497
+ backgroundColor = .clear
498
+ isUserInteractionEnabled = false
499
+ setupUI()
500
+ subscribeBattleState()
501
+ }
502
+
503
+ private func setupUI() {
504
+ addSubview(scoreView)
505
+ scoreView.addSubview(scoreLabel)
506
+ scoreLabel.snp.makeConstraints { make in
507
+ make.leading.trailing.equalToSuperview().inset(5)
508
+ }
509
+ scoreView.snp.makeConstraints { make in
510
+ make.height.equalTo(24)
511
+ make.bottom.equalToSuperview().offset(-5)
512
+ make.trailing.equalToSuperview().offset(-5)
513
+ }
514
+ }
515
+
516
+ // Subscribe to PK score changes
517
+ private func subscribeBattleState() {
518
+ battleStore.state
519
+ .subscribe(StatePublisherSelector(keyPath: \BattleState.battleScore))
520
+ .removeDuplicates()
521
+ .receive(on: RunLoop.main)
522
+ .sink { battleScore in
523
+ guard let score = battleScore[self.userId] else { return }
524
+ self.scoreLabel.text = "\(score)"
525
+ }
526
+ .store(in: &cancellableSet)
527
+ }
528
+ }
529
+ ```
530
+ - **Step 2**: Implement the `VideoViewDelegate.createBattleView` protocol.
531
+
532
+ ``` swift
533
+ // 1. Let your view controller conform to the VideoViewDelegate protocol
534
+ extension YourViewController: VideoViewDelegate {
535
+
536
+ public func createBattleView(battleUser: TUIBattleUser) -> UIView? {
537
+ // CustomBattleUserView is your custom PK user info view
538
+ let customView = CustomBattleUserView(liveId: liveId, battleUser: battleUser)
539
+ return customView
540
+ }
541
+
542
+ }
543
+ ```
544
+
545
+ #### Parameter Description:
546
+ <table>
547
+ <tr>
548
+ <td rowspan="1" colSpan="1" >**Parameter**</td>
549
+
550
+ <td rowspan="1" colSpan="1" >**Type**</td>
551
+
552
+ <td rowspan="1" colSpan="1" >**Description**</td>
553
+ </tr>
554
+
555
+ <tr>
556
+ <td rowspan="1" colSpan="1" >`battleUser`</td>
557
+
558
+ <td rowspan="1" colSpan="1" >`TUIBattleUser`</td>
559
+
560
+ <td rowspan="1" colSpan="1" >PK user info object</td>
561
+ </tr>
562
+
563
+ <tr>
564
+ <td rowspan="1" colSpan="1" >`battleUser.roomId`</td>
565
+
566
+ <td rowspan="1" colSpan="1" >`String`</td>
567
+
568
+ <td rowspan="1" colSpan="1" >PK room ID</td>
569
+ </tr>
570
+
571
+ <tr>
572
+ <td rowspan="1" colSpan="1" >`battleUser.userId`</td>
573
+
574
+ <td rowspan="1" colSpan="1" >`String`</td>
575
+
576
+ <td rowspan="1" colSpan="1" >PK user ID</td>
577
+ </tr>
578
+
579
+ <tr>
580
+ <td rowspan="1" colSpan="1" >`battleUser.userName`</td>
581
+
582
+ <td rowspan="1" colSpan="1" >`String`</td>
583
+
584
+ <td rowspan="1" colSpan="1" >PK user nickname</td>
585
+ </tr>
586
+
587
+ <tr>
588
+ <td rowspan="1" colSpan="1" >`battleUser.avatarUrl`</td>
589
+
590
+ <td rowspan="1" colSpan="1" >`String`</td>
591
+
592
+ <td rowspan="1" colSpan="1" >PK user avatar URL</td>
593
+ </tr>
594
+
595
+ <tr>
596
+ <td rowspan="1" colSpan="1" >`battleUser.score`</td>
597
+
598
+ <td rowspan="1" colSpan="1" >`UInt`</td>
599
+
600
+ <td rowspan="1" colSpan="1" >PK score</td>
601
+ </tr>
602
+ </table>
603
+
604
+
605
+ ### Displaying PK Status on Video Stream
606
+
607
+ #### Implementation Example
608
+
609
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/36561327c6df11f0a4a55254001c06ec.png)
610
+
611
+
612
+ #### Implementation
613
+ - **Step 1**: Create a custom PK global view (CustomBattleContainerView). For a complete implementation, refer to [AnchorBattleInfoView.swift](https://github.com/Tencent-RTC/TUIKit_iOS/blob/main/live/Sources/Features/AnchorBoardcast/View/HostBattle/View/AnchorBattleInfoView.swift).
614
+
615
+ - **Step 2**: Implement the `VideoViewDelegate.createBattleContainerView` protocol.
616
+
617
+ ``` swift
618
+ // Let your view controller conform to the VideoViewDelegate protocol and set the delegate
619
+ extension YourViewController: VideoViewDelegate {
620
+ func createBattleContainerView() -> UIView? {
621
+ return CustomBattleContainerView()
622
+ }
623
+ }
624
+ ```
625
+
626
+ ## Advanced Features
627
+
628
+ ### Updating PK Score via REST API
629
+
630
+ In typical** live host PK scenarios,** the value of gifts received by the host is linked to the PK score (for example, when a viewer sends a "Rocket" gift, the host's PK score increases by 500 points). You can implement real-time PK score updates using our REST API.
631
+
632
+ > Note:
633
+ >
634
+
635
+ > The PK score system in the LiveKit backend uses pure numeric calculation and accumulation. You must calculate the PK score according to your own business logic before calling the update API. See the following PK score calculation examples:
636
+ >
637
+ <table>
638
+ <tr>
639
+ <td rowspan="1" colSpan="1" >Gift Type</td>
640
+
641
+ <td rowspan="1" colSpan="1" >Score Calculation Rule</td>
642
+
643
+ <td rowspan="1" colSpan="1" >Example</td>
644
+ </tr>
645
+
646
+ <tr>
647
+ <td rowspan="1" colSpan="1" >Basic Gift</td>
648
+
649
+ <td rowspan="1" colSpan="1" >Gift value × 5</td>
650
+
651
+ <td rowspan="1" colSpan="1" >10 RMB gift → 50 points</td>
652
+ </tr>
653
+
654
+ <tr>
655
+ <td rowspan="1" colSpan="1" >Intermediate Gift</td>
656
+
657
+ <td rowspan="1" colSpan="1" >Gift value × 8</td>
658
+
659
+ <td rowspan="1" colSpan="1" >50 RMB gift → 400 points</td>
660
+ </tr>
661
+
662
+ <tr>
663
+ <td rowspan="1" colSpan="1" >Advanced Gift</td>
664
+
665
+ <td rowspan="1" colSpan="1" >Gift value × 12</td>
666
+
667
+ <td rowspan="1" colSpan="1" >100 RMB gift → 1200 points</td>
668
+ </tr>
669
+
670
+ <tr>
671
+ <td rowspan="1" colSpan="1" >Special Effect Gift</td>
672
+
673
+ <td rowspan="1" colSpan="1" >Fixed high score</td>
674
+
675
+ <td rowspan="1" colSpan="1" >520 RMB gift → 1314 points</td>
676
+ </tr>
677
+ </table>
678
+
679
+
680
+
681
+ #### REST API Call Flow
682
+
683
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100027182498/4043ba3cc6df11f0b011525400bf7822.png)
684
+
685
+
686
+ #### Key Process Description
687
+ 1. **Obtain PK Status:**
688
+
689
+ - **Callback Configuration**: Configure [PK Status Callback](https://write.woa.com/document/170203265552953344) to have the `LiveKit` backend actively notify your system when PK starts or ends.
690
+
691
+ - **Active Query**: Your backend can call the [PK Status Query](https://write.woa.com/document/170220847572692992) API at any time to check the current PK status.
692
+
693
+ 2. **PK Score Calculation**: Your backend calculates the PK score increment based on your business rules.
694
+
695
+ 3. **PK Score Update**: Your backend calls the [Update PK Score](https://write.woa.com/document/170220034100383744) API to update the PK score in the LiveKit backend.
696
+
697
+ 4. **LiveKit Backend Syncs to Client**: The backend automatically synchronizes the updated PK score to all clients.
698
+
699
+
700
+ #### Involved REST API Endpoints
701
+ <table>
702
+ <tr>
703
+ <td rowspan="1" colSpan="1" >**API**</td>
704
+
705
+ <td rowspan="1" colSpan="1" >**Function Description**</td>
706
+
707
+ <td rowspan="1" colSpan="1" >**Request Example**</td>
708
+ </tr>
709
+
710
+ <tr>
711
+ <td rowspan="1" colSpan="1" >Active API - Query PK Status</td>
712
+
713
+ <td rowspan="1" colSpan="1" >Check whether the current room is in PK</td>
714
+
715
+ <td rowspan="1" colSpan="1" >[Example](https://write.woa.com/document/170220847572692992)</td>
716
+ </tr>
717
+
718
+ <tr>
719
+ <td rowspan="1" colSpan="1" >Active API - Update PK Score</td>
720
+
721
+ <td rowspan="1" colSpan="1" >Update the calculated PK score</td>
722
+
723
+ <td rowspan="1" colSpan="1" >[Example](https://write.woa.com/document/170220034100383744)</td>
724
+ </tr>
725
+
726
+ <tr>
727
+ <td rowspan="1" colSpan="1" >Callback Configuration - PK Start Callback</td>
728
+
729
+ <td rowspan="1" colSpan="1" >Receive real-time notification when PK starts</td>
730
+
731
+ <td rowspan="1" colSpan="1" >[Example](https://write.woa.com/document/170203265552953344)</td>
732
+ </tr>
733
+
734
+ <tr>
735
+ <td rowspan="1" colSpan="1" >Callback Configuration - PK End Callback</td>
736
+
737
+ <td rowspan="1" colSpan="1" >Receive real-time notification when PK ends</td>
738
+
739
+ <td rowspan="1" colSpan="1" >[Example](https://write.woa.com/document/170221608179601408)</td>
740
+ </tr>
741
+ </table>
742
+
743
+
744
+ ## API Documentation
745
+
746
+ For detailed information on all public interfaces, properties, and methods of [CoHostStore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore/cohoststore) and related classes, refer to the official API documentation included with the [AtomicXCore](https://tencent-rtc.github.io/TUIKit_iOS/documentation/atomicxcore) framework. The relevant stores used in this guide are as follows:
747
+ <table>
748
+ <tr>
749
+ <td rowspan="1" colSpan="1" >**Store/Component**</td>
750
+
751
+ <td rowspan="1" colSpan="1" >**Description**</td>
752
+
753
+ <td rowspan="1" colSpan="1" >**API Reference**</td>
754
+ </tr>
755
+
756
+ <tr>
757
+ <td rowspan="1" colSpan="1" >`LiveCoreView`</td>
758
+
759
+ <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>
760
+
761
+ <td rowspan="1" colSpan="1" >[API Documentation](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)</td>
762
+ </tr>
763
+
764
+ <tr>
765
+ <td rowspan="1" colSpan="1" >`DeviceStore`</td>
766
+
767
+ <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>
768
+
769
+ <td rowspan="1" colSpan="1" >[API Documentation](https://tencent-rtc.github.io/TUIKit_Android/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.device/-device-store/index.html)</td>
770
+ </tr>
771
+
772
+ <tr>
773
+ <td rowspan="1" colSpan="1" >`CoHostStore`</td>
774
+
775
+ <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>
776
+
777
+ <td rowspan="1" colSpan="1" >[API Documentation](https://tencent-rtc.github.io/TUIKit_Android/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.live/-co-host-store/index.html)</td>
778
+ </tr>
779
+
780
+ <tr>
781
+ <td rowspan="1" colSpan="1" >`BattleStore`</td>
782
+
783
+ <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>
784
+
785
+ <td rowspan="1" colSpan="1" >[API Documentation](https://tencent-rtc.github.io/TUIKit_Android/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.live/-battle-store/index.html)</td>
786
+ </tr>
787
+ </table>
788
+
789
+
790
+ ## FAQs
791
+
792
+ ### Why didn't the other party receive my co-hosting invitation?
793
+ - Ensure that `targetHostLiveId` is correct and that the other host's live room is broadcasting normally.
794
+
795
+ - Check network stability. The invitation signaling has a default timeout of 30 seconds.
796
+
797
+
798
+ ### What happens if one host disconnects from the network or the app crashes during co-hosting or PK?
799
+
800
+ Both `CoHostStore` and `BattleStore` include built-in heartbeat and timeout detection. If one party exits abnormally, the other party is notified via events such as `onCoHostUserLeft` or `onUserExitBattle`. You can update the UI accordingly, for example, by displaying "The other party has disconnected" and ending the interaction.
801
+
802
+ ### Why can PK scores only be updated via REST API?
803
+
804
+ The REST API meets the security, real-time, and scalability requirements for PK scores:
805
+ - **Tamper-proof and fair**: Requires authentication and data validation. Each update is traceable (e.g., tied to a gift action), preventing manual score changes or cheating and ensuring fair competition.
806
+
807
+ - **Real-time synchronization across multiple clients**: Uses standardized formats (such as JSON) to quickly connect gift, PK, and display systems, ensuring real-time consistency of scores among hosts, viewers, and backend.
808
+
809
+ - **Flexible rule adaptation**: Backend configuration changes (such as adjusting gift-to-score mapping or bonus points) can be made without modifying the frontend, reducing iteration costs.
810
+
811
+
812
+ ### How do I manage the lifecycle and events of custom views added via `VideoViewDelegate`?
813
+
814
+ LiveCoreView automatically manages views returned by delegate methods, including adding and removing them. No manual lifecycle management is required.
815
+