.NET斗鱼直播弹幕客户端(下)

前言

在上篇文章中,我们提到了如何使用 .NET连接斗鱼TV直播弹幕的基本操作。然而想要做得好,做得容易扩展,就需要做进一步的代码整理。

本文将涉及以下内容:

  • 介绍如何使用 ReactiveExtensions( Rx),演示这一系列操作用起来,就像写 HelloWorld一样简单;

  • 用我自制的“准游戏引擎” FlysEngine,只需少量代码,即可实现桌面弹幕的效果;

  • 最后提供一波“伸手党”福利,文中所有可运行、完整代码,将按原样奉上。

Rx.NET

Rx,是 ReactiveExtensions的缩写,据说 Rx发明于 .NET2.0时代的微软。那时候还没有 async/await。后来,也许由于 RX对编程语言要求不高(如不要求内置 协程- coroutine), RX反倒在 .NET之外的其它编程语言中大行其道。如 rx.jsRxJava等等。

C#.NET2.0就提供了 yield关键字,然后 3.0提供了 LINQ5.0提供了 async/await,因此很多时候 RX的意义不大。但在某些情况下(如这种情况),就有意义了,原因请见下图:

-单数据多数据
同步TIEnumerable<T>
异步Task<T>Observable<T>IAsyncEnumerable<T>

C#协程支持同步多数据,异步单数据,但不支持同步多数据( C# 8.0现在已经支持 IAsyncEnumerable<T>),本文将使用 Rx来包装上一篇文章的斗鱼TV直播弹幕客户端。

来先看一波老代码:

640?wx_fmt=png

注意剪头所指的位置,那是基础代码“出口”,或者业务逻辑“入口”,基础代码不能简单地 return打断,因为它要不停地输出数据,这时就需要像 协程等编程语言功能,或者 Rx的支持。

Rx-Hello World

首先引入 NuGetSystem.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")

调用结果如下(和昨天效果完全一样):

640?wx_fmt=gif

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}))

效果如下:

640?wx_fmt=gif

只需一个 Merge指令即可合并两个直播间的弹幕( Observable<T>

扩展简单

比如只想提取特殊的弹幕,或者数据之前想做一些转换,可以使用 WhereSelect等数据过滤和转换操作符,符合 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的实质是 Direct2DWindowsAPI- UpdateLayeredWindowIndirect函数。如果不想使用 FlysEngine,完全可以使用其它方式代替。最简单的方式是使用 WPF,然后设置 AllowsTransparency=true,但这样性能会差一些。本文介绍的方法, CPU使用率将保持在 0%左右!

桌面弹幕的要点

  • 渲染文字 DirectWrite

  • 文字移动 将文字从屏幕右边移动到左边;

  • 检测是否离开屏幕 如果屏幕上不显示弹幕,即可将弹幕删除;

  • 初始位置确定 如果一行显示不下,则将弹幕放在下一行。

渲染文字

渲染文字一般是通过 DirectWrite,它性能很好,功能也强大。FlysEngineDirectWrite封装了,因此直接用便是。

注意: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表情:

640?wx_fmt=png

要多加一个枚举让其支持:

target.DrawText("????????????",	res.TextFormats[36], rectangle, res.GetColor(Color.Blue),	Direct2D.DrawTextOptions.EnableColorFont); // 重点

支持彩色 emoji表情后,效果如下:

640?wx_fmt=png


最终效果昨天已经见过了,如下:

640?wx_fmt=gif

本文(包括上文)所用的代码如下:

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骚操作】

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313394.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【 .NET Core 3.0 】框架之十 || AOP 切面思想

本文有配套视频&#xff1a;https://www.bilibili.com/video/av58096866/?p6前言上回《【 .NET Core3.0 】框架之九 || 依赖注入IoC学习 AOP界面编程初探》咱们说到了依赖注入Autofac的使用&#xff0c;不知道大家对IoC的使用是怎样的感觉&#xff0c;我个人表示还是比较可行…

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Docker

对于一个 .NET Core开发人员&#xff0c;你可能没有使用过Docker&#xff0c;但是你不可能没有听说过Docker。Docker是Github上最受欢迎的开源项目之一&#xff0c;它号称要成为所有云应用的基石&#xff0c;并把互联网升级到下一代。Docker是dotCloud公司开源的一款产品&#…

统计学习笔记(4) 线性回归(1)

Basic Introduction In this chapter, we review some of the key ideas underlying the linear regression model, as well as the least squares approach that is most commonly used to fit this model. Basic form: “≈” means “is approximately modeled as”, to …

敏捷这么久,你知道如何开敏捷发布火车吗?

译者&#xff1a;单冰从事项目管理十几年&#xff0c;先后管理传统型项目团队及敏捷创新型团队。负责京东AI事业部敏捷创新、团队工程效率改进及敏捷教练工作。曾经负责手机端京东App项目管理工作5年&#xff0c;带领千人团队实施敏捷转型工作&#xff0c;版本发布从2个月提升为…

Newton Method in Maching Learning

牛顿方法&#xff1a;转自http://blog.csdn.net/andrewseu/article/details/46771947 本讲大纲&#xff1a; 1.牛顿方法(Newton’s method) 2.指数族(Exponential family) 3.广义线性模型(Generalized linear models) 1.牛顿方法 假设有函数&#xff1a;&#xff0c;我们希…

一键分享博客或新闻到Teams好友或频道

在最近的开发者工具更新中&#xff0c;Teams提供了一个Share to Teams的能力&#xff0c;就是在你的网页上面&#xff0c;放置一个按钮&#xff0c;用户点击后&#xff0c;就可以很方便地将当前网页或者你指定的其他网页&#xff0c;分享到Teams好友或频道中。这个开发文档在这…

C#刷遍Leetcode面试题系列连载(3): No.728 - 自除数

点击蓝字“dotNET匠人”关注我哟加个“星标★”&#xff0c;每日 7:15&#xff0c;好文必达&#xff01;前言前文传送门&#xff1a;上篇文章中我们分析了一个递归描述的字符串问题&#xff0c;今天我们来分析一个数学问题&#xff0c;一道除法相关的面试题。今天要给大家分析的…

【.NET Core 3.0】框架之十二 || 跨域 与 Proxy

本文有配套视频&#xff1a;https://www.bilibili.com/video/av58096866/?p8一、为什么会出现跨域的问题跨域问题由来已久&#xff0c;主要是来源于浏览器的”同源策略”。何为同源&#xff1f;只有当协议、端口、和域名都相同的页面&#xff0c;则两个页面具有相同的源。只要…

.NET 时间轴:从出生到巨人

点击上方蓝字关注“汪宇杰博客”“ 自1995年互联网战略日以来最雄心勃勃的事业—— 微软.NET战略, 2000年6月30日”2002-02-13.NET Framework 1.0CLR 1.0Visual Studio .NET关键词&#xff1a;跨语言、托管代码2003-04-24.NET Framework 1.1CLR 1.1Visual Studio 2003关键词&am…

Boltzmann Machine 入门(2)

发现RBM 中的能量函数概念需要从Hopfield网络的角度理解&#xff0c;于是找到 http://blog.csdn.net/roger__wong/article/details/43374343 和关于BM的最经典论文 http://www.cs.toronto.edu/~hinton/papers.html#1983-1976 一、限制玻尔兹曼机的感性认识 要回答这个问题大…

针对深度学习的GPU芯片选择

转自&#xff1a;http://timdettmers.com/2014/08/14/which-gpu-for-deep-learning/ It is again and again amazing to see how much speedup you get when you use GPUs for deep learning: Compared to CPUs 10x speedups are typical, but on larger problems one can achi…

C# 8 - Range 和 Index(范围和索引)

C# 7 的 Span C# 7 里面出现了Span这个数据类型&#xff0c;它可以表示另一个数据结构里连续相邻的一串数据&#xff0c;并且它是内存安全的。 例子&#xff1a; 这个图的输出是3&#xff0c;4&#xff0c;5&#xff0c;6。 C# 8 的Range类型 而C# 8里面我们可以从一个序列里面…

DCT变换学习

http://blog.csdn.net/timebomb/article/details/5960624 timebomb的博客 DCT变换的基本思路是将图像分解为88的子块或1616的子块&#xff0c;并对每一个子块进行单独的DCT变换&#xff0c;然后对变换结果进行量化、编码。随着子块尺寸的增加&#xff0c;算法的复杂度急剧上升…

敏捷回顾会议的套路与实践分享

01—关于敏捷回顾会议实践过敏捷的人都知道&#xff0c;在敏捷中会有很多的会议要开&#xff0c;比如计划会议&#xff08;Planning&#xff09;、站立会议&#xff08;Daily Scrum&#xff09;、评审会议&#xff08;Review&#xff09;以及回顾会议&#xff08;Retrospective…

.Net Core AA.FrameWork应用框架介绍

开发多年&#xff0c;一直在从社区获取开源的便利&#xff0c;也深感社区力量的重要性&#xff0c;今天开源一个应用基础框架AA.FrameWork,也算是回馈社区&#xff0c;做出一点点贡献&#xff0c;希望能够帮助类似当年入行的我。AA.FrameWork 是基于.NET core流行的开源类库创建…

RBM/DBN训练中的explaining away概念

可以参照 Stanford大神DaphneKoller的概率图模型&#xff0c;里面贝叶斯网络一节讲到了explaining away。我看过之后试着谈谈自己的理解。 explainingaway指的是这样一种情况&#xff1a;对于一个多因一果的问题&#xff0c;假设各种“因”之间都是相互独立的&#xff0c;如果…

.NET Core使用gRPC打造服务间通信基础设施

一、什么是RPCrpc&#xff08;远程过程调用&#xff09;是一个古老而新颖的名词&#xff0c;他几乎与http协议同时或更早诞生&#xff0c;也是互联网数据传输过程中非常重要的传输机制。利用这种传输机制&#xff0c;不同进程&#xff08;或服务&#xff09;间像调用本地进程中…

DBN训练学习-A fast Learning algorithm for deep belief nets

转载自&#xff1a;http://blog.sciencenet.cn/blog-110554-889016.html DBN的学习一般都是从Hinton的论文A Fast Learning Algorithm for Deep Belief Nets开始。如果没有相关的背景知识&#xff0c;不知道问题的来由&#xff0c;这篇论文读起来是相当费劲的。 学习过程中主…

程序员家的大闸蟹:青壳、白底、肉多、爆黄,现在是吃大闸蟹的最佳时期

其实&#xff0c;我跟大家一样&#xff0c;也是dotNET跨平台和张队长的忠实粉丝&#xff0c;也是一名程序员。上上周&#xff0c;我在dotNET跨平台的优选商城买了队长推荐人生果&#xff0c;也是第一次吃这个人生果&#xff0c;味道鲜甜、汁水也特别多&#xff0c;但由于快递的…

环形链表II

1、题目描述 给定一个链表&#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索引从 0 开始&#xff09;。 如果 pos 是 -1&#xff0c;则…