.NET斗鱼直播弹幕客户端(下)
前言
在上篇文章中,我们提到了如何使用 .NET
连接斗鱼TV直播弹幕的基本操作。然而想要做得好,做得容易扩展,就需要做进一步的代码整理。
本文将涉及以下内容:
介绍如何使用
ReactiveExtensions
(Rx
),演示这一系列操作用起来,就像写HelloWorld
一样简单;用我自制的“准游戏引擎”
FlysEngine
,只需少量代码,即可实现桌面弹幕的效果;最后提供一波“伸手党”福利,文中所有可运行、完整代码,将按原样奉上。
Rx.NET
Rx
,是 ReactiveExtensions
的缩写,据说 Rx
发明于 .NET2.0
时代的微软。那时候还没有 async/await
。后来,也许由于 RX
对编程语言要求不高(如不要求内置 协程
- coroutine
), RX
反倒在 .NET
之外的其它编程语言中大行其道。如 rx.js
、 RxJava
等等。
C#
从 .NET2.0
就提供了 yield
关键字,然后 3.0
提供了 LINQ
, 5.0
提供了 async/await
,因此很多时候 RX
的意义不大。但在某些情况下(如这种情况),就有意义了,原因请见下图:
- | 单数据 | 多数据 |
---|---|---|
同步 |
T
|
IEnumerable<T>
|
异步 |
Task<T>
|
Observable<T> / IAsyncEnumerable<T>
|
C#
的 协程
支持同步多数据,异步单数据,但不支持同步多数据( C# 8.0
现在已经支持 IAsyncEnumerable<T>
),本文将使用 Rx
来包装上一篇文章的斗鱼TV直播弹幕客户端。
来先看一波老代码:
注意剪头所指的位置,那是基础代码“出口”,或者业务逻辑“入口”,基础代码不能简单地 return
打断,因为它要不停地输出数据,这时就需要像 协程
等编程语言功能,或者 Rx
的支持。
Rx
-Hello World
首先引入 NuGet
包 System.Reactive
,一个简单的“异步多值返回”的 Rx
示例代码如下:
Observable.Create<int>(async (o, cancellationToken) =>
{ for (var i = 0; i < 5; ++i) { await Task.Delay(1000); o.OnNext(i); } o.OnCompleted();
})
麻雀虽小,五脏俱全,如代码所示,几乎只需在正常代码外包一层 Rx
,即可享受 Rx
的好处。
使用 Rx
使用起来就更简单了,上篇展示的长达 252
行代码的 demo
,现在只需一行代码,即可无侵入式地调用:
DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/scboy")
调用结果如下(和昨天效果完全一样):
Rx
的其它好处
除了调用简单之外, Rx
的扩展也非常非常简单,比如完成以下操作,以前可能非常麻烦,需要改多处代码,而使用 Rx
,只需像 LINQ
一样加几个指令即可:
同时抓多个直播间的弹幕
#load ".\barrage.linq"
DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/scboy") .Select(x => new { Room = "scboy", Message = x.Message })
.Merge(DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/topic/lscs?rid=633019") .Select(x => new { Room = "lalala", Message = x.Message}))
效果如下:
只需一个 Merge
指令即可合并两个直播间的弹幕( Observable<T>
)
扩展简单
比如只想提取特殊的弹幕,或者数据之前想做一些转换,可以使用 Where
, Select
等数据过滤和转换操作符,符合 LINQ
的习惯,非常好用。比如我正常弹幕的提取,其实是从 JObjectFromUrl
转换而来, JObjectFromUrl
,又是从 RawFromUrl
转换而来,这提高了扩展性,又无需修改老代码,正是所谓“对扩展开放,对修改封闭”的开放-封闭原则:
public IObservable<JToken> JObjectFromUrl(string url) => RawFromUrl(url).Select(MsgTool.DecodeStringToJObject);
public IObservable<Barrage> ChatMessageFromUrl(string url) => JObjectFromUrl(url) .Where(x => x["type"].Value<string>() == "chatmsg") .Select(Barrage.FromJToken);
又比如可能我只想提取彩色弹幕,我只需 ChatMessageFromUrl().Where(x=>x.Color!=0xffffff)
即可,非常方便。
桌面弹幕
这可能是另一个主题——实时渲染,用到了我自己写的“准游戏引擎” FlysEngine
,因此需要安装 NuGet
包:FlysEngine.Desktop
。
桌面弹幕
不同于 网页弹幕
,只能在网页中显示,而 桌面弹幕
可以直接显示在屏幕最上方。有些公司年会可能用到了 桌面弹幕
,这无疑增加了主持人与观众们的互动,提高了群众参与的积极性。
注意:本文中所说
FlysEngine
的实质是Direct2D
和WindowsAPI
-UpdateLayeredWindowIndirect
函数。如果不想使用FlysEngine
,完全可以使用其它方式代替。最简单的方式是使用WPF
,然后设置AllowsTransparency=true
,但这样性能会差一些。本文介绍的方法,CPU
使用率将保持在0%
左右!
桌面弹幕的要点
渲染文字
DirectWrite
;文字移动 将文字从屏幕右边移动到左边;
检测是否离开屏幕 如果屏幕上不显示弹幕,即可将弹幕删除;
初始位置确定 如果一行显示不下,则将弹幕放在下一行。
渲染文字
渲染文字一般是通过 DirectWrite
,它性能很好,功能也强大。FlysEngine
将 DirectWrite
封装了,因此直接用便是。
注意:
DirectWrite
不仅渲染文字,还提供了.Metrics
属性,可以计算文字渲染之后的大小,这会让事情变得容易很多。
文字移动
文字移动首先需要一个位置,随着时间变化,将该位置的 X
坐标不段减少即可。这可以通过 FlysEngine
中的 UpdateLogic
事件实现,它会定期调用,传入一个 floatdt
,代码离上一次调用 UpdateLogic
的时间间隔。因此可以利用这个 dt
变量,计算是弹幕的新位置:
public void MoveLeft(float dt, float speed)
{ Position.X -= dt * speed;
}
检测是否离开屏幕
由于我们已知弹幕是矩形,(很显然屏幕也是矩形)因此这个检测比较简单,直接判断文字的 右边缘
是否 大于0
即可。
也由于需要经常/频繁地删除在屏幕上的弹幕对象,因此最好储存弹幕的数据结构别使用 O(n)
的集合,如最好别使用 List<T>
,它是线性表。我这里使用的是 链表
, .NET
的链表实现是 LinkedList<T>
(很多人以为是 List<T>
)。
多说一句,链接的遍历算法如下( while
循环):
var node = barrages.First;
while (node != null)
{ var next = node.Next; // do work here node = next;
}
之所以不使用 foreach
来遍历,因为这样遍历可以实现高性能的“边遍历、边删除”的实现。
初始位置确定
这一点思想需要多想想,需要从第一行开始,从后往前看,看最后那一边弹幕是否大于屏幕右边。只要想清楚了,代码很容易:
float GetNewY()
{ float y = 0; while (barrages.Reverse().Where(x => x.Position.Y == y).Select(x => x.Rect.Right).FirstOrDefault() > form.Width) { y += FontSize; } return y;
}
有了这些,就可以愉快地感受屏幕弹幕啦!
彩色 emoji
表情
Direct2D
支持——但默认不显示弹幕 emoji
表情:
要多加一个枚举让其支持:
target.DrawText("????????????", res.TextFormats[36], rectangle, res.GetColor(Color.Blue), Direct2D.DrawTextOptions.EnableColorFont); // 重点
支持彩色 emoji
表情后,效果如下:
结
最终效果昨天已经见过了,如下:
本文(包括上文)所用的代码如下:
id | 链接 |
---|---|
老式代码 | https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage_tranditional.linq |
新式代码 | https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage.linq |
合并弹幕 | https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage-combine.linq |
桌面弹幕 | https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/desktop-barrage.linq |
喜欢的朋友请“刷一波666???”,并关注我的微信公众号:【DotNet骚操作】
.NET斗鱼直播弹幕客户端(下)相关推荐
- .NET斗鱼直播弹幕客户端(2021)
.NET斗鱼直播弹幕客户端(2021) 离之前更新的两篇<.NET斗鱼直播弹幕客户端>已经有一段时间,近期有许多客户向我反馈刚好有这方面的需求,但之前的代码不能用了--但网上许多流传的No ...
- .NET斗鱼直播弹幕客户端(上)
前言 现在直播平台由于弹幕的存在,主播与观众可以更轻松地进行互动,非常受年轻群众的欢迎.斗鱼TV就是一款非常流行的直播平台,弹幕更是非常火爆.看到有不少主播接入 弹幕语音播报器. 弹幕点歌等模块,这都 ...
- 斗鱼直播弹幕python_python利用danmu实时获取斗鱼等直播网站字幕
danmu 是一个开源的直播平台弹幕接口 用python pip install danmu # -*- coding: utf-8 -*- import time, sys from danm ...
- OBS斗鱼直播弹幕插件效果
我没有安装任何OBS插件,一样达到了美化版弹幕效果
- 实时爬取斗鱼直播时的弹幕消息
最近想爬取实时消息,上网查了下,所以爬取斗鱼直播的弹幕消息做了下练习,这个开源的代码有很多,但是具体是怎么爬取到的还是要仔细地研究下.想爬取斗鱼的弹幕消息,按照常用的做法是打开网页用抓包工具抓包,但是 ...
- python 爬取直播弹幕视频_调用斗鱼API爬取直播间弹幕信息(用户昵称及弹幕内容)...
调用斗鱼API爬取直播间弹幕信息(用户昵称及弹幕内容) 查看<斗鱼弹幕服务器第三方接入协议v1.4.1>,了解斗鱼API的使用方法,即如何连接斗鱼弹幕服务器.维持连接及获取弹幕信息 Pyt ...
- 斗鱼直播flash怎么切换html5,GitHub - spacemeowx2/DouyuHTML5Player: 替换斗鱼的Flash弹幕播放器...
斗鱼HTML5播放器 基于 flv.js 的斗鱼HTML5播放器. 使用了 flv.js 内核提供的直播流播放, 用 JavaScript 实现了斗鱼的弹幕协议, 并支持发送弹幕和送礼物. 使用 不要 ...
- ubuntu下使用OBS开斗鱼直播
系统环境:ubuntu 15.10,OBS Studio 0.13.1 OBS是可以在linux,windows,mac下直播的开源软件,官方地址:https://obsproject.com/ 斗鱼 ...
- python爬斗鱼直播_Python爬虫:利用API实时爬取斗鱼弹幕
原标题:Python爬虫:利用API实时爬取斗鱼弹幕 这些天一直想做一个斗鱼爬取弹幕,但是一直考试时间不够,而且这个斗鱼的api接口虽然开放了但是我在github上没有找到可以完美实现连接.我看了好多 ...
最新文章
- PVD与CVD性能比较
- hdu 1561 The more, The Better_树状dp
- c++:文件操作1 文件的打开
- 在Ubuntu 14.04 64bit上安装google-earth免费版
- 【Groovy】编译时元编程 ( 利用注解进行 AST 语法树转换 | 定义注解并使用 GroovyASTTransformationClass 注明 AST 转换接口 | AST 转换接口实现 )
- 数据中心“泡澡”散热,阿里云启用全球最大液冷数据中心支撑双11
- SAP Spartacus powertools-spa site在Commerce Cloud后台的属性
- CentOS7 下安装 Redis
- 设计灵感|资讯博客类App界面设计
- 基于visual Studio2013解决C语言竞赛题之0204实数求值
- java的基本循环结构_Java基础之(九):循环结构
- 亚嵌协办 博文视点Open Party第5期 参会感言
- 读完了csapp(中文名:深入理解计算机系统)
- 关于悟空CRM部署经历--钟艳明
- 你知道ISO27000信息安全管理标准族有多少?
- python中三角函数运算符_Python入门之三角函数sin()函数实例详解
- 【HTML 教程系列第 13 篇】HTML 中的超链接标签 a,看这一篇就够了
- 2019开源BI软件排行榜
- 怎么查看计算机簇大小,分区格式与簇的大小讲解
- C# UDP Socket ReceiveFrom 远程主机强迫关闭了一个现有的连接。
热门文章
- ArcEngine 没有Esri.ArcGis.GeoAnalyst 命名空间
- VIM之Project 项目管理工具
- Mac OS X必备APP推荐之一
- 面对峰值响应冲击,解决高并发的三大策略
- Android 4 +https(如何启动TLS1 1 and TLS1 2)
- Lintcode165 Merge Two Sorted Lists solution 题解
- Mybatis常用总结:参数,返回,执行sql,include等
- C# 数据结构--排序[下]
- 快速配置Ehcache
- (装载)C#中AppDomain.CurrentDomain.BaseDirectory与Application.StartupPath的区别