本文转载自公众号:流利说技术团队(lls_tech)

版权归原作者所有

本文主要介绍了流利说团队基于 Cocos 游戏引擎进行音视频相关需求开发过程中所遇到的问题和解决方案。文章中将依次阐述 Cocos 引擎直接渲染视频的方案,继而引申出多线程环境下 OpenGL 环境的管理方法,最后说明音视频处理流水线模型需要解决的问题与我们的方案。

让 Cocos 引擎直接渲染视频

为什么?

可能大家首先会疑惑,为什么要让 Cocos 引擎来负责渲染视频呢?而不利用原生平台的渲染机制,如使用Android平台的SurfaceView或TextureView。

让我们来分析下利弊:

原生机制优势:1.常规播放器接口的直接支持。2.视频渲染性能稳定。3.代码简单。

原生机制劣势:1.无法更精确的调整视频与游戏元素的层级关系(只能置游戏其上或其下)2.游戏控制播放器动画有性能损耗(游戏→ native 损耗)。

因此,如果要在视频层上播放游戏显示游戏元素或游戏动画,利用原生机制无疑会转移大量的游戏业务在native端完成。这无论从项目管理和实现复杂度考虑都是不可取的。因此我们决定让 Cocos 引擎使用外接纹理渲染视频。

实现方案

分析 Cocos 引擎本身的渲染机制,我们发现cocos引擎中封装了cocos2d::Sprite 对象用于渲染显示,Sprite对象需要我们提供 cocos2d::Texture2D 对象和尺寸信息。因此我们可以封装一个 Cocos 引擎中的基础节点 cocos2d::ui::Widget 专门用于视频显示,Cocos 中 widge 对象是类似于 native 中 View 功能的组件,负责管理组织绘制大小,绘制位置和绘制内容。我们可以在其回调的 Draw 方法中完成视频绘制。

在 native 层我们将视频输出数据转为纹理,再传递至Widget中转化为 Cocos 的Texture2D对象交由 Sprite 绘制。而 Sprite 渲染尺寸则由 widget 提供。由此我们可以得出下面这样一个简易的转化链:

多线程 OpenGL

但是上面这条转化链并不能简单的实现。首先 Cocos 引擎是在单独开启的一个线程中进行工作的,以下简称 Cocos 线程。也就是说我们最终 OpenGL 的绘制都会在 Cocos 线程中操作。我们用 Cocos 线程的 OpenGL context 去进行纹理转化,甚至增加贴图美颜等功能都是不合适的。音视频中有一些 OpenGL 操作,很有可能使 Cocos 整个 OpenGL 状态机被破坏掉。所以需要将所有的音视频转化、处理操作都限制在子线程中。

假如我们需要在多线程下共享纹理数据,需要让 OpenGL Context 共享同一个ShareGroup。因此我们需要接管整个架构环境中所有 OpenGLContext的构建过程。如Android端我们需要在 Cocos 引擎Cocos2dxActivity的中将 Cocos2dxGLSurfaceView::setEGLContextFactory 修改为我们自己的提供的方法。除此之外,纹理转化和处理模块的 OpenGL 环境也需要统一构建共享 ShareGroup 的 context。

Android端有一点特殊之处,屏蔽了 ShareGroup 的概念。但是我们只要在 OpenGL Context 的构造函数传入一个 Context,即可让两个 Context共享 ShareGroup。

音视频处理流水线模型

建立模型

为了整合音视频处理的各个环节,构建统一的错误处理、线程管理、生命周期管理机制,我们对音视频处理流程进行了抽象,建立起一个以音视频源、线程分发器、消费者链组成的流水线模型。

抽象出的音视频源负责加载本地或网络视频资源,而后进行解码操作。亦或者为采集摄像头数据的采集器,最终输出视频帧数据。而消费者组成消费处理链,负责接收处理帧数据或纹理数据。如我们自定义的 cocos::Widget 可以作为消费链的最后一个消费者。

线程分发器即是负责连接源与消费者。线程分发器创建管理音视频各自的工作线程,把外部命令和音视频数据分发至目标线程再回调给消费者,保证消费者内部方法在同一线程执行,从而消除消费者模块的中的线程安全问题。

按照这样的方式建立的流水线模型具备较好的稳定性和扩展性,可以保证如 OpenGL 上下文管理,视频帧数据转化为纹理等诸多模块的复用。另外由于消费者和生产者的完全解耦,也能够实现诸如动态切换音视频源的功能。此外多线程流水线也能很好的发挥多核 CPU 的性能。

尽管模型已经建立,但在细节方面还存在不少问题等待我们去解决。下面我就简单说明几个问题以及我们的探索。

音视频帧数据的复用

为了避免内存频繁分配而造成的不必要的耗时。我们通常会对构建的之前生成过得音视频数据进行复用,因为存在多分辨率切换的问题,由此会生成诸多大小不一的内存块。因此复用的前提是被复用的内存>=需要分配的内存。在 Android 端即是指 ByteBuffer 的capacity需要满足上述条件。因此我们可以建立一个ByteBuffer对象池用于缓存已经被消费完成的ByteBuffer,在复用时遍历缓存池找寻符合大小条件的 ByteBuffer。在一般情况下视频数据需要 ByteBuffer 数组来存储,因此我们可以对对象池的每个对象增加标签属性,保证相同分辨率的视频数据可以快速找到可被复用的内存。

那么音视频数据被回收的时机是什么呢?单线程模型下是极为容易确定的,但是多线程环境下事情就变得复杂了,我们无法知道什么时候数据才被真正的消费完成。因此我们参考图片加载框架Fresco中对Bitmap回收问题的解决方案引入 Closeable References(可回收引用)概念。CloseableRef 对象包裹我们需要缓存的对象,内置的引用计数会在我们所有线程持有的引用都被 close 后才会回收。在回收的回调方法中我们将其加入缓存对象池中。

工作线程的阻塞监控

开发多线程复杂项目我们必须考虑到在低配机型下,工作线程积攒大量任务无法被消费处理的情况。如音频和视频的处理线程,如果视频处理过慢可能会导致严重的音画不同步。因此我们需要建立可以被管理的工作线程任务队列。我们在 Android 端的实践为:基于 HandlerThread ,另外增加一个可以被管理的Queue。每当产生任务,我们将任务入栈,并向 HandlerThread 发送一条出栈指令,HandlerThread  从 Queue 末尾出栈处理任务。

Queue 中可以记录多项重要参数用于决策处理。如综合任务预期执行时间、任务的处理时间和队列积攒数量进行进行策略性丢弃。或者根据一段时间的综合情况来决定是否降级生产音视频的分辨率等参数等。

总结

以上讲述的几个技术关键点是我们团队在项目开发过程中不断探讨与发现得出的。整套框架方案已经在项目中落地,获得了还不错的开发结果。希望能为大家带来些许帮助。另外敬请期待我们少儿流利说即将上线的直播课功能。

cocos creator 方法数组_基于 Cocos 游戏引擎的音视频研发探索相关推荐

  1. cocos creator 方法数组_Creator | 优化三剑客之内存!

    官方文档: 资源加载: https://docs.cocos.com/creator/manual/zh/scripting/dynamic-load-resources.html 资源释放: htt ...

  2. Cocos Creator入门实战:桌球小游戏

    Cocos Creator入门实战:桌球小游戏 转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/90035153 本篇主要是希望能够通过C ...

  3. ​Cocos Creator入门实战:桌球小游戏

    本文作者:BigBear 多年游戏行业研发经验 精通Unreal.CocosCreator游戏引擎 参与过多款手游.端游项目的研发 Cocos Creator入门实战:桌球小游戏 本篇主要是希望能够通 ...

  4. Cocos Creator:快速开发推箱子游戏

    本文首发于公众号: 一枚小工(caizj_cn) Cocos 已获得转载授权! 作者:蒲公英,开发工程师,7 年 Cocos 引擎使用经验. 游戏总共分为 4 个功能模块: 开始游戏(menuLaye ...

  5. 使用 cocos creator 3.0 制作抽奖小游戏

    使用 cocos creator 3.0 制作抽奖小游戏 描述 一个抽奖小游戏demo, 点击 抽奖按钮 进行抽奖, 抽完奖后该结果置灰.下一次抽奖就会跳过已经抽过的奖项. 注意: 每次点击 抽奖按钮 ...

  6. cocos creator 2.4.5消除小游戏新玩法消星星源码H5+安卓+IOS三端源码

    cocos creator 2.4.5消除小游戏新玩法消星星源码H5+安卓+IOS三端源码,开发脚本为typeScript方便扩展和阅读,支持cocos creator2.X版本,完整的源码可直接运营 ...

  7. cocos creator 2.1.4休闲小游戏《颜色跳跃》源码H5+安卓+IOS三端源码

    cocos creator 2.1.4休闲小游戏<颜色跳跃>源码H5+安卓+IOS三端源码,开发脚本为typeScript方便扩展和阅读,支持cocos creator2.X版本,完整的源 ...

  8. unity游戏开发毕设_《毕业设计(论文)-基于Unity游戏引擎的游戏设计》.doc

    学 号070125分类号本科生毕业论文(设计) 题目: 基于Unity游戏引擎的游戏设计 院(系) 电子与信息工程系专业 计算机科学与技术班级2007级学 生 姓 名指导教师(职称)提 交 时 间 二 ...

  9. Android基于腾讯云实时音视频实现类似微信视频通话最小化悬浮

    最近项目中有需要语音.视频通话需求,看到这个像环信.融云等SDK都有具体Demo实现,但咋的领导对腾讯情有独钟啊,IM要用腾讯云IM,不妙的是腾讯云IM并不包含有音视频通话都要自己实现,没办法深入了解 ...

最新文章

  1. spring-retry----线程内重试
  2. lux系统服务器安装后多大,服务器环境搭建
  3. 我的世界java版导入地图_网易我的世界备份与导入地图
  4. 搭建基础架构-Order
  5. 开源硬件论坛,燃烧你的创造力
  6. centos7限制cpu使用_CentOS7 CPU隔离配置
  7. 运用Logistic模型检验影响企业现金分红的主要因素
  8. Python统计TXT词云
  9. 编程语言对比 面向对象
  10. 网络嗅探器如何嗅探_SQL Server中的运行时常量嗅探
  11. Mount挂载命令使用方法
  12. MyBatis 简介
  13. 3991. 满足条件的01串-AcWing题库
  14. 全球顶尖!StarVR推出搭载集成眼动追踪的VR设备
  15. 背下这148句话,你可以提高一个档次了,不止在文学方面
  16. 基于Docker从零搭建Prometheus监控报警系统
  17. 水处理相关工艺控制程序汇总(RXXW_Dor)
  18. CEO年薪过亿,谁说了算?
  19. 基于Wireshark的IP数据包头中ToS段介绍2-1
  20. 舌尖美味实践团采访活动

热门文章

  1. php 同一行,php – 如何在同一行中对类方法进行多个调用?
  2. 计算机组成原理输入实验报告,计算机组成原理实验报告
  3. java 根据类名示例化类_Java LocalDateTime类| AdjustInto()方法与示例
  4. Java中List排序的3种方法!
  5. 第 5-4 课:ThreadLocal 详解 + 面试题
  6. C++ SVM Opencv3.4实现人脸检测
  7. 简单的ajax上传商品功能使用SevletFileUpload和FormData
  8. Synchronized 和 Lock 区别
  9. html ajax请求c#,c# – 获取通过webclient中的AJAX生成的html
  10. 小数分数转换c语言,这是把小数转换成分数的程序,可是输入0.6666无限循环