90天内开发一个视频平台

今年春天,我们发现自己的处境非常愉快。 由于大流行,我们的夏季会议显然需要转移到网上。 为了有效地在线进行这些工作,现成的软件解决方案不适合我们;我们需要编写自己的解决方案。 我们有三个月的时间来做这件事。

显然这是令人兴奋的三个月。 但从外部来看,这并不完全显而易见:什么是在线会议平台? 它由哪些部分组成? 因此,在最后一次夏季 DevOops 会议上,我问负责这项任务的人:

  • Nikolay Molchanov - JUG Ru 集团技术总监;
  • Vladimir Krasilshchik 是一位务实的 Java 程序员,从事后端工作(您也可以在我们的 Java 会议上看到他的报告);
  • Artyom Nikonov 负责我们所有的视频流。

顺便说一句,在秋冬会议上,我们将使用同一平台的改进版本 - 所以许多 habra 读者仍然是它的用户。

90天内开发一个视频平台

整体情况

——团队的构成是怎样的?

尼古拉·莫尔查诺夫: 我们有一名分析师、一名设计师、一名测试员、三个前端和一个后端。 当然还有T型专家!

——整个过程是怎样的?

尼古拉: 直到三月中旬,我们还没有准备好任何在线内容。 15月XNUMX日,整个在线轮播开始旋转。 我们建立了多个存储库,规划并讨论了基本架构,并在三个月内完成了所有工作。

当然,这经历了规划、架构、功能选择、对这些功能进行投票、这些功能的政策、设计、开发、测试等经典阶段。 结果,我们在 6 月 XNUMX 日将所有内容投入生产。 TechTrain。 所有事情都有 90 天的时间。

— 我们是否成功完成了我们的承诺?

尼古拉: 由于我们现在在线参加 DevOops 会议,这意味着它有效。 我个人致力于主要的事情:我将为客户提供一个可以召开在线会议的工具。

挑战在于:给我们一个工具,让我们可以向持票人广播我们的会议。

所有规划分为几个阶段,所有功能(全局约30个)分为4类:

  • 我们肯定会这样做(没有他们我们就活不下去),
  • 我们接下来要做的就是
  • 我们永远不会这样做,
  • 我们永远不会这样做。

我们制作了前两类的所有功能。

— 我知道总共创建了 600 个 JIRA 问题。 三个月内,你制作了 13 个微服务,我怀疑它们不仅是用 Java 编写的。 您使用了不同的技术,在三个可用区中拥有两个 Kubernetes 集群,在 Amazon 中拥有 5 个 RTMP 流。

现在让我们分别看看系统的每个组件。

串流

— 让我们从我们已经拥有视频图像并将其传输到某些服务开始。 Artyom,请告诉我们这个流媒体是如何发生的?

阿尔乔姆·尼科诺夫: 我们的总体方案如下:来自摄像机的图像 -> 我们的控制室 -> 本地 RTMP 服务器 -> 亚马逊 -> 视频播放器。 更多细节 写了它 六月的哈布雷。

一般来说,有两种全局方法可以实现此目的:基于硬件或基于软件解决方案。 我们选择软件路线是因为它对于远程扬声器来说更容易。 将硬件带到另一个国家/地区的扬声器并不总是可能的,但向扬声器提供软件似乎更容易、更可靠。

从硬件的角度来看,我们有一定数量的摄像机(在我们的演播室和远程扬声器处),演播室中有一定数量的遥控器,有时必须在广播期间在桌子底下进行维修。

来自这些设备的信号通过采集卡、输入/输出卡和声卡进入计算机。 在那里,信号被混合并组装成布局:

90天内开发一个视频平台
4 个扬声器的布局示例

90天内开发一个视频平台
4 个扬声器的布局示例

进一步地,连续广播是通过三台计算机实现的:主机一台,工作机两台。 第一台计算机收集第一个报告,第二台计算机收集休息时间,第一台计算机收集下一个报告,第二台计算机收集下一个休息时间,依此类推。 主机将第一个与第二个混合。

这创建了一种三角形,如果这些节点中的任何一个发生故障,我们可以快速且不损失质量地继续向客户提供内容。 我们就遇到过这样的情况。 在会议的第一周,我们修复了一台机器,打开/关闭它。 人们似乎对我们的韧性感到满意。

接下来,来自计算机的流传输到本地服务器,该服务器有两个任务:路由 RTMP 流和记录备份。 所以我们有多个记录点。 然后,视频流被发送到我们基于 Amazon SaaS 服务构建的系统部分。 我们用 媒体直播、S3、CloudFront。

尼古拉: 在视频到达观众之前会发生什么? 你必须以某种方式削减它,对吧?

阿尔乔姆: 我们自行压缩视频并将其发送到 MediaLive。 我们在那里推出转码器。 他们将视频实时转码成多种分辨率,以便人们可以在手机上、通过国内较差的互联网等观看它们。 然后这些流被切割成 ,这就是协议的工作原理 HLS。 我们向前端发送一个播放列表,其中包含指向这些块的指针。

— 我们使用 1080p 分辨率吗?

阿尔乔姆: 我们视频的宽度与 1080p - 1920 像素相同,高度稍小,图片更拉长 - 这是有原因的。

播放机

— Artyom 描述了视频如何进入流、如何针对不同的屏幕分辨率分配到不同的播放列表、切成块并进入播放器。 Kolya,现在告诉我这是什么类型的播放器,它如何消耗流,为什么是 HLS?

尼古拉: 我们有一个所有会议观众都可以观看的播放器。

90天内开发一个视频平台

本质上,这是库的包装 hls.js,上面写着许多其他球员。 但我们需要非常具体的功能:倒带并标记该人所在的位置以及他当前正在观看的报告。 我们还需要自己的布局、各种徽标以及我们内置的所有其他内容。 因此,我们决定编写自己的库(HLS 的包装器)并将其嵌入到网站中。

这是根功能,因此它几乎是最先实现的。 然后一切都围绕着它生长。

事实上,通过授权,播放器从后端接收一个播放列表,其中包含与时间和质量相关的块的链接,下载必要的块并将其显示给用户,并在此过程中执行一些“魔术”。

90天内开发一个视频平台
时间轴示例

— 播放器中内置了一个按钮,用于显示所有报告的时间线...

尼古拉: 是的,我们立即解决了用户导航的问题。 四月中旬,我们决定不再在单独的网站上广播每次会议,而是将所有内容合并在一个网站上。 这样,全通票用户就可以在不同的会议之间自由切换:既可以直播,也可以录制过去的会议。

为了让用户更容易导航当前流并在曲目之间切换,我们决定制作一个“整个广播”按钮和水平报告卡,用于在曲目和报告之间切换。 有键盘控制。

— 这有什么技术困难吗?

尼古拉: 他们有一个滚动条,上面标记了不同报告的起点。

— 最后,你们是在 YouTube 做类似的事情之前在滚动条上实现这些标记的吗?

阿尔乔姆: 当时他们还处于测试阶段。 这似乎是一个相当复杂的功能,因为他们在过去的一年里已经对用户进行了部分测试。 现在它已经上市了。

尼古拉: 但实际上我们的销售速度更快。 老实说,在这个简单的功能背后,播放器内部有大量的后端、前端、计算和数学。

前端

— 让我们弄清楚我们展示的这些内容(演讲卡、演讲者、网站、时间表)如何到达前端?

弗拉基米尔·克拉西尔希克: 我们有几个内部 IT 系统。 有一个系统可以输入所有报告和所有发言者。 演讲者参加会议有一个过程。 演讲者提交申请,系统捕获该申请,然后根据特定的管道创建报告。

90天内开发一个视频平台
这就是演讲者对管道的看法

这个系统是我们内部开发的。

接下来,您需要根据各个报告制定时间表。 如您所知,这是一个 NP 难题,但我们以某种方式解决了它。 为此,我们启动了另一个组件来生成时间表并将其上传到第三方云服务 Contentful。 在那里,一切看起来都像一张桌子,其中有会议的日子,日子里有时段,时段中有报告、休息或赞助活动。 所以我们看到的内容位于第三方服务中。 而任务就是将其传送到现场。

看起来这个网站只是一个带有播放器的页面,这里没有什么复杂的。 但事实并非如此。 该页面后面的后端转到 Contentful,从那里获取时间表,生成一些对象并将其发送到前端。 使用我们平台的每个客户端都会建立的 websocket 连接,我们向他发送从后端到前端的时间表更新。

真实案例:演讲者在会议期间就换了工作。 我们需要更换他的雇主公司徽章。 从后端来看,这是如何发生的? 更新通过 websocket 发送到所有客户端,然后前端本身重新绘制时间线。 这一切都发生得天衣无缝。 云服务和我们的几个组件的结合使我们有机会生成所有这些内容并将其提供给前台。

尼古拉: 在此需要澄清的是,我们的网站不是经典的 SPA 应用程序。 这既是一个基于布局的渲染网站,也是一个 SPA。 Google 实际上将此网站视为呈现的 HTML。 这有利于搜索引擎优化和向用户交付内容。 它不会等待 1,5 MB 的 JavaScript 加载才看到页面,它会立即看到已经渲染的页面,并且每次切换报表时您都会感觉到。 一切都在半秒内发生,因为内容已经准备好并发布在正确的位置。

— 让我们通过列出技术来对上述所有内容划清界限。 Tyoma 说我们有 5 个亚马逊流媒体,我们在那里提供视频和声音。 我们那里有 bash 脚本,我们用它们来启动和配置......

阿尔乔姆: 这是通过 AWS API 实现的,那里还有更多技术方面的服务。 我们划分了责任,以便我能够交付 CloudFront的,前端和后端开发人员从那里获取它。 我们有许多自己的绑定来简化内容的布局,然后我们将其制作为 4K 等。 由于期限非常紧迫,我们几乎完全在 AWS 上完成。

— 然后所有这些都进入使用后端系统的播放器中。 我们的播放器中有 TypeScript、React、Next.JS。 在后端,我们有多种 C#、Java、Spring Boot 和 Node.js 服务。 这一切都是使用 Yandex.Cloud 基础设施使用 Kubernetes 进行部署的。

我还想指出的是,当我需要熟悉该平台时,结果很简单:所有存储库都在 GitLab 上,所有内容都命名良好,编写了测试,有文档。 也就是说,即使在紧急模式下,他们也会处理这些事情。

业务限制和分析

— 我们根据业务需求定位了 10 个用户。 是时候谈谈我们的业务限制了。 我们必须确保高工作量,确保遵守有关个人数据保存的法律。 还有什么?

尼古拉: 最初,我们从视频需求开始。 最重要的是世界各地的分布式视频存储,以便快速交付给客户。 其他功能包括 1080p 分辨率以及倒带功能,但许多其他功能并未在实时模式下实现。 后来我们增加了启用2倍速度的功能,在它的帮助下您可以“赶上”直播并继续实时观看会议。 在此过程中,时间线标记功能出现了。 另外,我们必须能够容错并承受 10 个连接的负载。 从后端的角度来看,这大约是每次页面刷新的 000 个连接乘以 10 个请求。 这已经是 000 RPS/秒了。 相当有一点。

— 对于合作伙伴在线展台的“虚拟展览”还有其他要求吗?

尼古拉: 是的,这必须非常迅速且普遍地完成。 每次会议我们最多有 10 家合作伙伴公司,所有会议都必须在一两周内完成。 然而,它们的内容在格式上略有不同。 但制作了某种模板引擎来动态组装这些页面,几乎没有进一步的开发参与。

— 还有实时视图和统计分析的要求。 我知道我们为此使用 Prometheus,但请更详细地告诉我们:我们满足哪些分析要求,以及如何实现?

尼古拉: 最初,我们有收集 A/B 测试和信息的营销要求,以便了解未来如何正确地向客户提供最佳内容。 还需要对合作伙伴活动进行一些分析以及您看到的分析(访问柜台)。 所有信息都是实时收集的。

我们甚至可以向演讲者提供汇总形式的信息:在某个时间点有多少人在观看您。 同时,为了遵守联邦法律 152,我们不会以任何方式跟踪您的个人帐户和个人数据。

该平台已经拥有营销工具和我们用于实时测量用户活动(谁观看了报告的哪一秒)的指标,以便构建报告的出席率图表。 基于这些数据,正在进行的研究将使下一次的会议变得更好。

欺诈罪

— 我们有反欺诈机制吗?

尼古拉: 由于从业务角度来看时间紧迫,该任务最初并未设置为立即阻止不必要的连接。 如果两个用户使用同一帐户登录,则可以查看内容。 但我们知道一个帐户有多少同时观看次数。 我们还禁止了一些特别恶意的违规者。

弗拉季米尔: 值得赞扬的是,其中一名被禁止的用户明白为什么会发生这种情况。 他来了,道歉并答应买票。

——要实现这一切,你必须完整追踪所有用户从进入到退出的过程,始终知道他们在做什么。 这个系统如何运作?

弗拉季米尔: 我想谈谈分析和统计,然后我们分析报告是否成功,或者可以提供给合作伙伴。 所有客户端都通过 websocket 连接到特定的后端集群。 它站在那里 淡褐色。 每个客户在每个时间段发送他正在做什么以及他正在观看什么曲目。 然后,使用快速 Hazelcast 作业聚合这些信息,并将其发送回观看这些曲目的每个人。 我们在角落里看到现在有多少人和我们在一起。

90天内开发一个视频平台

相同的信息存储在 蒙戈 并进入我们的数据湖,我们有机会构建一个更有趣的图表。 问题来了:有多少独立用户查看了这份报告? 我们去 Postgres的,有所有通过此报告的 id 来的人的 ping。 我们收集、汇总了独特的信息,现在我们可以理解了。

尼古拉: 但同时,我们也接收来自Prometheus的实时数据。 它是针对所有 Kubernetes 服务、针对 Kubernetes 本身而设置的。 它收集了所有信息,通过 Grafana,我们可以实时构建任何图表。

弗拉季米尔: 一方面,我们下载它以进行进一步的 OLAP 处理。 对于 OLTP,应用程序将整个内容下载到 Prometheus、Grafana,图表甚至会收敛!

- 这是图收敛时的情况。

动态变化

— 告诉我们动态变化是如何推出的:如果报告在开始前 6 分钟被取消,那么行动链是什么? 哪个管道有效?

弗拉季米尔: 管道是非常有条件的。 有几种可能性。 第一个是时间表生成程序起作用并改变了时间表。 修改后的时间表上传到 Contentful。 之后后端了解到 Contentful 中此会议有更改,将其获取并重建。 一切都通过 websocket 收集和发送。

第二种可能性,当一切都以极快的速度发生时:编辑手动更改 Contentful 中的信息(链接到 Telegram、演讲者的演示文稿等),并且逻辑与第一次相同。

尼古拉: 一切都在不刷新页面的情况下发生。 对于客户来说,所有的改变都是无缝地发生的。 切换报告也是如此。 当时间到来时,报告和界面会发生变化。

弗拉季米尔: 此外,时间线中还有开始报告的时间截止点。 一开始什么也没有。 如果您将鼠标悬停在红色条纹上,那么在某个时刻,由于广播导演的帮助,将会出现截止点。 导演设置正确的广播开始时间,后端接收此更改,根据会议时间表计算整个曲目演示的开始和结束时间,将其发送给我们的客户,然后播放器绘制截止时间。 现在,用户可以轻松导航到报告的开头和结尾。 这是一个严格的业务要求,非常方便和有用。 您不必浪费时间查找报告的实际开始时间。 当我们进行预览时,那将是绝对精彩的。

部署

——我想问一下部署的问题。 Kolya 和团队一开始就花了很多时间来建立整个基础设施,让一切都为我们展开。 告诉我,这一切都是由什么制成的?

尼古拉: 从技术角度来看,我们最初要求产品尽可能从任何供应商那里抽象出来。 来到 AWS 专门从 AWS、或者专门从 Yandex、或者从 Azure 等制作 Terraform 脚本。 不太适合。 我们必须在某个时候搬到某个地方。

在前三周,我们一直在寻找更好的方法。 因此,我们得出的结论是,在这种情况下,Kubernetes 就是我们的一切,因为它允许我们创建自动扩展的服务、自动部署,并获得几乎所有开箱即用的服务。 当然,所有服务都必须经过培训才能与 Kubernetes、Docker 配合使用,团队也必须学习。

我们有两个集群。 测试和生产。 它们在硬件和设置方面完全相同。 我们将基础设施实施为代码。 所有服务都会使用自动管道自动部署到功能分支、主分支、测试分支和 GitLab 三个环境中。 这个最大限度地集成到GitLab中,最大限度地与Elastic、Prometheus集成。

我们有机会快速(对于后端在 10 分钟内,对于前端在 5 分钟内)通过所有测试、集成、运行功能测试、环境集成测试以及负载测试来对任何环境进行更改测试环境与我们想要在生产中获得的环境大致相同。

关于测试

— 你几乎测试了所有内容,很难相信你是如何编写所有内容的。 您能否告诉我们有关后端测试的信息:涵盖了多少内容,哪些测试?

弗拉季米尔: 已经编写了两种类型的测试。 首先是组件测试。 整个弹簧应用和底座的提升水平测试 测试容器。 这是对最高级别业务场景的考验。 我不测试功能。 我们只测试一些大的事情。 例如,在测试中,模拟登录用户的过程、用户请求他可以去的地方的门票,以及请求访问观看流。 用户场景非常清晰。

在实际环境中运行的所谓集成测试中实现了大约相同的事情。 事实上,当下一次生产部署推出时,真正的基本场景也正在生产中运行。 相同的登录,请求票证,请求访问CloudFront,检查流是否确实与我的权限连接,检查导演的界面。

目前我正在进行大约 70 个组件测试和大约 40 个集成测试。 覆盖率非常接近 95%。 这是针对组件的,较少针对集成的,根本没有那么多需要。 考虑到该项目涉及各种代码生成,这是一个非常好的指标。 没有其他办法可以完成我们三个月内所做的事情。 因为如果我们手动测试,为我们的测试人员提供功能,她会发现错误并将其返回给我们进行修复,那么调试代码的往返行程将非常长,我们将无法满足任何截止日期。

尼古拉: 传统上,要在更改某个功能时对整个平台进行回归,需要坐两天到处戳。

弗拉季米尔: 因此,当我估计一个功能时,我说我需要 4 天来使用两支简单的笔和 1 个 websocket,这是一个巨大的成功,Kolya 允许。 他已经习惯了这四天包括两种测试,然后很可能就会起作用。

尼古拉: 我还编写了 140 个测试:组件+功能,它们做同样的事情。 所有相同的场景都在生产中、测试中和生产中进行测试。 我们最近还添加了功能性基本 UI 测试。 通过这种方式,我们涵盖了可能会崩溃的最基本的功能。

弗拉季米尔: 当然,值得一提的是负载测试。 有必要在接近真实负载的负载下测试平台,以便了解一切情况、Rabbit 发生了什么、JVM 发生了什么以及实际需要多少内存。

— 我不确定我们是否在流端测试任何东西,但我记得我们聚会时转码器出现了问题。 我们测试过流吗?

阿尔乔姆: 反复测试。 组织聚会。 在组织聚会的过程中,大约有 2300 张 JIRA 门票。 这些只是人们为了聚会而做的一般事情。 我们将平台的一部分放到了一个单独的聚会页面上,该页面由基里尔·托尔卡乔夫(Kirill Tolkachev)运营(谈话).

说实话,没有什么大问题。 事实上,有几次我们在 CloudFront 上发现了缓存错误,我们很快就解决了它 - 我们只是重新配置了策略。 网站上的流媒体系统中的人员错误明显增多。

在会议期间,我不得不写更多的出口商,以便涵盖更多的设备和服务。 在某些地方,我不得不自己制造自行车,只是为了指标。 AV(音频视频)硬件的世界并不十分美好——你拥有某种你根本无法影响的设备“API”。 事实并非如此,您就能获得所需的信息。 硬件供应商真的很慢,几乎不可能从他们那里得到你想要的东西。 总共有 100 多个硬件,它们不能返回您需要的东西,并且您编写了奇怪且冗余的导出器,借助它们您至少可以以某种方式调试系统。

Оборудование

— 我记得在会议开始之前我们购买了部分额外的设备。

阿尔乔姆: 我们购买了电脑、笔记本电脑和电池组。 目前我们可以在没有电的情况下生活40分钟。 六月,圣彼得堡遭遇了严重的雷暴,所以我们遭遇了停电。 与此同时,一些提供商从不同的地点向我们提供光链路。 这实际上是 40 分钟的建筑停机时间,在此期间我们将打开灯光、声音、摄像机等。

— 我们在互联网上也有类似的故事。 在我们工作室所在的办公室里,我们在楼层之间拖着一张凶猛的网。

阿尔乔姆: 我们在楼层之间有 20 Gbit 的光纤。 再沿着楼层,某处有光学器件,某处没有光学器件,但通道仍然少于千兆位通道 - 我们在会议轨道之间在它们上运行视频。 一般来说,在自己的基础设施上工作非常方便;您很少可以在现场的线下会议上这样做。

— 在我在 JUG Ru Group 工作之前,我看到了线下会议的硬件室是如何在一夜之间搭建起来的,那里有一个大监视器,上面显示着你在 Grafana 中构建的所有指标。 现在还有一个总部房间,开发团队就坐在那里,在会议期间修复一些错误并开发功能。 同时,还有一个监控系统,显示在大屏幕上。 Artyom、Kolya 和其他人坐下来确保这一切不会掉落并且工作正常。

好奇心和问题

— 你说得很好,我们有亚马逊流媒体,有网络播放器,一切都是用不同的编程语言编写的,提供容错和其他业务要求,包括支持法人实体的个人帐户和个人,我们可以与使用OAuth 2.0的人集成,有反欺诈、用户阻止。 我们可以动态地推出更改,因为我们做得很好,而且都经过了测试。

我很想知道开始做某事时会遇到哪些奇怪的事情。 当你开发后端、前端时,是否遇到过奇怪的情况,结果出现了一些疯狂的东西,而你不知道该如何处理它?

弗拉季米尔: 在我看来,这种情况只发生在最近三个月。 每天。 正如你所看到的,我的头发都被拔掉了。

90天内开发一个视频平台
Vladimir Krasilshchik 三个月后,当某种游戏问世时,没有人知道如何处理它

每天都会有这样的事情,当有那么一个时刻,你拿着它,撕扯你的头发,或者意识到没有别人,只有你能做到。 我们的第一个大型活动是 TechTrain。 6 月 2 日凌晨 2.0 点,我们还没有推出生产环境,Kolya 正在推出它。 并且个人帐户无法作为使用OAuth2.0的授权服务器。 我们将其转变为 OAuth18 提供程序以将平台连接到它。 我已经连续工作了大概 XNUMX 个小时,我看着电脑,没有看到任何东西,我不明白为什么它不工作,Kolya 远程查看了我的代码,寻找 Spring 配置中的错误,找到了,LC 工作了,并且也在生产中。

尼古拉: 在 TechTrain 发布前一小时。

很多星星都聚集在这里。 我们非常幸运,因为我们有一个超级团队,每个人都受到在线做事的想法的启发。 这三个月里,我们都被“创造了 YouTube”这一事实所驱动。 我不让自己胡思乱想,而是告诉大家一切都会好起来的,因为其实一切都早已计算好了。

关于性能

— 您能告诉我该网站上一条轨道上有多少人吗? 是否存在任何性能问题?

尼古拉: 正如我们已经说过的,不存在性能问题。 一场报告的最大参加人数为1300人,这是在Heisenbug上。

— 本地观看有什么问题吗? 是否可以通过技术描述和图表来说明其工作原理?

尼古拉: 稍后我们会写一篇文章讨论这个问题。

您甚至可以在本地调试流。 会议开始后,事情变得更加容易,因为出现了我们可以随时观看的制作流程。

弗拉季米尔: 据我了解,前端开发人员在本地使用模拟进行工作,然后,由于向前端开发人员推出的时间也很短(5分钟),因此检查证书的情况没有问题。

— 一切都经过测试和调试,甚至在本地也是如此。 这意味着我们将写一篇包含所有技术特征的文章,向您展示,用图表告诉您一切,以及它是怎样的。

弗拉季米尔: 你可以接受它并重复它。

- 3个月内。

— 考虑到这是一个小团队在三个月内完成的,所有描述在一起听起来很酷。

尼古拉: 一个大团队不会这样做。 但如果一小群人彼此沟通非常密切、良好并且能够达成一致,那么就可以做到这一点。 他们没有任何矛盾,架构是在两天内发明的,最终确定的,实际上并没有改变。 在堆积功能请求和更改方面,对传入的业务需求有非常严格的促进作用。

— 夏季会议已经召开后,您的进一步任务清单上有哪些?

尼古拉: 例如,学分。 视频上出现蠕动线条,视频中某些位置会弹出窗口,具体取决于所显示的内容。 例如,演讲者想向听众提问,屏幕上会弹出一个投票,根据投票结果返回给演讲者本人。 在演示过程中以点赞、关注、评分等形式进行某种社交活动,以便您可以在适当的时候填写反馈,而不会在以后因反馈表格而分心。 最初是这样的。

并且还给整个平台添加了除了流媒体和会议之外,也是一个会后状态。 这些是播放列表(包括由用户编制的播放列表),可能是来自其他过去会议的内容,经过集成、标记、用户可以访问,也可以在我们的网站上查看(live.jugru.org).

— 伙计们,非常感谢你们的回答!

如果读者中有参加过我们夏季会议的人,请分享一下您对播放器和广播的印象。 什么让你方便,什么让你恼火,你将来希望看到什么?

如果您对该平台感兴趣并希望看到它“在战斗中”,我们会在我们的网站上再次使用它 秋冬会议。 它们的范围很广,所以几乎肯定有一个适合您。

来源: habr.com

添加评论