MediaCodec解码P010,OpenGLSL.texelFetch读取非归一化纹素。
废话不说,直接上干货,懂的都懂,不懂的慢慢消化。
一、Android.MediaCodec解码P010
1、正如之前的文章所说的,通过API判断是否支持HEVCProfileMain10HDR10,其实这些都不是硬性条件。想利用MediaCodec解码出P010格式的10bit数据,主要还是取决于手机DSP芯片是否支持。通过测试发现目前市面流行的芯片厂商,目前发现高通的SDM系列(即新代的晓龙)支持解码出P010格式的10bit(源码依据: https://android.googlesource.com/platform/hardware/qcom/sdm845/display/+/android-10.0.0_r40/gralloc/gralloc_priv.h)
2、在创建MeidaCodec之前配置的MediaFormat,需要指定颜色格式KEY_COLOR_FORMAT为0x7F420888 即 CodecCapabilities.COLOR_FormatYUV420Flexible,虽然这个格式定义很模糊,但好歹也跟系统说明了输出格式是YUV类型,要不然解码输出队列会抛异常提示,解不出数据。(查询官网资料,在Android 13,API33-Tiramisu,现在(2022-4-10)还是Preview预览版,在MediaCodecInfo.CodecCapabilities增加COLOR_FormatYUVP010类型的变量声明。)
3、以示例编码数据planet_sdr_1920x1080@30fps.h265为例子,常规解码得到的数据长度为
1920 * 1088(16对齐) * 3 / 2 = 3133440(byte)
如果输入的是10bit的planet_hdr_1920x1080@30fps.h265,得到的一帧解码数据长度是
1920 * 1088(16对齐) * 3 / 2 * 2 = 6266880(byte)
符合之前的文章的理论分析预期。此时可以证明当前解码的数据就是10bit的YUV raw data。
并且可以发现解码输出队列dequeueOutputBuffer的MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED,会输出格式变化
FORMAT change 2135033992 > 2141391882
即从0x7F420888 改变为 0x7FA30C0A
上面hardware/qcom/sdm845的源码连接就有其定义:
// 10 bit
#define HAL_PIXEL_FORMAT_ARGB_2101010 0x117
#define HAL_PIXEL_FORMAT_RGBX_1010102 0x118
#define HAL_PIXEL_FORMAT_XRGB_2101010 0x119
#define HAL_PIXEL_FORMAT_BGRA_1010102 0x11A
#define HAL_PIXEL_FORMAT_ABGR_2101010 0x11B
#define HAL_PIXEL_FORMAT_BGRX_1010102 0x11C
#define HAL_PIXEL_FORMAT_XBGR_2101010 0x11D
#define HAL_PIXEL_FORMAT_YCbCr_420_P010 0x11F
#define HAL_PIXEL_FORMAT_YCbCr_420_P010_UBWC 0x124
#define HAL_PIXEL_FORMAT_YCbCr_420_P010_VENUS 0x7FA30C0A
测试文件可以到这领取
链接:https://pan.baidu.com/s/1_nmoK2lhHc3wzZx0KYD7sg
提取码:lfeu
二、texelFetch读取非归一化纹素
在GLSL环境要想精确地换取每个像素的值,这个时候就不能使用传统的 texture(sampler2d) ,因为采样器sampler2d默认是float精度,而且texture采用函数会涉及归一化、过滤以及插值等复杂操作,基本无法得到某一确切像素的值。
此时很多网上教程就会教你使用纹素获取函数 texelFetch ,texlFetch 是 OpenGL ES 3.0 引入的 API ,它将纹理视为图像,可以精确访问像素的内容,我们可以类比通过索引来获取数组某个元素的值。texelFetch 使用的是未归一化的坐标直接访问纹理中的纹素,不执行任何形式的过滤和插值操作,坐标范围为实际载入纹理图像的宽和高。
texelFetch 使用起来比较方便,在片段着色器中,下面 2 行代码可以互换
gl_FragColor = texture(s_Texture, v_texCoord);
gl_FragColor = texelFetch(s_Texture, ivec2(int(v_texCoord.x * imgWidth), int(v_texCoord.y * imgHeight)), 0);
基础教学我就不再这累述了,那么利用 texelFetch提取理想的纹素 需要注意些什么呢?
1、升级EGLVersion到3.0以上,EGLConfig支持10bit的rgb位深
setEGLContextClientVersion(GLVersionUtils.isGLES30Supported() ? 3 : 2);
setEGLConfigChooser(new ConfigChooser(/*RedBitSize*/10, /*Green*/10, /*Blue*/10, /*Alpha*/2, /*Depth*/0, /*Stencil*/0));
2、创建的纹理对象一定要注意、注意、注意两个地方:
glGenTextures(1, textureHandles);
glBindTexture(target, mTextureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(target, 0, internalFormat, width, height, 0, format, dataType, null);
第一点:要想利用texelFetch提取纹素,这里的 GL_TEXTURE_MAG_FILTER 和 GL_TEXTURE_MIN_FILTER不能使用线性过滤的方式,即GL_LINEAR or GL_LINEAR_***的参数。(其实上面的基础介绍已经加粗提示)
第二点:一定要注意!注意!注意! internalFormat、format、dataType这三个参数的组合,详情请仔细阅读官方文档 https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml (用google翻译一行行的慢慢看)
结合我们现在实际的10bit双字节数据,这三个参数要该如何填写?
我们可以利用GL_R16UI、GL_RED_INTEGER、GL_UNSIGNED_SHORT(对应无符号双字节short类型的数据)或者GL_R16I、GL_RED_INTEGER、GL_SHORT(对应有符号双字节short类型的数据)
其中GL_RED 和 GL_RED_INTEGER是单通道的格式,GL_RG和GL_RG_INTEGER就是双通道的格式,我们可以利用单通道的格式存储Y分量,利用双通道的格式存储UV分量。至于带和不带INTEGER的格式区别是什么,其实官方文档已经给出解释:
GL_RED
Each element is a single red component. For fixed point normalized components, the GL converts it to floating point, clamps to the range [0,1], and assembles it into an RGBA element by attaching 0.0 for green and blue, and 1.0 for alpha.
每个元素都是一个红色分量。对于规定的归一化化组件,GL将其转换为浮点,限制为[0,1]范围,并通过附加0.0(绿色和蓝色)和1.0(alpha)将其组装为RGBA元素。GL_RED_INTEGER
Each element is a single red component. The GL performs assembles it into an RGBA element by attaching 0 for green and blue, and 1 for alpha.
每个元素都是一个红色分量。GL通过为绿色和蓝色附加0,为alpha附加1,将其组装为RGBA元素。
在从MeidaCodec解码出来的 6266880 长的ByteBuffer,可以直接利用asShortBuffer方法转换成short类型的数据,免得自己处理合并过程还需要注意大端小端问题,还浪费性能。大致的代码如下所示:
updateTextureData(ByteBuffer.asShortBuffer(), width, height);public void updateTextureData(Buffer data, int width, int height) {if (data == null || width != mWidth || height != mHeight) {return;}if ((width & Constant.AlignmentMask.ALIGN_4) != 0) {GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);} else {GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 4);}glActiveTexture(mTextureUnit);glBindTexture(GL_TEXTURE_2D, mTextureId);glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, mWidth, mHeight, 0,GL_RED_INTEGER, GL_UNSIGNED_SHORT, data);
}
3、到最后的GLSL环境,shader大致如下
#version 320 es
precision highp float;
uniform highp usampler2D tex_u_y;
#uniform highp isampler2D tex_i_y;
uniform lowp float imgWidth;
uniform lowp float imgHeight;
in vec2 vTextureCoord;
out vec4 _FragColor;void main() {float xPos = vTextureCoord.x * imgWidth;float yPos = vTextureCoord.y * imgHeight;//注意要显式声名精度highp uint ur = texelFetch(tex_u_y, ivec2(int(xPos), int(yPos)), 0).x;_FragColor = vec4(float(ur)/65536.0, 0.0, 0.0, 1.0);
}
利用texelFetch读取文素,需要确保着色器中用于纹理采样的类型与存储在纹理中的数据类型匹配。在本例中,由于使用的2D纹理包含无符号数值,因此采样器类型应为usampler2D,并且希望将采样操作的结果存储在类型为uvec4的变量中,每个个通道都是uint的类型;如果是有符号的数值,那么采样器对应的isampler2d,结果返回类型为ivec4的变量。
并且一定要注意显式的声名精度范围!要不然默认的int or uint精度只有1位。
下一章讨论BT.2020的10bit yuv -> rgb。
MediaCodec解码P010,OpenGLSL.texelFetch读取非归一化纹素。相关推荐
- MediaCodeC解码视频指定帧,迅捷、精确
原创文章,转载请联系作者 若待明朝风雨过,人在天涯!春在天涯 原文地址 提要 最近在整理硬编码MediaCodec相关的学习笔记,以及代码文档,分享出来以供参考.本人水平有限,项目难免有思虑不当之处, ...
- 【转】为什么不能使用字符流读取非文本的二进制文件?
读取文件 刚学Java的IO流部分时,书上说只能使用字节流去读取图片.视频等非文本二进制文件,不能使用字符流,否则文件会损坏.所以我就一直记住这一点了,但是为什么不能使用,这一直是我的一个疑惑.今天, ...
- MediaCodec 解码后数据对齐导致的绿边问题
前言 本文从简书迁移,原文地址:www.jianshu.com/p/ac53e9595- Android 使用 MediaCodec 解码 h264 数据后会有个数据对齐的问题. 简单说就是 Medi ...
- php解析m3u8代码,PHP解码转发M3U8 PHP读取转发M3U8的方法
这篇文章主要为大家详细介绍了PHP解码转发M3U8 PHP读取转发M3U8的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴. PHP的CURL方法模拟访客获取 ...
- 读取ANSYS结果文件中的数据C语言,Ansys后处理读取非默认的结果文件数据 | 坐倚北风...
在进行后处理时,Ansys默认读取当前目录下的默认求解结果文件.Ansys的求解结果文件有以下几种: (1).RST – 结构分析或耦合场分析结果: (2).RTH – 热分析和扩散分析结果: (3) ...
- Android Mediacodec解码视频(通过Surface播放、得到Image数据)
MediaCodec解码得到Image 对于仅仅需要将视频切分为一帧一帧并保存为图片的用户来说,使用这种方法比bigflake的方法会快10倍左右,因为没有OpenGL渲染,以及转换为Bitmap的开 ...
- 二叉树的深度(前序 中序 后序 递归非递归搜素)、广度、搜索 C++
a b c 使用 1 2 3 表示 /* 描述:二叉树的深度(前序 中序 后序 递归非递归搜素).广度.搜索 作者:jz 日期:20140819 */ #include<stdio.h> ...
- Android MediaCodec 解码H264码流播放
视频编解码,编的是什么码?解的又是什么码?有没有想过?现在主流的就是H264码流,Android 采集摄像头原始帧数据 这篇博客讲解的是如何从摄像头从提取YUV画面色值,然后由MediaCodec进行 ...
- MediaCodec解码h264流
上一篇博文介绍了如何用MediaCodec进行h264编码,这篇介绍如何用MediaCodec进行h264解码,解码时要注意的点就是要一帧一帧的喂给解码器,编码器是一帧一帧出数据的,网上有些demo居 ...
最新文章
- pyEcharts安装及详细使用指南
- pandas groupby
- 摸清源头 让电脑运行不再迟缓
- C# WinForm开发系列 - DataGrid
- OSError: mysql_config not found
- 初中计算机教师资格考试试题,2017下半年初中信息技术教师资格证面试试题(精选)第一批(2)...
- 利用pdf.js开发嵌入pdf显示,以及利用jquery-ui左右分栏显示
- java 责任链模式 链表_责任链模式的实现及源码中应用
- vue-router组件重用 路由切换时的问题
- Android入门:封装一个HTTP请求的辅助类
- Nginx只允许域名访问网站,禁止使用IP 访问80,443端口
- kafka可视化界面kibana_kafka日志同步至elasticsearch和kibana展示
- 微信emoji表情编码 、MySQL 存储 emoji 表情符号字符集
- word修改表格和下方段落的间距
- 蓝桥杯C/C++B组历届真题刷题【合集】
- # 书籍《银河帝国3:第二基地》读后感-20211018
- python元组使用什么界定符_CookBook/2-Python3基本语法.md at master · Byron4j/CookBook · GitHub...
- 【DL】第11 章:文本深度学习
- 实战开发:新手小白如何用C++制作经典网游DNF
- 手机浏览器简单搜索ua标识