上篇博客基于live555的rtsp播放器:使用SDL2\D3D9渲染yuv420p中指出在mac上使用SDL2会有问题,因此在mac上可以使用QOpenGLWidget渲染yuv420p。
一.GLSL Versions
You can use the #version
command as the first line of your shader to specify GLSL version:
#version 120
void main() {
gl_FragColor = vec4(1.0);
}
GLSL versions are released alongside GL versions. See the following charts to decide which version you would like to target.
OpenGL Version | GLSL Version |
2.0 | 110 |
2.1 | 120 |
3.0 | 130 |
3.1 | 140 |
3.2 | 150 |
3.3 | 330 |
4.0 | 400 |
4.1 | 410 |
4.2 | 420 |
4.3 | 430 |
GLSL ES Versions (Android, iOS, WebGL)
OpenGL ES has its own Shading Language, and the versioning starts fresh. It is based on OpenGL Shading Language version 1.10.
OpenGL ES Version | GLSL ES Version |
2.0 | 100 |
3.0 | 300 |
So, for example, if a feature is available in GLSL 120, it probably won't be available in GLSL ES 100 unless the ES compiler specifically allows it.
Differences at a Glance
Differences between (desktop) GLSL versions.
Version 100
Vertex shader:
uniform mat4 projTrans;
attribute vec2 Position;
attribute vec2 TexCoord;
varying vec2 vTexCoord;
void main() {
vTexCoord = TexCoord;
gl_Position = u_projView * vec4(Position, 0.0, 1.0);
}
Fragment shader:
uniform sampler2D tex0;
varying vec2 vTexCoord;
void main() {
vec4 color = texture2D(tex0, vTexCoord);
gl_FragColor = color;
}
Version 330
As of GLSL 130+, in
and out
are used instead of attribute
and varying
. GLSL 330+ includes other features like layout qualifiers and changes texture2D
to texture
.
Vertex shader:
#version 330
uniform mat4 projTrans;
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 TexCoord;
out vec2 vTexCoord;
void main() {
vTexCoord = TexCoord;
gl_Position = u_projView * vec4(Position, 0, 1);
}
Fragment shader:
#version 330
uniform sampler2D tex0;
in vec2 vTexCoord;
//use your own output instead of gl_FragColor
out vec4 fragColor;
void main() {
//'texture' instead of 'texture2D'
fragColor = texture(tex0, vTexCoord);
}
Other Significant Changes
GLSL 120 Additions
- You can initialize arrays within a shader, like so:
float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1);
float b[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1);
However, the above is not supported on Mac OSX Snow Leopard, even with GLSL 120. (1)
- You can initialize uniforms in a shader, and the value will be set at link time:
uniform float val = 1.0;
- You can use built-ins like
sin()
when setting aconst
value - Integers are implicitly converted to floats when necessary, for example:
float f = 1.0; <-- valid
float g = 1; <-- only supported in GLSL 120
vec2 v = vec2(1, 2.0); <-- only supported in GLSL 120
- You can use
f
to define a float:float f = 2.5f;
GLSL 130 Additions
int
anduint
support (and bitwise operations with them)switch
statement support- New built-ins:
trunc()
,round()
,roundEven()
,isnan()
,isinf()
,modf()
- Fragment output can be user-defined
- Input and output is declared with
in
andout
syntax instead ofattribute
andvarying
GLSL 150 Additions
texture()
should now be used instead oftexture2D()
GLSL 330 Additions
- Layout qualifiers can declare the location of vertex shader inputs and fragment shader outputs, eg:
layout(location = 2) in vec3 values[4];
Formally this was only possible with ARB_explicit_attrib_location
extension
参考链接:https://github.com/mattdesl/lwjgl-basics/wiki/GLSL-Versions
二.OpenGL渲染视频
#include "QHOpenGLPlayer.h"
QHOpenGLThread::QHOpenGLThread(QObject *parent)
:QThread (parent)
{
m_fileName="Wildlife.yuv";
}
QHOpenGLThread::~QHOpenGLThread()
{
}
void QHOpenGLThread::run()
{
FILE *pFile = fopen(m_fileName.toStdString().c_str(), "rb");
if (pFile == nullptr)
{
qDebug()<<"fopen failed";
}
// Y+U+V
int bufferSize = VIDEO_WIDTH*VIDEO_HEIGHT+VIDEO_WIDTH*VIDEO_HEIGHT/4+VIDEO_WIDTH*VIDEO_HEIGHT/4;
uint8_t *buffer = (uint8_t *) malloc(bufferSize);
while (true)
{
if (fread(buffer, 1, bufferSize, pFile) != bufferSize)
{
qDebug()<<"end of file";
break;
}
emit signalNewFrame(buffer,VIDEO_WIDTH,VIDEO_HEIGHT);
msleep(1000/29.97);
}
}
QHOpenGLPlayer::QHOpenGLPlayer(QWidget *parent)
: QOpenGLWidget(parent)
, m_buffer(nullptr)
, m_width(0)
, m_height(0)
{
m_thread=new QHOpenGLThread();
connect(m_thread,&QHOpenGLThread::signalNewFrame,this,&QHOpenGLPlayer::slotNewFrame,Qt::QueuedConnection);
m_thread->start();
}
QHOpenGLPlayer::~QHOpenGLPlayer()
{
}
void QHOpenGLPlayer::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0, 0.0, 0.0, 1.0);
const char *vSrc =
"#version 330 \n"
"layout (location = 0) in vec4 verPosIn; \n"
"layout (location = 1) in vec2 texPosIn; \n"
"out vec2 texPosOut; \n"
"void main(void) \n"
"{ \n"
" gl_Position = verPosIn; \n"
" texPosOut = texPosIn; \n"
"}";
const char *fSrc =
"#version 330 \n"
"out vec4 fragColor; \n"
"in vec2 texPosOut; \n"
"uniform sampler2D texY; \n"
"uniform sampler2D texU; \n"
"uniform sampler2D texV; \n"
"void main(void) \n"
"{ \n"
" vec3 yuv; \n"
" vec3 rgb; \n"
" yuv.x = texture(texY, texPosOut).r; \n"
" yuv.y = texture(texU, texPosOut).r-0.5; \n"
" yuv.z = texture(texV, texPosOut).r-0.5; \n"
" rgb = mat3(1.0, 1.0, 1.0, \n"
" 0.0, -0.3455, 1.779, \n"
" 1.4075, -0.7169, 0.0) * yuv; \n"
" fragColor = vec4(rgb, 1.0); \n"
"}";
m_shaderProgram.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex,vSrc);
m_shaderProgram.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment,fSrc);
m_shaderProgram.link();
m_vbo.create();
GLuint ids[3];
glGenTextures(3,ids);
m_idY = ids[0];
m_idU = ids[1];
m_idV = ids[2];
}
void QHOpenGLPlayer::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
void QHOpenGLPlayer::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
float x=0.0,y=0.0;
float wRatio = (float)width()/m_width;
float hRatio = (float)height()/m_height;
float minRatio = qMin(wRatio, hRatio);
x = m_width*minRatio/width();
y = m_height*minRatio/height();
GLfloat vertices[]{
-1.0f*x, -1.0f*y,
1.0f*x, -1.0f*y,
-1.0f*x, 1.0f*y,
1.0f*x, 1.0f*y,
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
m_shaderProgram.bind();
m_vbo.bind();
m_vbo.allocate(vertices,sizeof(vertices));
m_shaderProgram.enableAttributeArray(0);
m_shaderProgram.enableAttributeArray(1);
m_shaderProgram.setAttributeBuffer(0,GL_FLOAT,0,2,2*sizeof(GLfloat));
m_shaderProgram.setAttributeBuffer(1,GL_FLOAT,2*4*sizeof(GLfloat),2,2*sizeof(GLfloat));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,m_idY);
glTexImage2D(GL_TEXTURE_2D,0,GL_RED,m_width,m_height,0,GL_RED,GL_UNSIGNED_BYTE,m_buffer);
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);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D,m_idU);
glTexImage2D(GL_TEXTURE_2D,0,GL_RED,m_width/2,m_height/2,0,GL_RED,GL_UNSIGNED_BYTE,m_buffer+m_width*m_height);
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);
glActiveTexture(GL_TEXTURE0+2);
glBindTexture(GL_TEXTURE_2D,m_idV);
glTexImage2D(GL_TEXTURE_2D,0,GL_RED,m_width/2,m_height/2,0,GL_RED,GL_UNSIGNED_BYTE,m_buffer+m_width*m_height*5/4);
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);
m_shaderProgram.setUniformValue("texY",0);
m_shaderProgram.setUniformValue("texU",1);
m_shaderProgram.setUniformValue("texV",2);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
m_shaderProgram.disableAttributeArray(0);
m_shaderProgram.disableAttributeArray(1);
m_vbo.release();
m_shaderProgram.release();
}
void QHOpenGLPlayer::slotNewFrame(uchar *buffer,int width,int height)
{
m_buffer = buffer;
m_width = width;
m_height = height;
update();
}
代码参考了博客:https://blog.csdn.net/leixiaohua1020/article/details/40379845
关于着色器部分略作修改,使用的GLSL版本是330,因为该版本之前的写法在windows上虽然可以正常显示,但是在linux和mac上却没显示任何内容。
从代码可以看出,如果视频帧的宽和高分别为w和h,那么一帧YUV420P像素数据一共占用w*h*3/2 Byte的数据。其中前w*h Byte存储Y,接着的w*h*1/4 Byte存储U,最后w*h*1/4 Byte存储V。
至于着色器中in、out、location等关键字的意义以及流程中各个函数的用法,可以参考我之前翻译的几篇文章。
Qt OpenGL:学习现代3D图形编程之一,画一个三角形
Qt OpenGL:学习现代3D图形编程之二,玩转色彩
Qt OpenGL:学习现代3D图形编程之三,移动三角形
三.效果
源码链接:https://download.csdn.net/download/caoshangpa/14105525
原创不易,转载请标明出处:https://blog.csdn.net/caoshangpa/article/details/112448207