本文记录在OpenGL中使用 帧缓冲 (FBO)和像素缓冲(PBO)来上行视频帧数据(YUYV), 并最终渲染显示出来。在之前的文章中介绍了渲染 YUV420 和 YUYV 的流程,不过都是用的默认的帧缓冲,即创建一个和视频幅面一样的 GLFW 窗口, 但是在实际开发中会遇到各种幅面的素材,,此时默认的 GLFW 帧缓冲就不满足要求了, 所以需要借助自定义的 FBO 来完成各种幅面视频帧的上行。

  • 一、 帧缓冲(FBO)
  • 二、 像素缓冲(PBO)
  • 三、 关于YUYV纹理的采样
  • 四、 测试 Demo
  • 五、 总结

本文相关详细代码地址: https://github.com/pengguoqing/samples_code/tree/master/OpenGL

一、 帧缓冲(FBO)

  关于帧缓冲, LearnOpenGL 官方网站已经介绍得比较详细了, 这里就不再赘述。上行视频帧数据主要就是用到帧缓冲的颜色缓冲。首先根据视频帧的幅面为帧缓冲创建一个同幅面的纹理附件,FBO 创建纹理附件和渲染缓冲附件的流程都大体相似,所以就直接封装了一个简单的 CXFBO的 C++ 类,只需要简单的设置纹理附件的宽高参数即可,使用方式如下:

 CXFbo yuy2fbo;yuy2fbo.InitFbo(imgwidth, imgheight);yuy2fbo.UnBindFbo();

初始化的部分代码如下:

 glGenTextures(1, &m_colortex);glBindTexture(GL_TEXTURE_2D, m_colortex);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colortex, 0);

二、 像素缓冲(PBO)

  关于PBO这篇文章有非常详细的介绍: http://www.songho.ca/opengl/gl_pbo.html, 这里贴出原文中一张能简单清晰的阐述 PBO 原理的示意图:


比如 CPU 解码出一帧 YUV 数据后, 如果使用 PBO上行的话那么CPU 只需要运算将 YUV 数据拷贝至 像素缓冲区这一步, 后续流程都由利用 GPU 强大的并行运行能力将像素缓冲区数据加载到GPU显存中。 与不使用 PBO相比, CPU 只需要进行一次拷贝即可, 否则整个过程都由 CPU 控制,时钟周期这一缺点就显露出来了。这里贴出封装的 PBO初始化流程和调用接口:

public:bool Init(uint32_t width, uint32_t height, PBOTYPE type, GLenum pixfmt);void Map(uint8_t** ptr, uint32_t* linesize) const;void UnMap() const;void Bind() const;void UnBind() const;
bool CXPbo::Init(uint32_t width, uint32_t height, PBOTYPE type, GLenum pixfmt)
{m_width  = width;m_hegit  = height;m_pixfmt = pixfmt;switch (type){case PBOTYPE::kDynamic:m_rwtype = GL_PIXEL_UNPACK_BUFFER;break;case PBOTYPE::kStage:m_rwtype = GL_PIXEL_PACK_BUFFER;break;default:return false;break;}glGenBuffers(1, &m_pbo);if (!m_pbo){return false;}glBindBuffer(m_rwtype, m_pbo);m_pbosize  = m_width * GetPixfmtBpp(pixfmt) / 8;m_pbosize  = (m_pbosize + 3) & 0xFFFFFFFC;m_pbosize  *= m_hegit;glBufferData(m_rwtype, m_pbosize, nullptr, GL_STREAM_DRAW);glBindBuffer(m_rwtype, 0);return true;
}

三、 关于YUYV纹理的采样

  在之前的文章中 OpenGL渲染YUYV, 因为默认的帧缓冲和实际的素材的幅面相同, 而且又是用的 RGBA 来存储一组完整的 YUYV 数据, 所以 shader 中使用 texture直接采样时会自动进行插值,这就会导致 YUV 不是 严格严格匹配的,正确的做法是需要使用 textuerLod来加载原始纹理,以保证YUV的严格匹配,shader 如下:

vec3 SampleYUYV(vec2 pos)
{ivec2 size     = textureSize(image,  0);ivec2 actualpos = ivec2(pos.x, pos.y);vec2  actualuv = (vec2(actualpos.xy)+0.5)/size;vec4 yuyv     = textureLod(image, actualuv, 0);float leftover  = fract(pos.x);float y   = (leftover<0.5f) ? yuyv.x : yuyv.z;vec3 yuv = vec3(y, yuyv.yw);return YUV_to_RGB(yuv);
}

四、 测试 Demo

   首先准备一张 1280 * 960 幅面的 YUYV 数据,然后用 GLFW 创建一个 960 * 540 (16:9)的窗口用于显示。代码中使用宏来配置是否使用 PBO模式

#define USE_PBO  true

渲染管线的 pass 如下:

 while (!glfwWindowShouldClose(winhandle)){processInput(winhandle);glViewport(0, 0, imgwidth, imgheight);yuy2fbo.BindFbo();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);uploadshader.use();UploadYUYV(yuy2pbo, imgdata, uploadtex);glBindVertexArray(uploadvao);glBindTexture(GL_TEXTURE_2D, uploadtex);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindTexture(GL_TEXTURE_2D, 0);yuy2fbo.UnBindFbo();glBindVertexArray(0);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glViewport(0, 0, wndwidth, wndheight);rendershader.use();glBindVertexArray(rendervao);yuy2fbo.BindColorTexture();glGenerateMipmap(GL_TEXTURE_2D);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);yuy2fbo.UnBindColorTexture();glfwSwapBuffers(winhandle);glfwPollEvents();}

渲染结果如下:

使用 PBO后的纹理上行效率如下:

使用 glTexSubImage2D直接上行纹理的效率如下:

可以发现效率差别还是蛮大的。

五、 总结

  ① 使用 PBO进行纹理上行的效率要由于 glTexSubImage2D,但是测试的时候发现假如把 glMapBuffer的整个时间算上的话,两者其实是相差不多的。所以实际开发中可能需要 多个 PBO来交替上行纹理。
  ②渲染管线中存在多个 Pass 需要借助 帧缓冲来实现。

OpenGL使用FBO与PBO上行纹理 (YUYV)相关推荐

  1. OPENGL VBO,FBO和PBO

    转载自:http://hi.baidu.com/bfiyubfpgibjpqq/item/0897eb6a7c7f2d0aa0cf0f22 VBO:顶点缓冲对像. VBO其实没有用到GPU运算,也就是 ...

  2. 音视频开发之旅(38) -使用FBO实现渲染到纹理(Render to texture)

    目录 FBO基本知识 FBO实现渲染到纹理的流程 实践 遇到的问题 资料 收获 在之前的学习实践中我们把图片.视频.图形等渲染到屏幕时,采用的是直接屏幕上即默认的帧缓冲区,如果我们在渲染时不想直接渲染 ...

  3. OpenGL入门(四)之纹理Texture

    本系列文章为Learn OpenGL个人学习总结! OpenGL入门(一)之认识OpenGL和创建Window OpenGL入门(二)之渲染管线pipeline,VAO.VBO和EBO OpenGL入 ...

  4. OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...

  5. OpenGL ES VAO、VBO、EBO、FBO、PBO、TBO、UBO

    面试中经常被问到的 OpenGL ES 对象,你知道的有哪些? 该原创文章首发于微信公众号:字节流动 VBO 和 EBO VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EB ...

  6. VBO、FBO、PBO 学习总结贴 (OpenGL ES)

    这是几篇好文链接: VBO: http://blog.sina.com.cn/s/blog_4062094e0100aluv.html PBO: http://blog.sina.com.cn/s/b ...

  7. Android OpenGL ES 3.0 PBO像素缓冲区对象

    1.什么是PBO OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作.PBO 仅用于执行像素传输,不连接到纹理,且与 FBO (帧缓冲区对 ...

  8. opengl es java_java – 在Android OpenGL ES App中加载纹理

    1)您应该根据需要分配尽可能多的纹理名称.一个用于您正在使用的每个纹理. 加载纹理是一项非常繁重的操作,会使渲染管道停顿.所以,你永远不应该在游戏循环中加载纹理.您应该在呈现纹理的应用程序状态之前具有 ...

  9. Qt OpenGL 旗帜效果(飘动的纹理)

    这次教程中,我将教大家如何创建一个飘动的旗帜.我们所要创建的旗帜,说白了就是一个以正弦波方式运动的纹理映射图像.虽然不会很难,但效果确实很不错,希望大家能喜欢.当然这次教程是基于第06课的,希望大家确 ...

最新文章

  1. python高效 二分法查找
  2. Linux Platform Device and Driver
  3. JavaScript中的正则
  4. 原来 JS 也支持跟 Lua 语意一样的内嵌函数的闭包概念
  5. 你知道该如何搭建 AI 智能问答系统吗?
  6. Unity 官方教程2
  7. 大数据平台需求调研大纲模板
  8. 笔记本的计算机打开没有硬盘分区,电脑开机找不到(没有)活动分区怎么办?4个解决方法!...
  9. 收藏夹整理——程序员笔试面试
  10. 抓住那头牛(宽搜bfs)
  11. Teams app 的 SSO 机制
  12. 微信直连支付通道刷脸支付用户开通步骤
  13. 综述论文要写英文摘要吗_关于小论文的一些撰写建议!
  14. Android文本输入框EditText属性和方法说明
  15. Arcpy基础入门-2、arcpy的批处理功能
  16. Limit CPUID MAX
  17. 脚本防检测防封实战教程
  18. STC12C5A60S2自主适应时钟延时函数
  19. python爬虫m3u8#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:8 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:5.0000
  20. php7序列化,PHP内核层解析反序列化漏洞

热门文章

  1. 如何关闭Windows Server 2012的IE增强安全配置
  2. 中文/英文]VC6 sp6补丁下载|VS6 sp6补丁下载
  3. 搜索引擎是如何抓取网站内容的
  4. FastDFS-6.06安装(Centos 7)
  5. mysql的zip安装教程
  6. PageIndicatorView
  7. Ubuntu fcitx输入法占用内存越来越大(超过1G)
  8. 用于跑深度学习的嵌入式硬件平台资料整理(一)
  9. String转换成json
  10. 使用docker安装kurento