#include "DisplayHandler.h"
// 加入三维顶点数据 两个三角形组成正方形
const float vers[] = {
1.0f, -1.0f, 0.0f, // 右下
-1.0f, -1.0f, 0.0f, // 左下
1.0f, 1.0f, 0.0f, // 右上
-1.0f, 1.0f, 0.0f // 左上
};
// 加入材质坐标数据
const float txts[] = {
1.0f, 1.0f, // 右下
0.0f, 1.0f, // 左下
1.0f, 0.0f, // 右上
0.0f, 0.0f // 左上
};
#define GET_STR(x) #x
// 顶点着色器 glsl
static const char *vertexShader = GET_STR(
attribute vec4 aPosition; // 顶点坐标
attribute vec2 aTexCoord; // 材质顶点坐标
varying vec2 vTexCoord; // 输出的材质坐标 输出给片元着色器
uniform mat4 uMVPMatrix; // 模型视图投影矩阵
void main() {
vTexCoord = aTexCoord;
gl_Position = uMVPMatrix * aPosition; // 应用变换矩阵
}
);
// 片元着色器 NV12
static const char *fragYUV = GET_STR(
precision mediump float; // 精度
varying vec2 vTexCoord; // 顶点着色器传递的坐标
uniform sampler2D yTexture; // 输入材质参数(不透明灰度,单像素)
uniform sampler2D uvTexture; // 输入材质参数
void main() {
vec3 yuv;
vec3 rgb;
yuv.r = texture2D(yTexture, vTexCoord).r;
yuv.g = texture2D(uvTexture, vTexCoord).r - 0.5;
yuv.b = texture2D(uvTexture, vTexCoord).a - 0.5;
// YUV to RGB conversion
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0) * yuv;
// 输出像素颜色
gl_FragColor = vec4(rgb, 1.0);
}
);
DisplayHandler::DisplayHandler() {
videoWidth = 1920;
videoHeight = 1080;
displayWidth = 0;
displayHeight = 0;
scaleX = 1.0f;
scaleY = 1.0f;
glProgram = 0;
eglSurface = nullptr;
eglContext = nullptr;
eglDisplay = nullptr;
}
bool DisplayHandler::initEGL(EGLNativeWindowType *nwin) {
//EGL
//1 eglDisplay 显示
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL_NO_DISPLAY) {
printf("get eglDisplay failed!");
return false;
}
//初始化 后面两个参数是版本号
if (EGL_TRUE != eglInitialize(eglDisplay, 0, 0)) {
printf("eglInitialize failed!");
return false;
}
//2 surface (关联原始窗口)
//surface 配置
//输出配置
EGLConfig config;
EGLint configNum;
//输入配置
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_NONE
};
if (EGL_TRUE != eglChooseConfig(eglDisplay, configSpec, &config, 1, &configNum)) {
printf("eglChooseConfig failed!");
return false;
}
//创建surface (关联原始窗口)
eglSurface = eglCreateWindowSurface(eglDisplay, config, nwin, 0);
if (eglSurface == EGL_NO_SURFACE) {
printf("eglCreateWindowSurface failed!");
return false;
}
//3 context 创建关联上下文
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
eglContext = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, ctxAttr);
if (eglContext == EGL_NO_CONTEXT) {
printf("eglCreateContext failed!");
return false;
}
//egl 关联 openGL
if (EGL_TRUE != eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
printf("eglMakeCurrent failed!");
return false;
}
printf("EGL init success");
return true;
}
void DisplayHandler::deinitEGL() {
EGLBoolean success;
if (eglDisplay != nullptr && eglSurface != nullptr) {
success = eglDestroySurface(eglDisplay, eglSurface);
if (!success) {
printf("eglDestroySurface failure.");
}
eglSurface = nullptr;
}
if (eglDisplay != nullptr && eglContext != nullptr) {
success = eglDestroyContext(eglDisplay, eglContext);
if (!success) {
printf("eglDestroyContext failure.");
}
eglContext = nullptr;
success = eglTerminate(eglDisplay);
if (!success) {
printf("eglTerminate failure.");
}
eglDisplay = nullptr;
}
if (glProgram != 0) {
glDeleteProgram(glProgram);
glProgram = 0;
}
}
//初始化着色器
GLint DisplayHandler::initShader(const char *code, GLint type) {
GLuint shader;
GLint compiled;
// Create an empty shader object, which maintain the source code strings that define a shader
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
// Replaces the source code in a shader object
glShaderSource(shader, 1, &code, nullptr);
// Compile the shader object
glCompileShader(shader);
// Check the shader object compile status
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
GLchar *infoLog = (GLchar *) malloc(sizeof(GLchar) * infoLen);
glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
printf("Error compiling shader:\n%s\n", infoLog);
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
GLuint DisplayHandler::loadProgram(const char *vShaderStr, const char *fShaderStr) {
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
// Load the vertex/fragment shaders
vertexShader = initShader(vShaderStr, GL_VERTEX_SHADER);
fragmentShader = initShader(fShaderStr, GL_FRAGMENT_SHADER);
// Create the program object
programObject = glCreateProgram();
if (programObject == 0) {
return 0;
}
// Attaches a shader object to a program object
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
// Bind vPosition to attribute 0
glBindAttribLocation(programObject, 0, "vPosition");
// Link the program object
glLinkProgram(programObject);
// Check the link status
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
GLchar *infoLog = (GLchar *) malloc(sizeof(GLchar) * infoLen);
glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
printf("Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(programObject);
return GL_FALSE;
}
// Free no longer needed shader resources
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return programObject;
}
GLint DisplayHandler::createProgram() {
GLuint programObject;
// Load the shaders and get a linked program object
programObject = loadProgram((const char *)vertexShader,
(const char *)fragYUV);
if (programObject == 0) {
return GL_FALSE;
}
// Store the program object
glProgram = programObject;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glGenTextures(TEXTURE_NUM, mTextureID);
for (int i = 0; i < TEXTURE_NUM; i++) {
glBindTexture(GL_TEXTURE_2D, mTextureID[i]);
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);
}
// 激活渲染程序
glUseProgram(glProgram);
// 获取shader中的变量位置
aposLoc = glGetAttribLocation(glProgram, "aPosition");
atexLoc = glGetAttribLocation(glProgram, "aTexCoord");
mvpLoc = glGetUniformLocation(glProgram, "uMVPMatrix");
// 设置纹理层
glUniform1i(glGetUniformLocation(glProgram, "yTexture"), 0); // 纹理第1层
glUniform1i(glGetUniformLocation(glProgram, "uvTexture"), 1); // 纹理第2层
return 0;
}
void DisplayHandler::setVideoWH(int width, int height) {
videoWidth = width;
videoHeight = height;
}
void DisplayHandler::setDisplayWH(int width, int height) {
displayWidth = width;
displayHeight = height;
updateMVPMatrix();
}
void DisplayHandler::setScale(float xScale, float yScale) {
scaleX = xScale;
scaleY = yScale;
updateMVPMatrix();
}
void DisplayHandler::updateMVPMatrix() {
if (displayWidth <= 0 || displayHeight <= 0) {
return;
}
// 计算视频的宽高比
float videoAspect = (float) videoWidth / (float) videoHeight;
float displayAspect = (float) displayWidth / (float) displayHeight;
// 计算缩放比例
float scale = 1.0f;
float offsetX = 0.0f;
float offsetY = 0.0f;
if (videoAspect > displayAspect) {
// 视频比显示区域更宽
scale = (float) displayWidth / (float) videoWidth;
offsetY = ((float) displayHeight - (float) videoHeight * scale) / 2.0f;
} else {
// 视频比显示区域更高
scale = (float) displayHeight / (float) videoHeight;
offsetX = ((float) displayWidth - (float) videoWidth * scale) / 2.0f;
}
// 应用额外的缩放
scale *= scaleX;
// 创建正交投影矩阵
glm::mat4 projection = glm::ortho(
-displayWidth / 2.0f, displayWidth / 2.0f,
-displayHeight / 2.0f, displayHeight / 2.0f,
-1.0f, 1.0f);
// 创建模型视图矩阵
glm::mat4 model = glm::mat4(1.0f);
// 先缩放
model = glm::scale(model, glm::vec3(scale * videoWidth / 2.0f, scale * videoHeight / 2.0f, 1.0f));
// 再应用偏移
model = glm::translate(model, glm::vec3(
(offsetX + (displayWidth - videoWidth * scale) / 2.0f) / (scale * videoWidth / 2.0f),
(offsetY + (displayHeight - videoHeight * scale) / 2.0f) / (scale * videoHeight / 2.0f),
0.0f));
// 计算MVP矩阵
glm::mat4 mvp = projection * model;
// 传递给着色器
glUseProgram(glProgram);
glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, glm::value_ptr(mvp));
}
int DisplayHandler::getVideoWidth() const {
return videoWidth;
}
int DisplayHandler::getVideoHeight() const {
return videoHeight;
}
void DisplayHandler::update(unsigned char *yuvBuf) {
if (displayWidth <= 0 || displayHeight <= 0) {
return;
}
// 设置视口
glViewport(0, 0, displayWidth, displayHeight);
// 清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
// 使用程序
glUseProgram(glProgram);
// 启用顶点属性数组
glEnableVertexAttribArray(aposLoc);
glVertexAttribPointer(aposLoc, 3, GL_FLOAT, GL_FALSE, 0, vers);
glEnableVertexAttribArray(atexLoc);
glVertexAttribPointer(atexLoc, 2, GL_FLOAT, GL_FALSE, 0, txts);
// 更新Y纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID[0]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
videoWidth, videoHeight,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
yuvBuf);
// 更新UV纹理 (NV12格式中UV分量是交错存储的)
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mTextureID[1]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE_ALPHA,
videoWidth / 2, videoHeight / 2,
0,
GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE,
yuvBuf + (videoWidth * videoHeight));
// 绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 交换缓冲区
eglSwapBuffers(eglDisplay, eglSurface);
} rk3588 使用opengl 实现NV12 数据缩放