您的位置:首页 > 编程语言 > Qt开发

利用Qt + OpenGL 渲染 YUV数据,播放视频 mac版

2017-03-03 13:32 816 查看
最近利用Qt渲染YUV数据,折腾了很久,最开始使用FFmpeg将YUV数据转换成RGB数据后在用qt绘制,很快得到了成功,但是cpu占用率太大,最后放弃了。

在这先感谢来自文章http://blog.csdn.net/su_vast/article/details/52214642的作者,使用他的代码我实现了Windows上的YUV数据渲染。但是移植到mac上渲染不出来,经过折腾及对OpenGL的了解,发现问题出在OpenGL版本问题上,不同版本OpenGL的shader编写使用有些不同,资料来源于http://blog.csdn.net/ym19860303/article/details/44115135。

下面是我写的代码,有什么不完善的地方请读者反馈,相互学习。

renderview.h

#ifndef RENDERVIEW_H
#define RENDERVIEW_H

#include <QWidget>
#include <QOpenGLWidget>
#include <QGLContext>
#include <QGLWidget>
#include <QOpenGLTexture>
#include "global.h"

class RenderView : public QOpenGLWidget
{
Q_OBJECT

public:
RenderView(QWidget* parent = 0);
~RenderView();

void Render(unsigned char* buffer,int w, int h);
protected:
//virtual void paintEvent(QPaintEvent *event);
virtual void initializeGL();
virtual void resizeGL( int w, int h );
virtual void paintGL();

private:
GLuint prepareShaderProgram();

private:
GLuint m_shader;
GLuint m_vertexBuffer;
GLuint textureUniformY;
GLuint textureUniformU;
GLuint textureUniformV;
GLuint vertexBuffer;
GLuint vertextAttribute;
GLuint textureAttribute;

GLuint id_y;
GLuint id_u;
GLuint id_v;

int m_nVideoW;
int m_nVideoH;
int m_nViewW;
int m_nViewH;
unsigned char* m_pBufYuv420p;
unsigned char* m_pBuffer;
bool m_bStartPlay;
};

#endif // RENDERVIEW_H

renderview.cpp

#include "renderview.h"
#include <QPainter>
#include <QSurfaceFormat>
#include <QMutex>
#include <QDebug>
#include <QSurfaceFormat>
#include "DecoderCtrl/MainConcept.h"

#define BUFFER_MAX_SIZE 1920*1080+1024

const char *vsrc =
"#version 410\n"
"in vec4 vertexIn;\n"
"in vec2 textureIn;\n"
"out vec2 textureOut;\n"
"void main(void)\n"
"{\n"
" gl_Position = vertexIn;\n"
" textureOut = textureIn;\n"
"}";

const char *fsrc =
"#version 410\n"
"in vec2 textureOut;\n"
"out vec4 fragColor;\n"
"uniform sampler2D tex_y;\n"
"uniform sampler2D tex_u;\n"
"uniform sampler2D tex_v;\n"
"void main(void)\n"
"{\n"
" vec3 yuv;\n"
" vec3 rgb;\n"
" yuv.x = texture(tex_y, textureOut).r;\n"
" yuv.y = texture(tex_u, textureOut).r - 0.5;\n"
" yuv.z = texture(tex_v, textureOut).r - 0.5;\n"
" rgb = mat3( 1, 1, 1,\n"
" 0, -0.21482, 2.12798,\n"
" 1.28033, -0.38059, 0) * yuv;\n"
" fragColor = vec4(rgb, 1);\n"
"}";

void RenderYuv(unsigned char* yuvBuffer,int width, int height,void *pdate)
{
RenderView *render = (RenderView*)pdate;
if(render)
{
render->Render(yuvBuffer, width, height);
}
}

RenderView::RenderView(QWidget* parent) :
QOpenGLWidget(parent)
,m_pBufYuv420p(NULL)
,m_nVideoW(0)
,m_nVideoH(0)
,m_nViewW(0)
,m_nViewH(0)
,m_bStartPlay(false)
{
setAutoFillBackground(false);
MC_Codec_SetRenderNotify(RenderYuv, this);
m_pBufYuv420p = new unsigned char[BUFFER_MAX_SIZE];
memset(m_pBufYuv420p, 0, BUFFER_MAX_SIZE);
}

RenderView::~RenderView()
{

}

void RenderView::Render(unsigned char* buffer,int w, int h)
{
m_nVideoW = w;
m_nVideoH = h;
m_bStartPlay = true;
unsigned long bufLen = w*h*3/2;
m_pBufYuv420p = buffer;
memcpy(m_pBufYuv420p, buffer, bufLen);
update();
}

GLuint RenderView::prepareShaderProgram()
{
GLuint program = glCreateProgram();

//vertex shader
GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER);
glShaderSource( vertexShader, 1, (const GLchar**)&vsrc, NULL );
glCompileShader( vertexShader );
GLint compiled;
glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
GLint logSize;
glGetShaderiv( vertexShader, GL_INFO_LOG_LENGTH, &logSize );
char* logMsg = new char[logSize];
glGetShaderInfoLog( vertexShader, logSize, NULL, logMsg );
qWarning() << logMsg;
delete [] logMsg;
exit( EXIT_FAILURE );
}
glAttachShader( program, vertexShader );

//fragment shader
GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER);
glShaderSource( fragmentShader, 1, (const GLchar**)&fsrc, NULL );
glCompileShader( fragmentShader );
glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
GLint logSize;
glGetShaderiv( vertexShader, GL_INFO_LOG_LENGTH, &logSize );
char* logMsg = new char[logSize];
glGetShaderInfoLog( fragmentShader, logSize, NULL, logMsg );
qWarning() << logMsg;
delete [] logMsg;
exit( EXIT_FAILURE );
}
glAttachShader( program, fragmentShader );

/* Link output */
glBindFragDataLocation(program, 0, "fragColor");

/* link and error check */
glLinkProgram(program);

GLint linked;
glGetProgramiv( program, GL_LINK_STATUS, &linked );
if ( !linked ) {
qWarning() << "Shader program failed to link";
GLint logSize;
glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logSize);
char* logMsg = new char[logSize];
glGetProgramInfoLog( program, logSize, NULL, logMsg );
qWarning() << logMsg ;
delete [] logMsg;

exit( EXIT_FAILURE );
}

/* use program object */
glUseProgram(program);

return program;
}

void RenderView::initializeGL()
{
//glSetup();

// Prepare a complete shader program...
m_shader = prepareShaderProgram();

textureUniformY = glGetUniformLocation(m_shader, "tex_y");
textureUniformU = glGetUniformLocation(m_shader, "tex_u");
textureUniformV = glGetUniformLocation(m_shader, "tex_v");

// Create a interleaved triangle (vec3 position, vec3 color)
float vertexPoints[] ={
-1.0f, -1.0f,0.0f, 1.0f,
1.0f, -1.0f,1.0f, 1.0f,
-1.0f, 1.0f,0.0f, 0.0f,
1.0f, 1.0f,1.0f, 0.0f,
};

glGenVertexArrays(1, &m_vertexBuffer);
glBindVertexArray(m_vertexBuffer);

glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), vertexPoints, GL_STATIC_DRAW);
vertextAttribute = glGetAttribLocation(m_shader, "vertexIn");
textureAttribute = glGetAttribLocation(m_shader, "textureIn");
glEnableVertexAttribArray(vertextAttribute);
glVertexAttribPointer(vertextAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (const GLvoid *)0);
glEnableVertexAttribArray(textureAttribute);
glVertexAttribPointer(textureAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (const GLvoid *)(sizeof(float)*2));

//Init Texture
glGenTextures(1, &id_y);
glBindTexture(GL_TEXTURE_2D, id_y);
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);

glGenTextures(1, &id_u);
glBindTexture(GL_TEXTURE_2D, id_u);
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);

glGenTextures(1, &id_v);
glBindTexture(GL_TEXTURE_2D, id_v);
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);
//glCheckError();
}

void RenderView::resizeGL( int w, int h )
{
// Set the viewport to window dimensions
m_nViewW = w;
m_nViewH = h;
glViewport( 0, 0, w, qMax( h, 1 ) );
//glCheckError();
}

void RenderView::paintGL()
{
// Clear the buffer with the current clearing color
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

if(m_bStartPlay)
{
float x,y;
float wRatio = (float)m_nViewW/m_nVideoW;
float hRatio = (float)m_nViewH/m_nVideoH;
float minRatio = qMin(wRatio, hRatio);
y = m_nVideoH * minRatio/m_nViewH;
x = m_nVideoW * minRatio/m_nViewW;

float vertexPoints[] ={
-x, -y,0.0f, 1.0f,
x, -y,1.0f, 1.0f,
-x, y,0.0f, 0.0f,
x, y,1.0f, 0.0f,
};
glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), vertexPoints, GL_STATIC_DRAW);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, id_y);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW, m_nVideoH, 0, GL_RED, GL_UNSIGNED_BYTE, m_pBufYuv420p);
glUniform1i(textureUniformY, 0);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, id_u);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW / 2, m_nVideoH / 2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p + m_nVideoW*m_nVideoH);
glUniform1i(textureUniformU, 1);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, id_v);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW / 2, m_nVideoH / 2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p + m_nVideoW*m_nVideoH * 5 / 4);
glUniform1i(textureUniformV, 2);
// Draw stuff
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
}
//∫glCheckError();
}

在界面显示前记得添加下面代码

QSurfaceFormat format;
format.setVersion(4,1);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: