九、RGBA数据转YUV422存储

news2024/11/25 12:48:17

1、介绍

将RGBA转换为YUV数据,首先我们是知道有公式是可以将RGBA转换为YUV的,但是图像的每个像素都有一个R、G、B,A值的,但是YUV422(就是两个像素两个Y一个U一个V的),因此我们还需要将一个像素的RGBA四个值转换为YUV三个值之后还需要将下一个像素点的Y值也要计算出来,则需要偏移采样将下个像素的RGB值也提出了来计算下个像素的Y值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDLdMaTS-1686050151948)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\yuv.png)]

如图所示,我们在 shader 中执行两次采样,RGBA 像素(R0,G0,B0,A0)转换为(Y0,U0,V0),像素(R1,G1,B1,A1)转换为(Y1),然后组合成(Y0,U0,Y1,V0),这样 8 个字节表示的 2 个 RGBA 像素就转换为 4 个字节表示的 2 个 YUYV 像素。

RGB to YUV 的转换公式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAZ1DP6M-1686050151953)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\rgb.png)]

片段shader的编写:

#version 300 es
precision mediump float;
in vec2 v_texCoord;//纹理坐标
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;//RGBA 纹理
uniform float u_Offset;//采样偏移

//RGB to YUV
//Y =  0.299R + 0.587G + 0.114B
//U = -0.147R - 0.289G + 0.436B
//V =  0.615R - 0.515G - 0.100B
const vec3 COEF_Y = vec3( 0.299,  0.587,  0.114);
const vec3 COEF_U = vec3(-0.147, -0.289,  0.436);
const vec3 COEF_V = vec3( 0.615, -0.515, -0.100);

void main()
{
    vec2 texelOffset = vec2(u_Offset, 0.0);
    vec4 color0 = texture(s_TextureMap, v_texCoord);//当前像素的RGB值
    //偏移 offset 采样  偏移量到下一个像素 这个值应该是1/纹理宽度=到下一个像素的偏移量   
    vec4 color1 = texture(s_TextureMap, v_texCoord + texelOffset);//下一个像素的RGB值,则需要纹理坐标+偏移量
	//dot(x, y): 点积,各分量分别相乘 后 相加;给定两个n维向量a=(a1,a2,…,an)和b=(b1,b2,…,bn),求点积a·b=a1b1+a2b2+…+anbn。
    float y0 = dot(color0.rgb, COEF_Y);
    float u0 = dot(color0.rgb, COEF_U) + 0.5;//同样UV需要+0.5, 因为归一化
    float v0 = dot(color0.rgb, COEF_V) + 0.5;
    float y1 = dot(color1.rgb, COEF_Y);//得到下一个像素的Y值

    outColor = vec4(y0, u0, y1, v0);//组合的YUYV值
}

从上可以得知:RGB转YUV422的时候,就是两个像素的的RGBA值变成了一组YUYV值,则数据量少了一半,那么glViewPort 时 width 变为原来的一半,同样 glReadPixels 时 width 也变为原来的一半。

2、代码实践

使用EGL+FBO并将结果写入YUV文件中。

1)Java部分

MainActivity.java

package com.example.sixrgb2yuv;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.AssetManager;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private NativeEglRender mBgRender;
    private AssetManager mrg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mrg = getResources().getAssets();
        mBgRender = new NativeEglRender();
        mBgRender.native_InitScene(mrg);//将AssetManager对象传下去
        mBgRender.native_EglRenderInit();//调到Native里面的EGL初始化去
        mBgRender.native_EglRenderDraw();//调用底层OPENGL的离屏渲染
    }
}

NativeEglRender.java

package com.example.sixrgb2yuv;

public class NativeEglRender {
    static {
        System.loadLibrary("native-lib");
    }
    public static native void native_InitScene(Object mrg);
    public native void native_EglRenderInit();
    public native void native_EglRenderDraw();
    public native void native_EglRenderUnInit();
}

2)C++部分

native.cpp部分

//
// Created by CreatWall_zhouwen on 2023/4/11.
//

#include "jni.h"
#include <android/log.h>
#include <GLES3/gl3.h>
#include "RGB2YUYV.h"
#include "Util.h"
#include "ReadFileUtil.h"
#define NATIVE_RENDER_CLASS_NAME "com/example/sixrgb2yuv/NativeEglRender"
#define TAG "GLTRIANGLE"
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_sixrgb2yuv_NativeEglRender
 * Method:    native_InitScene
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL native_InitScene(JNIEnv *env, jobject instance, jobject msg)
{
   g_mrg = AAssetManager_fromJava(env, msg);
}

/*
 * Class:     com_example_sixrgb2yuv_NativeEglRender
 * Method:    native_EglRenderInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderInit(JNIEnv *env, jobject instance)
{
   RGB2YUYV::GetInstance();
   int width = 0, height = 0;
   unsigned char *img = ReadBMP("awesomeface.bmp", width, height);
   RGB2YUYV::GetInstance()->CreateGlesEnv();
   RGB2YUYV::GetInstance()->getTexturedata(img, width, height);
   RGB2YUYV::GetInstance()->CreateProgram(
         reinterpret_cast<const char *>(LoadFileContent("vertex.vs")),
         reinterpret_cast<const char *>(LoadFileContent("fFboShader.fs")));
}
/*
 * Class:     com_example_sixrgb2yuv_NativeEglRender
 * Method:    native_EglRenderDraw
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderDraw(JNIEnv *env, jobject instance)
{
   RGB2YUYV::GetInstance()->Draw();
}
/*
 * Class:     com_example_sixrgb2yuv_NativeEglRender
 * Method:    native_EglRenderUnInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderUnInit(JNIEnv *env, jobject instance)
{
   RGB2YUYV::GetInstance()->UnInit();
}

#ifdef __cplusplus
}
#endif

static JNINativeMethod g_RenderMethods[] = {
      {"native_InitScene",             "(Ljava/lang/Object;)V",       (void *)(native_InitScene)},
      {"native_EglRenderInit",             "()V",       (void *)(native_EglRenderInit)},
      {"native_EglRenderDraw",        "()V",       (void *)(native_EglRenderDraw)},
      {"native_EglRenderUnInit",        "()V",       (void *)(native_EglRenderUnInit)},
};

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{
   LOGD("RegisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("RegisterNativeMethods fail. clazz == NULL");
      return JNI_FALSE;
   }
   if (env->RegisterNatives(clazz, methods, methodNum) < 0)
   {
      LOGD("RegisterNativeMethods fail");
      return JNI_FALSE;
   }
   return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className)
{
   LOGD("UnregisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("UnregisterNativeMethods fail. clazz == NULL");
      return;
   }
   if (env != NULL)
   {
      env->UnregisterNatives(clazz);
   }
}

// call this func when loading lib
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
   LOGD("===== JNI_OnLoad =====");
   jint jniRet = JNI_ERR;
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return jniRet;
   }

   jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                              sizeof(g_RenderMethods) /
                              sizeof(g_RenderMethods[0]));
   if (regRet != JNI_TRUE)
   {
      return JNI_ERR;
   }

   return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *jvm, void *p)
{
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return;
   }

   UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);
}

Opengl操作类

RGB2YUYV.h

//
// Created by CreatWall_zhouwen on 2023/4/28.
//

#ifndef SIXRGB2YUV_RGB2YUYV_H
#define SIXRGB2YUV_RGB2YUYV_H

#include <GLES3/gl3.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>

class RGB2YUYV {
public:
    RGB2YUYV();
    ~RGB2YUYV();
    int CreateGlesEnv();
    void CreateProgram(const char *ver, const char * fragfbo);//创建工程并初始化
    void getTexturedata(unsigned char *data, int width, int height);//从NDK那边获取图片数据传过来
    static RGB2YUYV* GetInstance();
    static void DestroyInstance();
    void Draw();//绘画 就是执行离屏渲染再将离屏渲染的结果显示
    void UnInit();
private:
    GLuint m_FboVertexShader;//FBO的顶点着色器和片段着色器
    GLuint m_FboFragmentShader;
    GLuint m_FboProgramObj;//FBO工程ID
    GLuint m_ImageTextureId;//图片数据的纹理ID
    GLuint m_FboTextureId;//FBO绑定的空数据纹理ID
    GLint m_FboSamplerLoc;//FBO片段着色器中的采样器值的位置
    GLuint m_FboId;//FBO的ID
    unsigned char *texturedata;
    int texturewidth, textureheight;
    GLuint m_VaoId;//存放顶点数据
    GLuint m_VboIds[3];//0表示顶点坐标缓冲区,1表示离屏渲染纹理坐标缓冲区,2表示纹理索引坐标缓冲区

    EGLConfig  m_eglConf;
    EGLSurface m_eglSurface;
    EGLContext m_eglCtx;
    EGLDisplay m_eglDisplay;
};



#endif //SIXRGB2YUV_RGB2YUYV_H

RGB2YUYV.cpp

//
// Created by CreatWall_zhouwen on 2023/4/28.
//

#include <stdio.h>
#include "RGB2YUYV.h"
#include "Util.h"
#include "GLUtil.h"
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include "sys/stat.h"
#include "stdint.h"
RGB2YUYV* m_pContext = nullptr;
#define TAG "RGB2YUYV"
//顶点坐标
GLfloat vVertices[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
        1.0f,  1.0f, 0.0f,
};
//纹理坐标
GLfloat vFboTexCoors[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
};
GLushort indices[] = { 0, 1, 2, 1, 3, 2 };//三角形的索引数组

RGB2YUYV::RGB2YUYV() {
     m_FboVertexShader = GL_NONE;//FBO的顶点着色器和片段着色器
     m_FboFragmentShader= GL_NONE;
     m_FboProgramObj= GL_NONE;//FBO工程ID
     m_ImageTextureId= GL_NONE;//图片数据的纹理ID
     m_FboTextureId= GL_NONE;//FBO绑定的空数据纹理ID
     m_FboSamplerLoc= GL_NONE;//FBO片段着色器中的采样器值的位置
     m_FboId= GL_NONE;//FBO的ID
     m_VaoId= GL_NONE;//存放顶点数据
     m_VboIds[0]= GL_NONE;//0表示顶点坐标缓冲区,1表示离屏渲染纹理坐标缓冲区,2表示纹理索引坐标缓冲区

     m_eglConf= GL_NONE;
     m_eglSurface= GL_NONE;
     m_eglCtx= GL_NONE;
     m_eglDisplay= GL_NONE;
}

RGB2YUYV::~RGB2YUYV() {

}

int RGB2YUYV::CreateGlesEnv() {
    // EGL config attributes
    const EGLint confAttr[] =
            {
                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
                    EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
                    EGL_RED_SIZE,   8,
                    EGL_GREEN_SIZE, 8,
                    EGL_BLUE_SIZE,  8,
                    EGL_ALPHA_SIZE, 8,// if you need the alpha channel
                    EGL_DEPTH_SIZE, 16,// if you need the depth buffer
                    EGL_STENCIL_SIZE,8,
                    EGL_NONE
            };

    // EGL context attributes
    const EGLint ctxAttr[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };

    // surface attributes
    // the surface size is set to the input frame size
    const EGLint surfaceAttr[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT,1,
            EGL_NONE
    };
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;

    int resultCode = 0;
    do
    {
        //1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if(m_eglDisplay == EGL_NO_DISPLAY)
        {
            //Unable to open connection to local windowing system
            LOGD("EGLRender::CreateGlesEnv Unable to open connection to local windowing system");
            resultCode = -1;
            break;
        }

        //2. 初始化 EGL 方法
        if(!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers))
        {
            // Unable to initialize EGL. Handle and recover
            LOGD("EGLRender::CreateGlesEnv Unable to initialize EGL");
            resultCode = -1;
            break;
        }

        LOGD("EGLRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);

        //3. 获取 EGLConfig 对象,确定渲染表面的配置信息
        if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
        {
            LOGD("EGLRender::CreateGlesEnv some config is wrong");
            resultCode = -1;
            break;
        }

        //4. 创建渲染表面 EGLSurface, 使用 eglCreatePbufferSurface 创建屏幕外渲染区域
        m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
        if(m_eglSurface == EGL_NO_SURFACE)
        {
            switch(eglGetError())
            {
                case EGL_BAD_ALLOC:
                    // Not enough resources available. Handle and recover
                    LOGD("EGLRender::CreateGlesEnv Not enough resources available");
                    break;
                case EGL_BAD_CONFIG:
                    // Verify that provided EGLConfig is valid
                    LOGD("EGLRender::CreateGlesEnv provided EGLConfig is invalid");
                    break;
                case EGL_BAD_PARAMETER:
                    // Verify that the EGL_WIDTH and EGL_HEIGHT are
                    // non-negative values
                    LOGD("EGLRender::CreateGlesEnv provided EGL_WIDTH and EGL_HEIGHT is invalid");
                    break;
                case EGL_BAD_MATCH:
                    // Check window and EGLConfig attributes to determine
                    // compatibility and pbuffer-texture parameters
                    LOGD("EGLRender::CreateGlesEnv Check window and EGLConfig attributes");
                    break;
            }
        }

        //5. 创建渲染上下文 EGLContext
        m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
        if(m_eglCtx == EGL_NO_CONTEXT)
        {
            EGLint error = eglGetError();
            if(error == EGL_BAD_CONFIG)
            {
                // Handle error and recover
                LOGD("EGLRender::CreateGlesEnv EGL_BAD_CONFIG");
                resultCode = -1;
                break;
            }
        }

        //6. 绑定上下文
        if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
        {
            LOGD("EGLRender::CreateGlesEnv MakeCurrent failed");
            resultCode = -1;
            break;
        }
        LOGD("EGLRender::CreateGlesEnv initialize success!");
    }
    while (false);

    if (resultCode != 0)
    {
        LOGD("EGLRender::CreateGlesEnv fail");
    }
    LOGD("EGLRender::CreateGlesEnv Success");
    return resultCode;
}

void RGB2YUYV::CreateProgram(const char *ver, const char *fragfbo) {
    LOGD("CreateProgram Enter");
    // 编译链接用于离屏渲染的着色器程序
    m_FboProgramObj = CreateGLProgram(ver, fragfbo, m_FboVertexShader, m_FboFragmentShader);
    if (m_FboProgramObj == GL_NONE)
    {
        LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
        return;
    }
    LOGD("CreateGLProgram Success");

    //获取片段着色器中s_TextureMap的属性位置,编译后期指定是哪个纹理
    m_FboSamplerLoc = glGetUniformLocation(m_FboProgramObj, "s_TextureMap");
    LOGD("glGetUniformLocation Success");

    //生成VBO 加载顶点数据和索引数据
    glGenBuffers(4, m_VboIds);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vFboTexCoors), vFboTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);//最后一个为纹理的索引缓冲数据
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    LOGD("glGenBuffers Success");

    //初始化离屏渲染的VAO
    glGenVertexArrays(1, &m_VaoId);
    glBindVertexArray(m_VaoId);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);
    glBindVertexArray(GL_NONE);
    LOGD("m_VaoId[0] Success");

    //创建并初始化图形纹理
    glGenTextures(1, &m_ImageTextureId);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
    LOGD("CreateProgram %s", texturedata);
    glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_ImageTextureId Success");
    //创建离屏的纹理,不绑定数据值申请内存
    glGenTextures(1, &m_FboTextureId);
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //最后输出YUYV数据格式,则宽缩短一半了。
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth/2, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_FboTextureId Success");

    //创建并初始化FBO,帧缓冲
    glGenFramebuffers(1, &m_FboId);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);//绑定帧缓冲
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);//激活这个m_FboTextureId纹理绑定GL_TEXTURE_2D
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);//纹理附加到帧缓冲
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
        LOGD("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");
        return ;
    }
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
    LOGD("m_FboId Success");
}

void RGB2YUYV::Draw() {
    LOGD("Draw Enter");
    glViewport(0, 0, texturewidth/2, textureheight);
    glUseProgram(m_FboProgramObj);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
    glBindVertexArray(m_VaoId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glUniform1i(m_FboSamplerLoc, 0);
    float texelOffset = (float) (1.f / (float) texturewidth);
    glUniform1f(glGetUniformLocation(m_FboProgramObj, "u_offest"), texelOffset);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    glBindVertexArray(GL_NONE);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("Draw success");
    uint8_t *pBuffer = new uint8_t[texturewidth*textureheight * 2];
    glReadPixels(0, 0, texturewidth / 2, textureheight, GL_RGBA, GL_UNSIGNED_BYTE, pBuffer);
    //写文件
    const char *imgPath= "/data/data/com.example.sixrgb2yuv/RGB2YUYV.yuv";
    FILE *fp = fopen(imgPath, "wb");
    if(fp == NULL)
    {
        LOGD("fopen error");
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        return ;
    }
    fwrite(pBuffer,1, texturewidth*textureheight * 2,  fp);
    fclose(fp);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    LOGD("Draw End");
}


void RGB2YUYV::getTexturedata(unsigned char *data, int width, int height) {
     texturedata = data;
     texturewidth = width;
     textureheight = height;
     LOGD("getTexturedata Success %s", texturedata);
}


RGB2YUYV *RGB2YUYV::GetInstance() {
     if (m_pContext == nullptr)
     {
          m_pContext = new RGB2YUYV();
     }
     return m_pContext;
}

void RGB2YUYV::DestroyInstance() {
     if (m_pContext)
     {
          delete m_pContext;
          m_pContext = nullptr;
     }
}



void RGB2YUYV::UnInit() {
     LOGD("EGLDraw::UnInit");
     if (m_FboProgramObj)
     {
          glDeleteProgram(m_FboProgramObj);
          m_FboProgramObj = GL_NONE;
     }
     if (m_FboTextureId)
     {
          glDeleteTextures(1, &m_FboTextureId);
          m_FboTextureId = GL_NONE;
     }
     if (m_VboIds[0])
     {
          glDeleteBuffers(3, m_VboIds);
          m_VboIds[0] = GL_NONE;
          m_VboIds[1] = GL_NONE;
          m_VboIds[2] = GL_NONE;

     }
     if (m_VaoId)
     {
          glDeleteVertexArrays(1, &m_VaoId);
          m_VaoId = GL_NONE;
     }
     if (m_FboId)
     {
          glDeleteFramebuffers(1, &m_FboId);
          m_FboId = GL_NONE;
     }

     //8. 释放 EGL 环境
     if (m_eglDisplay != EGL_NO_DISPLAY) {
          eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
          eglDestroyContext(m_eglDisplay, m_eglCtx);
          eglDestroySurface(m_eglDisplay, m_eglSurface);
          eglReleaseThread();
          eglTerminate(m_eglDisplay);
     }
}

工具类

GLUTIL_H

//
// Created by CreatWall_zhouwen on 2023/4/21.
//

#ifndef FORTHDRAWFBO_GLUTIL_H
#define FORTHDRAWFBO_GLUTIL_H


#include "Util.h"
#include <GLES3/gl3.h>
#define TAG "GLUTIL_H"

GLuint LoadShader(GLenum shaderType, const char *pSource)
{
    LOGD("LoadShader Entern");
    //创建对应类型的着色器
    GLuint ShaderHandle = glCreateShader(shaderType);//创建shaderType类型的着色器
    if (ShaderHandle)
    {
        //附加着色器源码
        glShaderSource(ShaderHandle, 1, &pSource, NULL);//shader源码为pSource
        //编译着色器
        glCompileShader(ShaderHandle);
        //获取编译结果是否成
        GLint compiled = 0;
        glGetShaderiv(ShaderHandle, GL_COMPILE_STATUS, &compiled);
        if (!compiled){
            glDeleteShader(ShaderHandle);
            LOGD("glCompileShader ShaderHandle error");
            return 0;
        }
    }
    LOGD("LoadShader Leave");
    return ShaderHandle;
}

GLuint CreateGLProgram(const char *pVertexShaderSource, const char *pFragShaderSource, GLuint &vertexShaderHandle, GLuint &fragShaderHandle)
{
    LOGD("CreateProgram Entern");

    GLuint program = 0;
    GLint AttachStatus = GL_FALSE;
    //创建顶点着色器
    vertexShaderHandle = LoadShader(GL_VERTEX_SHADER, pVertexShaderSource);
    if (!vertexShaderHandle) return program;
    LOGD("vertexShaderHandle success");

    //创建片段着色器
    fragShaderHandle = LoadShader(GL_FRAGMENT_SHADER, pFragShaderSource);
    if (!fragShaderHandle) return program;
    LOGD("fragShaderHandle success");

    //创建程序
    program = glCreateProgram();
    if (program)
    {
        AttachStatus = 0;
        glAttachShader(program, vertexShaderHandle);
        glGetShaderiv(vertexShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus != 0){//返回0就是成功
            LOGD("glAttachShader vertexShaderHandle error %d",AttachStatus);
            return 0;
        }
        LOGD("glAttachShader vertexShaderHandle success %d",AttachStatus);
        AttachStatus = 0;
        glAttachShader(program, fragShaderHandle);
        glGetShaderiv(fragShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus != 0){
            LOGD("glAttachShader fragShaderHandle error %d",AttachStatus);
            return 0;
        }
        LOGD("glAttachShader fragShaderHandle success %d",AttachStatus);
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE){
            LOGD("glLinkProgram error AttachStatus = %d", linkStatus);
            glDeleteProgram(program);
            program = 0;
            return 0;
        }
        glDetachShader(program, vertexShaderHandle);
        glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
        glDetachShader(program, fragShaderHandle);
        glDeleteShader(fragShaderHandle);
        fragShaderHandle = 0;
        LOGD("glCreateProgram success");
    }
    LOGD("CreateProgram Leave");
    return program;
}


#endif //FORTHDRAWFBO_GLUTIL_H

在这里插入图片描述

参考链接

android通过JNI用C/C++创建本地文件 https://blog.csdn.net/qq_34759481/article/details/84548821

使用 OpenGL 实现 RGB 到 YUV 的图像格式转换 https://blog.csdn.net/Kennethdroid/article/details/117675581

android通过JNI用C/C++创建本地文件 https://blog.csdn.net/qq_34759481/article/details/84548821

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VLAN内容

一、VLAN VLAN是拥有一组共同要求且与物理位置无关的终端设备的逻辑组。 终端设备包括终端用户工作站、服务器、路由器等诸如此类设备。 物理子网由想同物理电缆分段中的设备组成&#xff1b;逻辑子网由相互通信且物理位置无关的设备所组成。VLAN是一种逻辑子网&#xff0c;并…

华为OD机试真题 Java 实现【分糖果】【2022Q2 200分】,附详细解题思路

一、题目描述 小明从糖果盒中随意抓一把糖果&#xff0c;每次小明会取出一半的糖果分给同学们。 当糖果不能平均分配时&#xff0c;小明可以选择从糖果盒中&#xff08;假设盒中糖果足够&#xff09;取出一个糖果或放回一个糖果。 小明最少需要多少次&#xff08;取出、放回…

Sentinel-1(Resolution、Pixel Spacing)

目录 10m&#xff1f;还是20*22m&#xff1f; Resolution和Pixel Spacing 10m&#xff1f;还是20*22m&#xff1f; Sentinel-1 SAR GRD的分辨率为10m&#xff0c;基本上是常识了https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S1_GRD#description…

创建型设计模式06-单例模式

&#x1f9d1;‍&#x1f4bb;作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 单例模式 单例模式是一种创建型设计模式&#xff0c;它的目的是确保一个类只有一个实例&#xff0c;并…

RHCE 作业四

1.dns正向解析 一.初始准备 关闭安全软件安装bind软件 [rootserver ~]# setenforce 0 [rootserver ~]# systemctl stop firewalld [rootserver ~]# yum install bind -y 配置服务端和客户端ip 二.DNS配置 1>服务端编辑bind主配置文件 [rootserver ~]# vim /et…

案例24:基于Springboot旅游景点导游平台系统开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

六、opengles显示YUV数据

工程文件名为&#xff1a;com.example.threetextureyuv 1、yuv回顾 1&#xff09;yuv的由来 是在保持图片质量的前提下降低图片内存大小提供传输效率&#xff0c;并且传统的BGR格式 对于黑白图片不支持亮度的调节。 Y”表示明亮度&#xff08;Luminance、Luma&#xff09;&…

javascript基础二十九:JavaScript如何判断一个元素是否在可视区域中?

一、用途 可视区域即我们浏览网页的设备肉眼可见的区域&#xff0c;如下图 在日常开发中&#xff0c;我们经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值&#xff08;例如 100 px&#xff09;&#xff0c;从而实现一些常用的功能&#xff0c;例如&#xff1a;…

路径规划算法:基于和声优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于和声优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于和声优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法和声…

chatgpt赋能python:Python如何倒序循环

Python如何倒序循环 在Python编程中&#xff0c;倒序循环是一种常见的操作。有时候我们需要倒序遍历一个序列&#xff0c;以便获取最后一个元素或在某些情况下需要运算。Python提供了多种方法来实现倒序循环。在本文中&#xff0c;我们将介绍Python中可用的不同循环遍历方法&a…

源代码加密技术分析

在源代码开发企业&#xff0c;如何保护好自己开发的产品&#xff0c;维护好自主知识产权&#xff0c;是企业开发过程中必要了解的&#xff0c;对于经常做开发的来讲对源代码加密也多种方法&#xff0c;对于传统的C或C之类的语言来说&#xff0c;要在Web上保护源代码是很容易的&…

快手流批一体数据湖构建实践

导读 本次将介绍快手为什么建设数据湖&#xff0c;在数据湖建设过程中遇到的问题和取得的成果&#xff0c;并对未来发展进行展望。 主要内容包括以下四大部分&#xff1a; 1. 数据湖架构 2. 基于 Hudi 构建快手数据湖 3. 快手的实践案例 4. 快手的发展规划 01 数据湖架构…

AB压测工具的介绍及安装

前言 今天我要和大家聊聊AB压测工具&#xff0c;如果你对网站性能测试感兴趣或有需要&#xff0c;那么这篇文章一定会帮到你。 我曾经也因为缺少良好的压力测试工具而苦恼&#xff0c;直到我发现了AB压测工具。它可以帮助我们测试网站在高并发情况下的性能表现&#xff0c;让…

4. 共享模型之管程(4.1 共享带来的问题)

4.1 共享带来的问题 1、Java 的体现2、问题分析3、临界区4、竞态条件 1、Java 的体现 两个线程对初始值为 0 的静态变量一个做自增&#xff0c;一个做自减&#xff0c;各做 5000 次&#xff0c;结果是 0 吗&#xff1f; public class TestCounterUnsafe {static int counter …

Arrays源码

介绍 java.util中的工具类&#xff0c;提供数组相关的常用操作&#xff0c;排序、比较、填充、二分查找等功能 该类还包含一个静态内部类ArrayList&#xff0c;其中add、remove、clear方法都是没有实现的。 常量&变量 /*** The minimum array length below which a para…

测试人总结怎么写?一篇文章详细总结全了!

目录 前言&#xff1a; 总结内容应包括哪些 不可缺少的模板-前期准备 总结过去-用数据来说话 纵向数据 横向数据 展望未来-做好规划 总结亮点 从其他人学到的点 总结弊病 结尾&#xff1a; 前言&#xff1a; 在这一年里&#xff0c;我作为一名测试人员&#xff0c;不断努力…

Tree 树形控件一级菜单没有复选框,子菜单有复选框,如何实现?

<el-dialogtitle"技术职称选择":visible.sync"isShow"width"30%"top"50px":before-close"closeInputSelectedDepDialog"><div class"tree-content"><el-treeclass"filter-tree my-left-tree&…

AMD HD7850 4G显卡刷Bios验真伪(二)

结果就是&#xff1a;开机黑屏&#xff0c;使用HD7850的bios无法识别它… 秉着寻根问底的原则&#xff0c;继续冲浪找线索~ 是的 你猜对了&#xff0c;不出意外的话 就出了意外… 初步断定&#xff0c;这货是7850的阉割版Radeon HD 7850 768SP 1. 首先&#xff0c;尝试在泡泡…

Spring高手之路3——揭秘Spring依赖注入和SpEL表达式

本篇会给大家举出各种Spring属性依赖注入的例子&#xff0c;方便大家理解。 文章目录 1. setter属性注入1.1 使用XML进行setter方法注入1.2 使用Bean注解进行setter方法注入1.3 setter方法注入完整代码示例 2. 构造器注入2.1 使用XML进行构造器注入2.2 使用Bean注解进行构造器属…

面试经历:我为什么选择的测试的?

目录 前言&#xff1a; 判定缺陷间的重复及依赖关系需要开发能力 使用自动化测试工具需要开发能力 黑盒测试偏爱开发能力 说明 白盒测试需要开发能力 安全测试需要开发能力 开发测试工具 前言&#xff1a; 不知不觉已经从事软件测试六年了&#xff0c;从毕业到进入外包公司外…