OpenGLES 实验室之2D篇 第三弹 の 直播白板

news2024/11/29 2:51:50

f75a150aac7c95ddc13e6b4c244f091e.gif

本文字数:4555

预计阅读时间:12分钟

笔者之前发表的音视频文章,有图像的处理,音频的重采样等等,都属于入门级别。通过阅读它们,读者能对音视频有了了解。可在 Gitee 上面回顾。

       2023 年,笔者将整理下 关于OpenGLES的实验室系列 并进行发表。首先为读者带来2D篇的系列,它大多是x y坐标,不涉及z坐标,所以用 2D篇。内容上,它不对OpenGLES的基础知识进行细说与讨论。但如果对OpenGLES不了解或者了解一点,仍可通过本实验室系列了解OpenGLES。它旨在激起读者的兴趣,扩展到实际的应用上。总的来说,这些实验& Demo将是额外的,即对基础学习的补充,通过这些它们的实践和运用,能让读者进一步了解OpenGLES。

前言

本次实验室带来的是《OpenGLES实验室之2D篇 第三弹 の 直播白板》。

直播白板应用在搜狐视频App直播,配合播主的教学,在白板上展示英语、物理、历史等图片或者手绘文字。更加生动且有趣的进行讲解,并且还可以在助手App使用 PPT 等准备好的教材,让直播内容更加便捷,且素材丰富。助手还提供了大白板和小白板的切换来展示不同白板的效果,更充分和自由的展示白板,也让观众可以学习更多的知识。

而直播白板的原理是将另一部设备(助手端)的截图数据传到直播的手机上,然后将该图片进行主播端展示和推流的混流。

而本Demo只包含采集,不包含推流,所以摄像头采集的数据,与白板图片(固定图片)进行混流后回显。其实该混流输出的这个output就是要推流给观众观看的直播流画面。可能有读者会问,那为什么直播预览不直接用混流回显?其实是可以的,但缺少灵活性。主播端分开展示,可以比较好的满足主播端的多样性,如实现大小白板(不同白板大小)切换的动画,主播预览的自定义区域设置等等。

Demo   

Demo分别是直播实现的 多重纹理(顶部小白板) 和 多通道渲染(全屏大白板)。

QHWhiteBoardMan: iOS:OpenGLES实验室之2D篇 第三弹 の 直播白板

实现

https://www.kdocs.cn/l/cbAanyom6hhH

搜狐视频App的白板效果如下:

fbf5a0d29e85ff6dd687444f91d02460.png

大白板

4b43c0d1280534986b102bb6ec72bed3.png

小白板

结构图

多通道渲染采用多纹理多次绘制,而多重纹理 采用多纹理单次绘制。

8430cb6785a889858143e1f6aff1fa83.png

多通道渲染

通过管道组合进行一层一层绘制,跟画画很像。

多通道渲染的顺序很重要,由于大白板的主播画面是白板的上面,所以需要先绘制白板,再绘制主播画面,否则白板会覆盖在主播画面,这样主播就预览不到自己。

// 第一层:白板
glViewport(0, 0, (int)_frameWidth, (int)_frameHeight);
        
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _texId1);
glUniform1i(_filterInputTextureUniform, 1);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// 第二层:主播画面
CGFloat d = 4;
glViewport(0, 0, (int)_frameWidth/d, (int)_frameWidth/d);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _texId2);
glUniform1i(_filterInputTextureUniform2, 2);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// 最终
glViewport(0, 0, (int)_frameWidth, (int)_frameHeight);

Shader只需直接输出像素

NSString *const rgbFragmentShaderString = SHADER_STRING
(
 varying highp vec2 v_texcoord;
 uniform sampler2D inputImageTexture;
 
 void main()
 {
     gl_FragColor = vec4(texture2D(inputImageTexture, v_texcoord).bgr, 1);
 }
);

       效果如下:

6410d705b123f1f8c7e42adceb8926b9.png

多重纹理

可见与多通道渲染明显的不同就是少了一次glDrawArrays,它不仅仅是一次绘制,其包含的执行可看下一章节:对比

glViewport(0, 0, (int)_frameWidth, (int)_frameHeight);
        
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _texId1);
glUniform1i(_filterInputTextureUniform, 1);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _texId2);
glUniform1i(_filterInputTextureUniform2, 2);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Shader需要增加多一个sampler2D图数据变量,并进行区域控制,或者进行mix。

NSString *const rgbFragmentShaderString2 = SHADER_STRING
(
 varying highp vec2 v_texcoord;
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 
 void main()
 {
    if (v_texcoord.y < 0.5) {
        gl_FragColor = vec4(texture2D(inputImageTexture2, v_texcoord).bgr, 1);
    }
    else {
        gl_FragColor = vec4(texture2D(inputImageTexture, v_texcoord).bgr, 1);
    }
 }
);

       效果如下:

e15e5639b938c753c74b04b7ed07effe.png

对比

多通道渲染与多重纹理的对比:

1、多通道渲染会多次绘制glDrawArrays,而多重纹理则绘制一次;

2、第二次后的绘制都要进行 前像素颜色的读取,并和当前像素颜色混合(如果允许alpha,则它们会进行mix,《iOS:OpenGLES实验室之2D篇 第一弹 の 智能弹幕》介绍过),这些内部操作,即需要的更多性能开销,只要执行一次就多一次内部开销;

3、多通道渲染可通过glViewport更改每次绘制的视图区域,而多重纹理则不行,因为视图只有一个,但可以在fsh上处理,相对比较麻烦,还得进行图片的自行缩放;

4、多通道渲染的管道组合下,可动态添加图层。多重纹理需要在 fsh 里提前添加 纹理数据变量,并进行读取才可以。Apple的 GLKBaseEffect 支持 2 个 GLKVertexAttribTexCoord。

纹理缓存

首先将白板图片的数据保存到 缓存,使用《iOS:OpenGLES实验室之2D篇 第二弹 の 瘦脸修图》的一样的实现,直接将png转为rgba的原始数据,上传到共享缓存,并绑定 纹理id。

glGenTextures(1, &_texId2);
glBindTexture(GL_TEXTURE_2D, _texId2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
unsigned char *pBGRAImageIn;
[QHUtil input:@"WhiteBoard_rgba" ofType:@"rgb" len:&pBGRAImageIn];
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)1125, (GLsizei)2436,
             0, GL_RGBA, GL_UNSIGNED_BYTE, pBGRAImageIn);
glBindTexture(GL_TEXTURE_2D, 0);

       激活纹理后使用,因为前面已经将图片缓存到共享内容,GPU可以通过 textureId直接获取数据。

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _texId2);
glUniform1i(_filterInputTextureUniform2, 2);

Android Demo

Demo上 Android 与 iOS 的相同点

       在OpenGLES,在调用上和转化纹理有稍微不同,当主流程是一样, 都是 多通道渲染 先绘制白板,再绘制摄像头采集,而多重纹理则同时上传两个纹理进行位置的判断来选择渲染。

       而GLSL的编写则都是一样的。

说明

多通道渲染采用Java层调用GLES,多重纹理 则采用CPP层调用 GLES,这应该是Android端调用OpenGLES的两种方式。

       然后白板也使用两种上传纹理的方式,一种是bitmap,一种是RGBData。

       由于笔者编写Android能力有限,所以Demo的 多通道渲染是参考 安卓同事(曾哥童鞋) 的声网自采集水印,多重纹理是参考 《音视频开发进阶指南——展晓凯/魏晓红》 的采集例子。因此Demo里面对于Camera和GLES 的封装和调用,笔者基本原封保留,这边对以上参考表示感谢。

       由于白板的比例不一致,为保持GLSL写法一致,所以没处理变形,请忽略。

4db19b0bf0ecc2731d520a0daa70b518.jpeg

多通道渲染

136ded4b7f357c13b8e22b1cbc5654d5.jpeg

多重纹理

最后

       实际中,直播白板的内容是改变的,会不停传图片数据,然后进行更新。这其实才是OpenGLES混流最大的性能瓶颈里,因为大量的改变会进行很大的内存交换。

       总的来说,如何选择使用 多通道渲染 还是 多重纹理,它们都能做到相同的混流效果,只是编写的实现不一样,还需根据实际需求进行选择应用哪种实现技术。而对直播而言,会使用超过两个图层,如直播水印,直播昵称,水印广告,大小白板,logo等等,所以倾向于 多通道渲染,它在直播推流器的框架设计更具扩展性。

       感谢各位读者,本实验室之2D篇,完结啦👋!

       欢迎读者回顾本系列的文章:

  • 《iOS:OpenGLES 实验室之2D篇 第一弹 の 智能弹幕》

  • 《iOS:OpenGLES 实验室之2D篇 第二弹 の 瘦脸修图》

链接

  • 《Gitee》

    https://gitee.com/chenqihui

  • 《QHWhiteBoardMan: iOS:OpenGLES 实验室之2D篇 第三弹 の 直播白板》

    https://gitee.com/chenqihui/qhwhite-board-man

  • 《BradLarson/GPUImage: An open source iOS framework for GPU-based image and video processing》

    https://github.com/BradLarson/GPUImage

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

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

相关文章

如何使用depcheck检查vue和react的依赖,以后不用把时间浪费在依赖问题上了

当我们在开发 JavaScript 项目时&#xff0c;会引入各种依赖库。但是有些依赖库可能只用到了部分功能&#xff0c;或者已经不再需要了&#xff0c;但是却一直被保留在项目中。 这些未使用的依赖库会占据项目的空间&#xff0c;增加项目的复杂度&#xff0c;影响项目的性能。为…

Elasticsearch --- 索引库、文档操作

一、索引库操作 索引库就类似数据库表&#xff0c;mapping映射就类似表的结构。 我们要向es中存储数据&#xff0c;必须先创建“库”和“表”。 1.1、mapping映射属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; type&#xff1a;字段数据…

【python】scikit-learn包:机器学习

环境配置&#xff1a;Scikit-learn包 只支持python语言 安装 WinR &#xff0c;输入指令&#xff1a;pip install -U scikit-learn 数据预处理 数据导入 借助pandas和numpy 进行数据导入与处理 字符串类label的数字化编码 机器学习的函数大部分只能对数字信息进行处理&…

轻量级任务看板做任务管理

利用看板管理工作和任务&#xff0c;可以让团队更高效&#xff0c;也可以一目了然的了解任务进度及问题 1、首先创建一个任务看板 使用看板工具轻量级项目模板创建一个任务看板。 任务看板内包含&#xff1a;列表和任务卡片&#xff0c;列表一般代表任务流程及状态&#xff…

(四)ArcMap基础——要素的选择

要素的选择 当要在已有的数据中选择部分要素时&#xff0c;ArcMap提供了三种方式&#xff1a;按属性选择、按位置选择及按图形选择。 目录 要素的选择一、按属性选择二、按位置选择三、按图形选择 一、按属性选择 通过设置 SQL 查询表达式&#xff0c;用来选择与选择条件匹配…

怎样解决高并发下的I/O瓶颈?

大家好&#xff0c;我是易安。 说起Java I/O&#xff0c;相信你一定不陌生。你可能使用I/O操作读写文件&#xff0c;也可能使用它实现Socket的信息传输…这些都是我们在系统中最常遇到的和I/O有关的操作。 我们都知道&#xff0c;I/O的速度要比内存速度慢&#xff0c;尤其是在现…

【致敬未来的攻城狮计划】— 连续打卡第十五天:FSP固件库外部中断处理编程(外部中断检测按键控制LED闪烁)

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

J - 在赌场玩

第一周任务 - Virtual Judge (vjudge.net) http://t.csdn.cn/rcwO7 第一周任务 - Virtual Judge (vjudge.net) 【题目描述】 然后所有玩家成对玩&#xff0c;每对玩家只玩一次。因此&#xff0c;例如&#xff0c;如果总共有四个玩家&#xff0c;则进行六场比赛&#xff1a;第…

真题详解(二分查找平均值)-软件设计(六十)

真题详解&#xff08;数据流图平衡&#xff09;-软件设计&#xff08;五十九)https://blog.csdn.net/ke1ying/article/details/130394959 全码&#xff1a;指关系模式所有属性都是这个关系模式的候选码。 RISC特点&#xff1a; 指令种类&#xff1a;少&#xff0c;精简 指令…

彻底弄懂Java的泛型1 - 泛型类

Java泛型是初级程序员向中高级程序员进阶的必经之路&#xff0c;他不是特别难&#xff0c;但是想全部搞懂和会用&#xff0c;还是不容易的。 本文从实战角度出发&#xff0c;讲解你在公司做开发&#xff0c;可能会用到泛型的一种场景。 泛型T的用法 引子 先来看一个简单的类…

UDP 协议

目录 一、什么是协议 二、认识UDP 协议 2.2 UDP 协议的报文格式 2.3 使用UDP 协议传输大文件时的策略 2.4 UDP协议的工作流程 一、什么是协议 为了使数据在网络上传输&#xff08;从源头到达目的&#xff09;&#xff0c;网络通信的参与方必须遵循相同的规则&#xff0c;如…

SpaceX的星舰爆炸了:产品开发,快速失败真的很重要

目录 前言 快速失败 产品生命周期 专栏上线 前言 看到很多人都在聊星舰&#xff0c;今天就来简单谈谈“炸星舰”带给我们的启示。 在美国中部时间20日&#xff0c;SpaceX公司的“星舰”超重型火箭进行了首次轨道飞行。 但在该火箭成功点火升空几分钟后&#xff0c;却在半…

Java-synchronized实现详解(从Java到汇编)

synchronized作为java语言中的并发关键词&#xff0c;其在代码中出现的频率相当高频&#xff0c;大多数开发者在涉及到并发场景时&#xff0c;一般都会下意识得选取synchronized。 synchronized在代码中主要有三类用法&#xff0c;根据其用法不同&#xff0c;所获取的锁对象也…

如何通过开源项目搭建私有云平台--第四步下:安装rancher 监控

第四步下&#xff1a;安装rancher 监控,缺告警 本来想监控与告警一起写&#xff0c;但最近几天研究了rancher的告警&#xff0c;按照文档说法&#xff0c;配置了但没有触发&#xff0c;网上找了一些资料&#xff0c;有的在rancer 2.6成功的&#xff0c;但我用同样的方法在2.7.…

09 【Sass语法介绍-函数指令】

1.前言 在之前的章节我们学习过 Sass 提供的各种各样的函数&#xff0c;那么如果我们需要自定定义函数来使用就需要用到函数指令 function了。本节内容我们来学习 Sass 函数指令的语法和使用&#xff0c;在 Sass 中自定义函数是必须要掌握的&#xff01; 2.什么是 Sass 函数指…

又一款可视化神器,开源了!

在互联网数据大爆炸的这几年&#xff0c;各类数据处理、数据可视化的需求使得 GitHub 上诞生了一大批高质量的 BI 工具。 借助这些 BI 工具&#xff0c;我们能够大幅提升数据分析效率、生成更高质量的项目报告&#xff0c;让用户通过直观的数据看到结果&#xff0c;减低沟通成…

安卓项目如何做单元测试

前言 先说一下创建篇文章的目的&#xff0c;近期负责搭建公司的单元测试框架&#xff0c;于是查阅了网上的很多文章&#xff0c;以及参考了github上很多的项目例子&#xff0c;并且也进行了相当多的尝试。这其中花费了很多的精力&#xff0c;大约有两三周的时间&#xff0c;远…

淘系抓包流程(淘宝数据无法抓包解决方式)

淘系抓包流程 结合frida和adb工具以及mumu模拟器进行抓包。 具体的关系图: frida的安装 frida安装&#xff0c;直接安装官网的脚手架。frida官网使用python的pip安装&#xff0c;python > 3。 安装后使用查看版本命令来确认是否安装。 pip install frida-tools frida --ve…

【严重】VMware Aria Operations for Logs v8.10.2 存在反序列化漏洞(CVE-2023-20864)

漏洞描述 VMware Aria Operations for Logs前身是vRealize Log Insight&#xff0c;VMware用于处理和管理大规模的日志数据产品。 VMware Aria Operations for Logs 8.10.2版本中存在反序列化漏洞&#xff0c;具有 VMware Aria Operations for Logs 网络访问权限的未经身份验…

“SCSA-T学习导图+”系列:交换技术之STP

本期引言&#xff1a; 在通信工程当中&#xff0c;从物理层面上&#xff0c;我们可以采用冗余链路保证网络的健壮性。冗余是指出于系统安全和可靠性等方面的考虑&#xff0c;人为地对一些关键部件或功能进行重复的配置。当系统发生故障时&#xff0c;比如某一设备发生损坏&…