@tencent-rtc/mcp 1.3.6 → 1.3.8
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/package.json +2 -1
- package/resource/doc/livekit/live/flutter/live-audience-list.md +339 -0
- package/resource/doc/livekit/live/flutter/live-audio-effect.md +742 -0
- package/resource/doc/livekit/live/flutter/live-barrage.md +581 -0
- package/resource/doc/livekit/live/flutter/live-base-beauty.md +486 -0
- package/resource/doc/livekit/live/flutter/live-co-guest-interaction.md +608 -0
- package/resource/doc/livekit/live/flutter/live-gift.md +800 -0
- package/resource/doc/livekit/live/flutter/live-host-interaction.md +1113 -0
- package/resource/doc/livekit/live/flutter/live-integration.md +1041 -0
- package/resource/doc/livekit/live/flutter/live-live-list.md +865 -0
- package/resource/doc/livekit/live/flutter/workflow.md +353 -0
- package/tools/common/record-prompt.js +31 -0
- package/tools/common/record-prompt.js.map +1 -1
- package/tools/common/record-result.js +32 -0
- package/tools/common/record-result.js.map +1 -1
- package/tools/definitions/record_result_def.js +18 -10
- package/tools/definitions/record_result_def.js.map +1 -1
- package/utils/get-device-identifier.js +58 -0
- package/utils/get-device-identifier.js.map +1 -0
- package/utils/report-cls-client.js +4 -2
- package/utils/report-cls-client.js.map +1 -1
- package/utils/report-es-client.js +1 -1
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
This document aims to guide Flutter developers on how to use the `BarrageStore` module in the `AtomicXCore` framework to quickly integrate a bullet screen system with various features and outstanding performance for your live stream app.
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Core Features
|
|
7
|
+
|
|
8
|
+
`BarrageStore` provides a complete bullet screen solution for your live stream app. Core features include:
|
|
9
|
+
- Receive and show danmakus information in live rooms.
|
|
10
|
+
|
|
11
|
+
- Send text bullet screen for audience interaction.
|
|
12
|
+
|
|
13
|
+
- Send custom business bullet screen to support complex scenarios such as gift and likes.
|
|
14
|
+
|
|
15
|
+
- Insert a system prompt in the local message list (for example, "Welcome XX to enter live room").
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Core Concept Analysis
|
|
19
|
+
<table>
|
|
20
|
+
<tr>
|
|
21
|
+
<td rowspan="1" colSpan="1" >**Core Concept**</td>
|
|
22
|
+
|
|
23
|
+
<td rowspan="1" colSpan="1" >**Type**</td>
|
|
24
|
+
|
|
25
|
+
<td rowspan="1" colSpan="1" >**Core Responsibility and Description**</td>
|
|
26
|
+
</tr>
|
|
27
|
+
|
|
28
|
+
<tr>
|
|
29
|
+
<td rowspan="1" colSpan="1" >[Barrage](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/Barrage-class.html)</td>
|
|
30
|
+
|
|
31
|
+
<td rowspan="1" colSpan="1" >`class`</td>
|
|
32
|
+
|
|
33
|
+
<td rowspan="1" colSpan="1" >Represents a data model for a Danmu Message. It contains all key information such as sender information (`sender`), message content (`textContent` or `data`), and message type (`messageType`).</td>
|
|
34
|
+
</tr>
|
|
35
|
+
|
|
36
|
+
<tr>
|
|
37
|
+
<td rowspan="1" colSpan="1" >[BarrageState](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/BarrageState-class.html)</td>
|
|
38
|
+
|
|
39
|
+
<td rowspan="1" colSpan="1" >`class`</td>
|
|
40
|
+
|
|
41
|
+
<td rowspan="1" colSpan="1" >Represents the current status of the bullet screen module. Its core attribute `messageList` is a `ValueListenable<List<Barrage>>`, storing all Danmu messages in the live streaming room in chronological order, serving as the data source for UI rendering.</td>
|
|
42
|
+
</tr>
|
|
43
|
+
|
|
44
|
+
<tr>
|
|
45
|
+
<td rowspan="1" colSpan="1" >[BarrageStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/BarrageStore-class.html)</td>
|
|
46
|
+
|
|
47
|
+
<td rowspan="1" colSpan="1" >`class`</td>
|
|
48
|
+
|
|
49
|
+
<td rowspan="1" colSpan="1" >This is the core management class for danmu function interaction. Through it, you can send messages (`sendTextMessage`, `sendCustomMessage`) and receive and listen to ALL Danmu Message updates by listening to its `barrageState.messageList` property.</td>
|
|
50
|
+
</tr>
|
|
51
|
+
</table>
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Implementation Steps
|
|
55
|
+
|
|
56
|
+
### Step 1: Integrating the Component
|
|
57
|
+
- **Live streaming**: Refer to [quick integration](https://write.woa.com/document/200455640177692672) for seamless integration with **AtomicXCore** and service access.
|
|
58
|
+
|
|
59
|
+
- **Voice chat room**: Refer to [quick integration](https://write.woa.com/document/200555778169073664) for seamless integration with **AtomicXCore** and service access.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Return to the current project after integration.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### Step 2: Initialize and Listen to the Bullet Screen
|
|
66
|
+
|
|
67
|
+
Retrieve a `BarrageStore` instance bound to the current live streaming room `liveId`, and set a listener to receive the latest **Full** barrage message list in real time.
|
|
68
|
+
``` java
|
|
69
|
+
import 'package:flutter/foundation.dart';
|
|
70
|
+
import 'package:atomic_x_core/atomicxcore.dart';
|
|
71
|
+
|
|
72
|
+
class BarrageManager {
|
|
73
|
+
final String liveId;
|
|
74
|
+
late final BarrageStore barrageStore;
|
|
75
|
+
late final VoidCallback _messageListChangedListener = _onMessageListChanged;
|
|
76
|
+
|
|
77
|
+
// Expose message list change notification to the public
|
|
78
|
+
final ValueNotifier<List<Barrage>> messagesNotifier =
|
|
79
|
+
ValueNotifier<List<Barrage>>([]);
|
|
80
|
+
|
|
81
|
+
BarrageManager({required this.liveId}) {
|
|
82
|
+
// 1. Get the singleton of BarrageStore by liveId (location parameter)
|
|
83
|
+
barrageStore = BarrageStore.create(liveId);
|
|
84
|
+
|
|
85
|
+
// 2. Start listening to the bullet screen immediately after initialization
|
|
86
|
+
_subscribeToBarrageUpdates();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void _subscribeToBarrageUpdates() {
|
|
90
|
+
// 3. Use ValueListenable's addListener to listen for messages list adjustment
|
|
91
|
+
barrageStore.barrageState.messageList.addListener(_messageListChangedListener);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void _onMessageListChanged() {
|
|
95
|
+
// 4. When the messageList is updating, get the new list and notify the UI layer
|
|
96
|
+
// Key point: The complete list obtained here contains ALL historical messages
|
|
97
|
+
messagesNotifier.value = barrageStore.barrageState.messageList.value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
void dispose() {
|
|
101
|
+
barrageStore.barrageState.messageList.removeListener(_messageListChangedListener);
|
|
102
|
+
messagesNotifier.dispose();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Step 3: Send Text Bullet Screen
|
|
108
|
+
|
|
109
|
+
Call the `sendTextMessage` method to broadcast a text message to all users in the live streaming room.
|
|
110
|
+
``` java
|
|
111
|
+
extension BarrageManagerSend on BarrageManager {
|
|
112
|
+
Send a text bullet screen
|
|
113
|
+
Future<void> sendTextMessage(String text) async {
|
|
114
|
+
// Recommendation: Add non-null verification to avoid sending invalid messages.
|
|
115
|
+
if (text.isEmpty) {
|
|
116
|
+
debugPrint("Bullet screen content cannot be empty");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Call the core API to sendMessage
|
|
121
|
+
final result = await barrageStore.sendTextMessage(
|
|
122
|
+
text: text,
|
|
123
|
+
extensionInfo: null,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Use isSuccess to check the result
|
|
127
|
+
if (result.isSuccess) {
|
|
128
|
+
debugPrint("Bullet screen text '$text' sent successfully");
|
|
129
|
+
} else {
|
|
130
|
+
debugPrint("Bullet screen text sending failure: ${result.errorMessage}");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**API parameter:**
|
|
137
|
+
<table>
|
|
138
|
+
<tr>
|
|
139
|
+
<td rowspan="1" colSpan="1" >**Parameter**</td>
|
|
140
|
+
|
|
141
|
+
<td rowspan="1" colSpan="1" >**Type**</td>
|
|
142
|
+
|
|
143
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
144
|
+
</tr>
|
|
145
|
+
|
|
146
|
+
<tr>
|
|
147
|
+
<td rowspan="1" colSpan="1" >`text`</td>
|
|
148
|
+
|
|
149
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
150
|
+
|
|
151
|
+
<td rowspan="1" colSpan="1" >The text content to be sent.</td>
|
|
152
|
+
</tr>
|
|
153
|
+
|
|
154
|
+
<tr>
|
|
155
|
+
<td rowspan="1" colSpan="1" >`extensionInfo`</td>
|
|
156
|
+
|
|
157
|
+
<td rowspan="1" colSpan="1" >`Map<String, String>?`</td>
|
|
158
|
+
|
|
159
|
+
<td rowspan="1" colSpan="1" >Attach extra extended information applicable to business customization.</td>
|
|
160
|
+
</tr>
|
|
161
|
+
</table>
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
### Step 4: Send Custom Barrage Item
|
|
165
|
+
|
|
166
|
+
Send a message containing custom business logic, such as gift, like or gaming interaction command. This message is transparent to other clients and requires the business layer to parse.
|
|
167
|
+
``` java
|
|
168
|
+
import 'dart:convert';
|
|
169
|
+
|
|
170
|
+
extension BarrageManagerCustom on BarrageManager {
|
|
171
|
+
/// Send a custom barrage item, for example for sending a gift
|
|
172
|
+
Future<void> sendGiftMessage({
|
|
173
|
+
required String giftId,
|
|
174
|
+
required int giftCount,
|
|
175
|
+
}) async {
|
|
176
|
+
// 1. Define a business ID
|
|
177
|
+
const businessID = "live_gift";
|
|
178
|
+
|
|
179
|
+
// 2. Encode business data as a JSON string
|
|
180
|
+
final giftData = {"gift_id": giftId, "gift_count": giftCount};
|
|
181
|
+
final jsonString = jsonEncode(giftData);
|
|
182
|
+
|
|
183
|
+
// 3. Call the core API to send custom messages
|
|
184
|
+
final result = await barrageStore.sendCustomMessage(
|
|
185
|
+
businessID: businessID,
|
|
186
|
+
data: jsonString,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (result.isSuccess) {
|
|
190
|
+
debugPrint("Gift message (custom barrage item) sent successfully");
|
|
191
|
+
} else {
|
|
192
|
+
debugPrint("Gift message send fail: ${result.errorMessage}");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**API parameter:**
|
|
199
|
+
<table>
|
|
200
|
+
<tr>
|
|
201
|
+
<td rowspan="1" colSpan="1" >**Parameter**</td>
|
|
202
|
+
|
|
203
|
+
<td rowspan="1" colSpan="1" >**Type**</td>
|
|
204
|
+
|
|
205
|
+
<td rowspan="1" colSpan="1" >**Description**</td>
|
|
206
|
+
</tr>
|
|
207
|
+
|
|
208
|
+
<tr>
|
|
209
|
+
<td rowspan="1" colSpan="1" >`businessID`</td>
|
|
210
|
+
|
|
211
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
212
|
+
|
|
213
|
+
<td rowspan="1" colSpan="1" >Business unique identifier, such as "live_gift", used for recipient to distinguish different custom messages.</td>
|
|
214
|
+
</tr>
|
|
215
|
+
|
|
216
|
+
<tr>
|
|
217
|
+
<td rowspan="1" colSpan="1" >`data`</td>
|
|
218
|
+
|
|
219
|
+
<td rowspan="1" colSpan="1" >`String`</td>
|
|
220
|
+
|
|
221
|
+
<td rowspan="1" colSpan="1" >Business data, typically strings in JSON format.</td>
|
|
222
|
+
</tr>
|
|
223
|
+
</table>
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
### Step 5: Insert Prompt Message Locally
|
|
227
|
+
|
|
228
|
+
Insert a local message into the current user's message list. This message will not be sent to other users in the live streaming room. It is suitable for displaying system welcome, warning or operation prompt information.
|
|
229
|
+
``` java
|
|
230
|
+
extension BarrageManagerLocal on BarrageManager {
|
|
231
|
+
Insert a welcome notification into the local message list
|
|
232
|
+
void showWelcomeMessage(LiveUserInfo user) {
|
|
233
|
+
// 1. Create a Barrage message (using named parameters construct function)
|
|
234
|
+
final welcomeTip = Barrage(
|
|
235
|
+
messageType: BarrageType.text,
|
|
236
|
+
Welcome ${user.userName} to the live room.
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// 2. Call API to insert it into the local list
|
|
240
|
+
barrageStore.appendLocalTip(welcomeTip);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Step 6: Manage User Speaking (Mute and Unblock)
|
|
246
|
+
|
|
247
|
+
As an Anchor or admin, you can manage the user speaking permission in the live streaming room to maintain a healthy communication environment.
|
|
248
|
+
|
|
249
|
+
#### Forbid/Unblock Single-User Speaking
|
|
250
|
+
|
|
251
|
+
Mute or unblock designated users can be achieved by the `disableSendMessage` API in `LiveAudienceStore`.
|
|
252
|
+
``` java
|
|
253
|
+
// 1. Get the LiveAudienceStore instance bound to the current live streaming room (location parameter)
|
|
254
|
+
final liveId = "your_live_id";
|
|
255
|
+
final audienceStore = LiveAudienceStore.create(liveId);
|
|
256
|
+
|
|
257
|
+
// 2. Define the operating user ID and mute status
|
|
258
|
+
final userIdToMute = "user_id_to_be_muted";
|
|
259
|
+
final shouldDisable = true; // true for mute, false for unblock
|
|
260
|
+
|
|
261
|
+
// 3. Call API to perform operation
|
|
262
|
+
final result = await audienceStore.disableSendMessage(
|
|
263
|
+
userID: userIdToMute,
|
|
264
|
+
isDisable: shouldDisable,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (result.isSuccess) {
|
|
268
|
+
debugPrint("${shouldDisable ? "mute" : "unblock"} user $userIdToMute successfully");
|
|
269
|
+
} else {
|
|
270
|
+
debugPrint("Operation failure: ${result.errorMessage}");
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Enable/Disable Global Mute
|
|
275
|
+
|
|
276
|
+
To mute all users in the live streaming room (normally excluding the Anchor), you need to update the live room information through `LiveListStore`.
|
|
277
|
+
``` java
|
|
278
|
+
// 1. Get the LiveListStore singleton
|
|
279
|
+
final liveListStore = LiveListStore.shared;
|
|
280
|
+
|
|
281
|
+
// 2. Get the current live room information, create a new LiveInfo object, and modify the global mute status
|
|
282
|
+
final currentLiveInfo = liveListStore.liveState.currentLive.value;
|
|
283
|
+
final updatedLiveInfo = LiveInfo(
|
|
284
|
+
liveID: currentLiveInfo.liveID,
|
|
285
|
+
liveName: currentLiveInfo.liveName,
|
|
286
|
+
isMessageDisable: true, // true for mute all, false for shutdown
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// 3. Call the update API and specify the modify flags list
|
|
290
|
+
final result = await liveListStore.updateLiveInfo(
|
|
291
|
+
liveInfo: updatedLiveInfo,
|
|
292
|
+
modifyFlagList: [ModifyFlag.isMessageDisable],
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
if (result.isSuccess) {
|
|
296
|
+
debugPrint("Global mute status updated successfully");
|
|
297
|
+
} else {
|
|
298
|
+
debugPrint("Operation failure: ${result.errorMessage}");
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Complete UI Example
|
|
303
|
+
``` java
|
|
304
|
+
import 'package:flutter/material.dart';
|
|
305
|
+
import 'package:atomic_x_core/atomicxcore.dart';
|
|
306
|
+
|
|
307
|
+
class BarrageWidget extends StatefulWidget {
|
|
308
|
+
final String liveId;
|
|
309
|
+
|
|
310
|
+
const BarrageWidget({Key? key, required this.liveId}) : super(key: key);
|
|
311
|
+
|
|
312
|
+
@override
|
|
313
|
+
State<BarrageWidget> createState() => _BarrageWidgetState();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
class _BarrageWidgetState extends State<BarrageWidget> {
|
|
317
|
+
late BarrageManager _barrageManager;
|
|
318
|
+
final TextEditingController _inputController = TextEditingController();
|
|
319
|
+
final ScrollController _scrollController = ScrollController();
|
|
320
|
+
|
|
321
|
+
@override
|
|
322
|
+
void initState() {
|
|
323
|
+
super.initState();
|
|
324
|
+
_barrageManager = BarrageManager(liveId: widget.liveId);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
void _scrollToBottom() {
|
|
328
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
329
|
+
if (_scrollController.hasClients) {
|
|
330
|
+
_scrollController.animateTo(
|
|
331
|
+
_scrollController.position.maxScrollExtent,
|
|
332
|
+
duration: const Duration(milliseconds: 200),
|
|
333
|
+
curve: Curves.easeOut,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@override
|
|
340
|
+
void dispose() {
|
|
341
|
+
_barrageManager.dispose();
|
|
342
|
+
_inputController.dispose();
|
|
343
|
+
_scrollController.dispose();
|
|
344
|
+
super.dispose();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@override
|
|
348
|
+
Widget build(BuildContext context) {
|
|
349
|
+
return Column(
|
|
350
|
+
children: [
|
|
351
|
+
// Bullet screen list
|
|
352
|
+
Expanded(
|
|
353
|
+
child: ValueListenableBuilder<List<Barrage>>(
|
|
354
|
+
valueListenable: _barrageManager.messagesNotifier,
|
|
355
|
+
builder: (context, messageList, child) {
|
|
356
|
+
// Scroll to the bottom
|
|
357
|
+
_scrollToBottom();
|
|
358
|
+
return ListView.builder(
|
|
359
|
+
controller: _scrollController,
|
|
360
|
+
itemCount: messageList.length,
|
|
361
|
+
itemBuilder: (context, index) {
|
|
362
|
+
final barrage = messageList[index];
|
|
363
|
+
return _buildBarrageItem(barrage);
|
|
364
|
+
},
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
),
|
|
368
|
+
),
|
|
369
|
+
// input box
|
|
370
|
+
_buildInputBar(),
|
|
371
|
+
],
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
Widget _buildBarrageItem(Barrage barrage) {
|
|
376
|
+
return Container(
|
|
377
|
+
margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
|
378
|
+
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
379
|
+
decoration: BoxDecoration(
|
|
380
|
+
color: Colors.black54,
|
|
381
|
+
borderRadius: BorderRadius.circular(16),
|
|
382
|
+
),
|
|
383
|
+
child: RichText(
|
|
384
|
+
text: TextSpan(
|
|
385
|
+
children: [
|
|
386
|
+
TextSpan(
|
|
387
|
+
text: '${barrage.sender.userName}: ',
|
|
388
|
+
style: const TextStyle(
|
|
389
|
+
color: Colors.yellow,
|
|
390
|
+
fontSize: 14,
|
|
391
|
+
fontWeight: FontWeight.bold,
|
|
392
|
+
),
|
|
393
|
+
),
|
|
394
|
+
TextSpan(
|
|
395
|
+
text: barrage.textContent,
|
|
396
|
+
style: const TextStyle(color: Colors.white, fontSize: 14),
|
|
397
|
+
),
|
|
398
|
+
],
|
|
399
|
+
),
|
|
400
|
+
),
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
Widget _buildInputBar() {
|
|
405
|
+
return Container(
|
|
406
|
+
padding: const EdgeInsets.all(8),
|
|
407
|
+
color: Colors.white,
|
|
408
|
+
child: Row(
|
|
409
|
+
children: [
|
|
410
|
+
Expanded(
|
|
411
|
+
child: TextField(
|
|
412
|
+
controller: _inputController,
|
|
413
|
+
decoration: const InputDecoration(
|
|
414
|
+
hintText: 'Say something...',
|
|
415
|
+
border: OutlineInputBorder(),
|
|
416
|
+
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
|
417
|
+
),
|
|
418
|
+
onSubmitted: (_) => _sendMessage(),
|
|
419
|
+
),
|
|
420
|
+
),
|
|
421
|
+
const SizedBox(width: 8),
|
|
422
|
+
ElevatedButton(
|
|
423
|
+
onPressed: _sendMessage,
|
|
424
|
+
child: const Text('Send')
|
|
425
|
+
),
|
|
426
|
+
],
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
void _sendMessage() {
|
|
432
|
+
final text = _inputController.text.trim();
|
|
433
|
+
if (text.isNotEmpty) {
|
|
434
|
+
_barrageManager.sendTextMessage(text);
|
|
435
|
+
_inputController.clear();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## Advanced Features: Optimizing Performance in High Concurrency Scenarios
|
|
442
|
+
|
|
443
|
+
After using `BarrageStore` to build the danmaku function, this chapter will guide you on how to handle more complex business scenarios, ensuring it can still provide users with a smooth and stable experience in real, complex high-concurrency live broadcasting scenarios. This chapter will focus on three core business scenarios, offering clear optimization schemes and example code.
|
|
444
|
+
|
|
445
|
+
### Scenario One: High-Concurrency Bullet Screen Scenario
|
|
446
|
+
|
|
447
|
+
#### Scenario Description
|
|
448
|
+
|
|
449
|
+
During a popular event, a large number of audience flooded into the live streaming room, with bullet screens updating at a frequency of dozens per second.
|
|
450
|
+
|
|
451
|
+
#### **Technical Challenge**
|
|
452
|
+
|
|
453
|
+
The SDK returns the complete bullet screen list at an extremely high frequency. Each time the UI is rebuilt, the main thread will be blocked by intensive UI layout and rendering operations, causing interface lag.
|
|
454
|
+
|
|
455
|
+
#### **Optimization Scheme: **Batch Processing & Traffic Peak Shaving
|
|
456
|
+
|
|
457
|
+
Do not respond to every data update, but set a time threshold (such as 300 ms). Only when the distance from the last UI refresh exceeds this threshold, execute the next refresh operation. This reduces UI redraws from dozens per second to 3-4 per second, significantly improving smoothness.
|
|
458
|
+
``` java
|
|
459
|
+
class BarrageUIManager {
|
|
460
|
+
List<Barrage>? _latestMessageList;
|
|
461
|
+
Timer? _refreshTimer;
|
|
462
|
+
final void Function(List<Barrage>) onUpdate;
|
|
463
|
+
|
|
464
|
+
BarrageUIManager({required this.onUpdate}) {
|
|
465
|
+
// Check whether need to refresh every 0.3 seconds
|
|
466
|
+
_refreshTimer = Timer.periodic(
|
|
467
|
+
const Duration(milliseconds: 300),
|
|
468
|
+
(_) => _refreshUIIfNeeded(),
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/// This method is frequently invoked externally with the latest full list
|
|
473
|
+
void update(List<Barrage> fullList) {
|
|
474
|
+
_latestMessageList = fullList;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
void _refreshUIIfNeeded() {
|
|
478
|
+
// Check whether there is new data pending refresh
|
|
479
|
+
final newList = _latestMessageList;
|
|
480
|
+
if (newList == null) return;
|
|
481
|
+
|
|
482
|
+
_latestMessageList = null; // Clear flags to avoid repeated refresh
|
|
483
|
+
|
|
484
|
+
// Update data source and refresh UI
|
|
485
|
+
onUpdate(newList);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
void dispose() {
|
|
489
|
+
_refreshTimer?.cancel();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Scenario 2: Guarantee Memory Stability for Long-Term Live Stream
|
|
495
|
+
|
|
496
|
+
#### Scenario Description
|
|
497
|
+
|
|
498
|
+
Your application requires support for uninterrupted live streaming lasting several hours or even around the clock, such as game live broadcast or slow live streaming. During this period, the App must maintain stable operation and cannot exit unexpectedly due to long-running.
|
|
499
|
+
|
|
500
|
+
#### Technical Challenge
|
|
501
|
+
|
|
502
|
+
The full `messageList` returned by the SDK will grow infinitely during long-term live streams. Even if the UI layer implements traffic throttling, the huge array held by the data layer will continue to occupy memory, eventually causing the app to crash.
|
|
503
|
+
|
|
504
|
+
#### **Optimization Scheme**: Fixed Capacity Circular Buffer
|
|
505
|
+
|
|
506
|
+
**Only let your own data source (DataSource) hold a finite number of messages**. Regardless of how large the full list returned by the SDK is, your application only takes the latest part for display.
|
|
507
|
+
``` java
|
|
508
|
+
void _refreshUIIfNeeded() {
|
|
509
|
+
final fullList = _latestMessageList;
|
|
510
|
+
if (fullList == null) return;
|
|
511
|
+
|
|
512
|
+
_latestMessageList = null;
|
|
513
|
+
|
|
514
|
+
// Key point: Take the latest N messages
|
|
515
|
+
const capacity = 500; // The client keeps the latest 500 messages only
|
|
516
|
+
final cappedList = fullList.length > capacity
|
|
517
|
+
? fullList.sublist(fullList.length - capacity)
|
|
518
|
+
: fullList;
|
|
519
|
+
|
|
520
|
+
onUpdate(cappedList);
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## API documentation
|
|
525
|
+
|
|
526
|
+
For detailed information about ALL public interfaces, attributes, and methods of [BarrageStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/BarrageStore-class.html) and its related classes, see the official API documentation included with the [AtomicXCore](https://tencent-rtc.github.io/TUIKit_Flutter/index.html) framework.
|
|
527
|
+
|
|
528
|
+
## FAQs
|
|
529
|
+
|
|
530
|
+
### How to Achieve Various Bullet Screen Styles Such As Color Bullet Screen and Gift Bullet Screen
|
|
531
|
+
|
|
532
|
+
This is actually achieved by the custom message `sendCustomMessage`. `BarrageStore` does not limit your business imagination.
|
|
533
|
+
|
|
534
|
+
**Implementation Approach**
|
|
535
|
+
1. **Define data structure:** Work with your client and server team to define the JSON structure of custom messages. For example, a color bullet screen can be defined as follows:
|
|
536
|
+
|
|
537
|
+
``` json
|
|
538
|
+
{ "type": "colored_text", "text": "This is a color bullet screen!", "color": "#FF5733" }
|
|
539
|
+
```
|
|
540
|
+
2. **Sender:** During sending, convert this JSON structure to a string and send it via the `data` parameter of `sendCustomMessage`. `businessId` can be set to a unique identifier representing your business, such as "`barrage_style_v1`".
|
|
541
|
+
|
|
542
|
+
3. **Receiver**: After receiving the Danmu Message, check whether its `messageType` is `.custom` and whether `businessId` matches. If matched, parse the `data` string (usually parsing JSON), and render your custom UI style based on the parsed data (such as `color, text`).
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
### Different Classes and Files Call `BarrageStore.create("some_id")`. Will This Create Multiple Instances and Cause Chaos?
|
|
546
|
+
|
|
547
|
+
No worries at all. `AtomicXCore`'s internal mechanism will underwrite that as long as the `liveId` passed in is identical, the `BarrageStore` instance obtained will always be the same one bound to the live streaming room. You can use it wherever required without manually managing the singleton.
|
|
548
|
+
|
|
549
|
+
### Why Call SendTextMessage but Can'T See Messages Sent in the Message List?
|
|
550
|
+
|
|
551
|
+
**Take the following steps to troubleshoot:**
|
|
552
|
+
1. **Check returned results**: The `sendTextMessage` method has a complete callback. Please check if the callback returned a successful status or failure. If unsuccessful, the error information will specify the issue (such as "You are muted", "network error").
|
|
553
|
+
|
|
554
|
+
2. **Confirm the timing of listening**: Underwrite your `barrageStore.state` subscription occurs after the `liveId` live stream starts. If you start listening before joining the live streaming room, you may miss some messages.
|
|
555
|
+
|
|
556
|
+
3. **Check liveId**: Confirm the `liveId` used when creating the `BarrageStore` instance, joining the live streaming room, and sending messages is the same, including case sensitivity.
|
|
557
|
+
|
|
558
|
+
4. **Network issue**: Check whether the current network connection of the device is normal. Sending messages depends on the network.
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
### How to Show New Audience the History Danmu Messages When They Enter the Live Room
|
|
562
|
+
|
|
563
|
+
`AtomicXCore` supports pulling history Danmu Messages, but this requires you to perform one simple configuration in the **server console**. Once configured, the SDK will automatically handle everything subsequently with no need to write additional code.
|
|
564
|
+
|
|
565
|
+
#### **Procedure 1: Perform Configuration in the Live Console**
|
|
566
|
+
1. Log in to the [**console > Live > Configuration**](https://console.trtc.io/live/configuration), and select the target application at the **top**.
|
|
567
|
+
|
|
568
|
+
2. On the Live configuration page, select **new member pull historical messages**, modify **new member viewable latest message count**, supports a maximum of **50**.
|
|
569
|
+
|
|
570
|
+

|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
**Step 2: Client Seamless Retrieval**
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
After completing the above configuration, your client-side code **requires no modification**.
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
When a new user joins the live room, the `AtomicXCore` underlying layer will automatically pull your configuration history message count. These historical messages will be pushed to your UI layer through your realized `BarrageStore.barrageState` subscription channel, just like real-time messages. Your application will naturally receive and show these historical bullet screens, same as receiving real-time bullet screens.
|
|
580
|
+
|
|
581
|
+
|