Skip to content

Commit 288413e

Browse files
committed
优化弹幕渲染与屏蔽逻辑,减少资源浪费
- 在 `DataCard.cs` 中为属性 `set` 方法增加值变更检查。 - 替换 `IsThereHLVPresentAsync` 为 `GetHlsStreamUrlAsync`,优化流地址获取。 - 添加页面可见性标志 `IsPageVisible`,避免无意义刷新。 - 调整弹幕字体样式,增强视觉效果。 - 优化屏蔽词逻辑,使用缓存减少重复分割操作。 - 增加弹幕缓冲机制,批量刷新降低 UI 线程压力。 - 新增 `BarrageBlockWords` 类,提升屏蔽词匹配效率。
1 parent b066819 commit 288413e

7 files changed

Lines changed: 203 additions & 42 deletions

File tree

Desktop/Models/DataCard.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,28 @@ protected void OnPropertyChanged(string propertyName)
2020
public long Uid
2121
{
2222
get => _uid;
23-
set { _uid = value; OnPropertyChanged(nameof(Uid)); }
23+
set { if (_uid != value) { _uid = value; OnPropertyChanged(nameof(Uid)); } }
2424
}
2525

2626
private long _roomId;
2727
public long Room_Id
2828
{
2929
get => _roomId;
30-
set { _roomId = value; OnPropertyChanged(nameof(Room_Id)); }
30+
set { if (_roomId != value) { _roomId = value; OnPropertyChanged(nameof(Room_Id)); } }
3131
}
3232

3333
private string _title = string.Empty;
3434
public string Title
3535
{
3636
get => _title;
37-
set { _title = value; OnPropertyChanged(nameof(Title)); }
37+
set { if (_title != value) { _title = value; OnPropertyChanged(nameof(Title)); } }
3838
}
3939

4040
private string _nickname = string.Empty;
4141
public string Nickname
4242
{
4343
get => _nickname;
44-
set { _nickname = value; OnPropertyChanged(nameof(Nickname)); }
44+
set { if (_nickname != value) { _nickname = value; OnPropertyChanged(nameof(Nickname)); } }
4545
}
4646

4747
private bool _isRec;
@@ -156,7 +156,7 @@ public double DownloadSpe
156156
public string DownloadSpe_str
157157
{
158158
get => _downloadSpeStr;
159-
set { _downloadSpeStr = value; OnPropertyChanged(nameof(DownloadSpe_str)); }
159+
set { if (_downloadSpeStr != value) { _downloadSpeStr = value; OnPropertyChanged(nameof(DownloadSpe_str)); } }
160160
}
161161

162162
private bool _isDownload;
@@ -184,14 +184,14 @@ public bool IsDownload
184184
public long LiveTime
185185
{
186186
get => _liveTime;
187-
set { _liveTime = value; OnPropertyChanged(nameof(LiveTime)); }
187+
set { if (_liveTime != value) { _liveTime = value; OnPropertyChanged(nameof(LiveTime)); } }
188188
}
189189

190190
private string _liveTimeStr = string.Empty;
191191
public string LiveTime_str
192192
{
193193
get => _liveTimeStr;
194-
set { _liveTimeStr = value; OnPropertyChanged(nameof(LiveTime_str)); }
194+
set { if (_liveTimeStr != value) { _liveTimeStr = value; OnPropertyChanged(nameof(LiveTime_str)); } }
195195
}
196196

197197
/// <summary>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
namespace Desktop.Services
2+
{
3+
/// <summary>
4+
/// 弹幕屏蔽词缓存:屏蔽词配置字符串按"|"切分的结果缓存复用,配置内容变化时才重新切分。
5+
/// 弹幕接收是高频路径(每条消息至少匹配一次),原来每条消息都Split一次会产生大量临时数组和字符串分配。
6+
/// </summary>
7+
internal static class BarrageBlockWords
8+
{
9+
private static string _cachedConfig = null;
10+
private static string[] _cachedWords = System.Array.Empty<string>();
11+
12+
/// <summary>
13+
/// 当前生效的屏蔽词数组(已去除空项)。读多写少,重建时直接整体替换引用,旧数组被正在使用的线程读完后由GC回收
14+
/// </summary>
15+
public static string[] Words
16+
{
17+
get
18+
{
19+
string config = Core.Config.Core_RunConfig._BlockBarrageList ?? string.Empty;
20+
if (config != _cachedConfig)
21+
{
22+
_cachedConfig = config;
23+
_cachedWords = config.Split('|', System.StringSplitOptions.RemoveEmptyEntries);
24+
}
25+
return _cachedWords;
26+
}
27+
}
28+
29+
/// <summary>
30+
/// 判断文本是否命中任一屏蔽词(普通循环,避免LINQ闭包分配)
31+
/// </summary>
32+
public static bool IsBlocked(string text)
33+
{
34+
if (string.IsNullOrEmpty(text))
35+
{
36+
return false;
37+
}
38+
foreach (string word in Words)
39+
{
40+
if (text.Contains(word))
41+
{
42+
return true;
43+
}
44+
}
45+
return false;
46+
}
47+
}
48+
}

Desktop/Views/Control/CardControl.xaml.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ private async void MenuItem_PlayWindow_Click(object sender, RoutedEventArgs e)
5757
Models.DataCard dataCard = GetDataCard(sender);
5858
try
5959
{
60-
bool hasHls = await IsThereHLVPresentAsync(dataCard.Uid);
61-
if (hasHls)
60+
string hlsUrl = await GetHlsStreamUrlAsync(dataCard.Uid);
61+
if (!string.IsNullOrEmpty(hlsUrl))
6262
{
63-
Windows.VlcPlayWindow vlcPlayWindow = new Windows.VlcPlayWindow(dataCard.Uid);
63+
//取流结果直接传给播放窗复用,避免开窗后再发起一次重复的取流请求
64+
Windows.VlcPlayWindow vlcPlayWindow = new Windows.VlcPlayWindow(dataCard.Uid, hlsUrl);
6465
vlcPlayWindow.Show();
6566
}
6667
else
@@ -78,9 +79,9 @@ private async void MenuItem_PlayWindow_Click(object sender, RoutedEventArgs e)
7879
}
7980

8081
/// <summary>
81-
/// 异步检测是否有HLS流
82+
/// 异步获取HLS流地址,获取不到(无HLS流或请求失败)时返回null
8283
/// </summary>
83-
public async Task<bool> IsThereHLVPresentAsync(long uid)
84+
public async Task<string> GetHlsStreamUrlAsync(long uid)
8485
{
8586
return await Task.Run(() =>
8687
{
@@ -89,9 +90,9 @@ public async Task<bool> IsThereHLVPresentAsync(long uid)
8990
string url = "";
9091
if (roomCard != null && Core.RuntimeObject.Download.HLS.GetHlsAvcUrl(roomCard, Core.Config.Core_RunConfig._DefaultPlayResolution, out url) && !string.IsNullOrEmpty(url))
9192
{
92-
return true;
93+
return url;
9394
}
94-
return false;
95+
return null;
9596
});
9697
}
9798

@@ -104,10 +105,11 @@ private async void Border_DoubleClickToOpenPlaybackWindow(object sender, MouseBu
104105
Models.DataCard dataCard = (Models.DataCard)grid.DataContext;
105106
try
106107
{
107-
bool hasHls = await IsThereHLVPresentAsync(dataCard.Uid);
108-
if (hasHls)
108+
string hlsUrl = await GetHlsStreamUrlAsync(dataCard.Uid);
109+
if (!string.IsNullOrEmpty(hlsUrl))
109110
{
110-
Windows.VlcPlayWindow vlcPlayWindow = new Windows.VlcPlayWindow(dataCard.Uid);
111+
//取流结果直接传给播放窗复用,避免开窗后再发起一次重复的取流请求
112+
Windows.VlcPlayWindow vlcPlayWindow = new Windows.VlcPlayWindow(dataCard.Uid, hlsUrl);
111113
vlcPlayWindow.Show();
112114
}
113115
else

Desktop/Views/Pages/DataPage.xaml.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public partial class DataPage
3333
public static int PageIndex = 1;
3434
public static string screen_name = string.Empty;
3535
public static int Width = 0;
36+
/// <summary>
37+
/// 房间列表页当前是否可见(导航进入/离开时由Loaded/Unloaded维护)。
38+
/// 页面不可见时3秒定时刷新直接跳过——看不见的数据刷了也是白耗CPU和网络请求
39+
/// </summary>
40+
public static bool IsPageVisible { get; private set; } = false;
3641

3742
public DataPage()
3843
{
@@ -46,6 +51,21 @@ public DataPage()
4651
System.Windows.MessageBox.Show($"UI初始化出现重大错误,错误堆栈{ex.ToString()}");
4752
}
4853
Width = (int)CardsItemsControl.ActualWidth;
54+
//页面被NavigationCacheMode缓存后实例常驻,导航进入/离开只触发Loaded/Unloaded,据此维护可见性标志
55+
Loaded += DataPage_Loaded;
56+
Unloaded += DataPage_Unloaded;
57+
}
58+
59+
private void DataPage_Loaded(object sender, RoutedEventArgs e)
60+
{
61+
IsPageVisible = true;
62+
//回到本页时立即刷新一次,避免展示离开期间的过期数据
63+
RequestImmediateRefresh();
64+
}
65+
66+
private void DataPage_Unloaded(object sender, RoutedEventArgs e)
67+
{
68+
IsPageVisible = false;
4969
}
5070

5171
public async void Init()
@@ -207,10 +227,15 @@ public static void Refresher(object state)
207227
}
208228

209229
/// <summary>
210-
/// 请求立即刷新一次房间卡片(带重入保护,进行中的刷新会跳过本次请求)
230+
/// 请求立即刷新一次房间卡片(带重入保护,进行中的刷新会跳过本次请求;页面不可见时直接忽略
211231
/// </summary>
212232
public static void RequestImmediateRefresh()
213233
{
234+
//页面不可见时刷新纯属浪费:数据刷出来也没人看,等导航回本页时Loaded会补一次刷新
235+
if (!IsPageVisible)
236+
{
237+
return;
238+
}
214239
if (Interlocked.Exchange(ref _refreshing, 1) == 1)
215240
{
216241
return;

Desktop/Views/Windows/DanMuCanvas/OutlinedText.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class OutlinedText : FrameworkElement
2828
public FontFamily TextFontFamily { get; set; }
2929
public Brush Fill { get; set; } = Brushes.White;
3030
public Brush Stroke { get; set; } = Brushes.Black;
31-
public double StrokeThickness { get; set; } = 2.0;
31+
public double StrokeThickness { get; set; } = 1.0;
3232

3333
private void EnsureGeometry()
3434
{
@@ -39,7 +39,8 @@ private void EnsureGeometry()
3939
Typeface typeface = new Typeface(
4040
TextFontFamily ?? SystemFonts.MessageFontFamily,
4141
FontStyles.Normal,
42-
FontWeights.Bold,
42+
//用Black(900)字重让弹幕更粗;若自定义字体没有Black字重会回落到Bold,观感不变差
43+
FontWeights.Black,
4344
FontStretches.Normal);
4445
_formattedText = new FormattedText(
4546
Text,

Desktop/Views/Windows/DanmaOnlyWindow.xaml.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ private void LiveChatListener_MessageReceived(object? sender, Core.LiveChat.Mess
132132
{
133133
case DanmuMessageEventArgs Danmu:
134134
{
135-
string[] BlockWords = Core.Config.Core_RunConfig._BlockBarrageList.Split('|');
136-
if (BlockWords.Any(word => !string.IsNullOrEmpty(word) && Danmu.Message.Contains(word)))
135+
//屏蔽词走缓存(配置不变时不重新Split),避免每条弹幕都分配临时数组
136+
if (Services.BarrageBlockWords.IsBlocked(Danmu.Message))
137137
{
138138
return;
139139
}
@@ -157,8 +157,8 @@ private void LiveChatListener_MessageReceived(object? sender, Core.LiveChat.Mess
157157
}
158158
case SendGiftEventArgs sendGiftEventArgs:
159159
{
160-
string[] BlockWords = Core.Config.Core_RunConfig._BlockBarrageList.Split('|');
161-
if (BlockWords.Any(word => !string.IsNullOrEmpty(word) && sendGiftEventArgs.GiftName.Contains(word)))
160+
//屏蔽词走缓存(配置不变时不重新Split),避免每条礼物消息都分配临时数组
161+
if (Services.BarrageBlockWords.IsBlocked(sendGiftEventArgs.GiftName))
162162
{
163163
return;
164164
}
@@ -178,6 +178,12 @@ private void LiveChatListener_MessageReceived(object? sender, Core.LiveChat.Mess
178178
default:
179179
break;
180180
}
181+
//未识别/不需要展示的消息类型(如进场消息INTERACT_WORD、Reconnect指令等)不会产生有效文本,
182+
//直接丢弃不入队——大直播间这类消息非常高频,入队会把弹幕窗口刷满空白行并让flush空转
183+
if (string.IsNullOrEmpty(msg.Message))
184+
{
185+
return;
186+
}
181187
//弹幕先进缓冲,200ms聚合一次批量刷新到UI,避免每条弹幕都同步Invoke阻塞弹幕接收线程
182188
lock (_pendingDanmaLock)
183189
{

0 commit comments

Comments
 (0)