网络视频服务方式,经历了点播、直播、互动直播的发展过程。传统的一端收录、一端播放,交流依靠文字评论的方式,用户参与感不强。而互动视频技术,允许收看方实时参与互动,具有即时性和互动性,通过增强反馈、剧情参与、内容探索等方式为观众带来丰富的观看体验,并能通过互动创造内容和价值,因此更能吸引受众。
与常见的娱乐视频不同,线上教育领域,会更需要孩子和老师进行实时的互动。互动视频中针对 K12 教育的特殊场景做的专门优化,可以为不同学员提供独特的学习体验。本文将对新东方互动视频系统的移动端架构设计做一个总结,包含过程中的技术演进以及后续的优化设想。为了确定互动视频支持的功能模块,我们从移动端的角度,梳理了如下需求:能够根据互动视频场景需要,有选择地使用播放器原有的基础能力,实现包括倍速、快进/快退等播放控制功能;获取并理解所定义接口协议的内容,创建运行时需要的节点,更新节点的状态,以及驱动节点的变化;根据播放过程中经过的节点,实时渲染出列表,并支持随时的跨节点流动;播放功能高度可用,支持包括课件/视频/音频等多种播放素材。根据以上的需求,我们对整体的功能模块进行了划分和基本设计:在上面拆分的模块中,播放内核架构在依赖组件上,主要提供节点的协议数据请求、解析包装、缓存以及核心的播控、节点流转等功能,依赖组件则是提供了播放、日志上报、录音、测评等更基础的功能。上面我们为了满足基本功能和需求,进行了模块的拆分和设计。这些功能模块,驱动着互动视频中的各种节点在一个固定的时序上进行流转。在满足目前需求的前提下,节点本身被设计成多种类型,例如视频/图片/课件/音频/答题等,其中视频/图片/音频等多媒体节点,本身自带时间轴属性,这类型的节点,在系统内部,也被定义为 Base 节点。与 Base 节点对应的是 Active 节点,Active 节点支持多节点/循环节点的复杂跳转动作,同时承载用户的交互行为并输出交互结果,而下一节点的跳转对象就由该交互结果决定。在保持扩展能力的设计原则下,Active 节点的职能可由原生或者 H5 来进行承接。而为了支持 H5 作为容器的前提下,节点的流转依然可以由统一的节点控制模块来进行协调,需要建立一个 H5 与原生的交互通道,并确立对应的交互协议。在上面的设计中,节点控制模块接收遵循自控制协议的信息,原生节点直接与之交互,而 H5 节点,则是通过 Bridge 通信协议,进行间接交互。这种控制协议的统一化,可以允许节点控制模块处理非原生节点的流转,虽然目前系统中已支持节点属性仅为原生/H5,但原则上保留了足够的扩展性。无论是直播或点播,视频是否能够流畅播放会直接影响到用户的观看感受。所以在互动视频的设计中,视频播放功能的流畅度,是重点考虑的内容。在互动视频一期的时候,我们采取资源全量下载的方案,在剧情试图进入某一个场景节点的时候,提前获取所有该节点的资源信息,并等待资源下载完毕之后,才进入该场景,这种方案的好处是明显的,在单个场景执行的内部,用户可以获得最为极致的流畅体验,但存在的弊端也同样无法忽略,在场景资源较大的情况下,用户等待下载资源的时间过长,会导致较差的体验。为了解决这个问题,我们在二期中针对性设计了秒播方案。在这个方案中,视频被视为一个个分段,当请求数据的时候,播放器不直接与外界进行网络交互,而是通过本地搭建的 HTTP 服务器进行中转。该服务器模块会分析视频流请求对象,从中获取当次请求的数据 Range,并根据该 Range 到内存找寻缓存,如果找寻不到将会从磁盘缓存中查找,同样查找不到后,就会从网络中拉取。这种分段的数据请求方式,可以灵活的响应播放器播控层的控制,在播控操作播放进度,请求 Range 发生大幅度跳跃性变动的时候,传统的视频流请求根据策略不同,可能会丢弃长度过短的视频数据,也可能出现重复下载同一段视频数据的情况。而 HTTP 本地服务自定义强,通过实现在获取到缓存之后,实时调整网络请求视频数据 Range 的方式,增强了灵活性,也提高了数据的可复用性。秒播方案提高了数据的可复用性,但如果视频是首次播放的情况,无论是内存又或者磁盘,都没有该视频的缓存数据。这种情况下,该视频的播放流畅性,无法得到足够的保证。针对这种情况,设计了节点的预加载技术,在场景节点还没有在我们的剧情轴中铺开之前,就会介入其内部的资源下载进程。视频轴列表,主要分为两种类型,一种是线性列表,另外一种则是树状列表。针对线性列表,采取预缓存所有节点的方案,保证了线性互动视频的播放流畅性,而针对树状列表,则根据当前场景节点来获取下一层的所有节点,并进行缓存,而由于树状列表的情况多样性,没有经历的节点数据常沦为无效数据,所以每次进入一个新的场景节点,都会做无效资源的清除以控制缓存的过快增长。当进入一个新的场景节点的时候,如果判断该节点资源已下载完毕,会直接对本地的节点数据进行加载,并展示该节点。而如果该节点资源暂未缓存,会直接对其进行展示,并将在展示过程中下载的资源缓存在本地,以备下次使用。在业务发展的过程中,互动视频在经历多个迭代之后,稳定性有了一定程度的增强。在基于业务的考虑下,需要将这个系统从原有的应用内部进行抽离,并封装成 SDK,供其他业务部门快速接入使用。而由于之前的架构设计虽然最大程度考虑了内部功能的可扩展性,但在例如接口隔离,代码解耦等方面并没有做到最好,所以这里重新进行了一次模块设计。在上面的架构中,SDK 整体被划分为 3 个层级。最底下的 Service 层级是提供最基础功能例如录音、测评、数据解析、视图 layout 的模块,第二个层级则是原先的互动视频的播放内核,处理互动视频的包括节点跳转、数据下载缓存、播控等功能,而最顶部的层级则是接口层,这一个层级的出现主要是为了分割 SDK 内部实现与具体业务方代码的耦合关系,同时帮助 SDK 在需要进行内部代码大幅改动的时候维持外部接口的稳定性。SDK 化之后另外一个需要考虑的就是二进制包的输出问题。目前的阶段,SDK 发版频率较低,打包效率也比较高,暂没有云端流程化打包的必要性。所以在目前的设计中,打包系统依赖 cocoapod 的插件机制来进行,在 cocoapod-package 的原有功能上做进一步扩展。目前的插件实现了本地打包的前提下,同时支持了远程更新 tag 号,自定义注入组件等功能,并支持一定的后续扩展能力。互动视频系统从概念出发,逐步落实到工程的代码上;从需求出发,逐步落实到模块拆分以及后续的 SDK 封装发放上。在架构的设计上,并非一成不变,需求的变动驱动设计的变更,核心思想是在设计中保留足够的扩展能力。在后续的优化上,混合节点是一个值得探讨的方向,目前系统中 Active 节点支持原生和 H5 节点的能力,可以尝试扩展到支持类似 Weex、Flutter、ReactiveNative 等混合开发的框架上,在性能和开发效率上做出权衡取舍的同时,为业务提供更多可能性。截止至本文完成的时间,涉及的技术点已经申请专利,并通过公司法务审核。