《Ndk中使用Mediacode解码》
《android mediacodec 编码demo(java)》
《NDK中使用mediacodec编码h264》
《Android native 层使用opengl渲染YUV420p和NV12》
《android 使用NativeWindow渲染RGB视频》
《opengl 叠加显示文字》
《android studio 编译freeType》
《最原始的yuv图像叠加文字的实现--手动操作像素》

利用opengl进行yuv的渲染,主要原理利用显卡的加速运算,是把YUV转换成 RGBA ,然后交给gl渲染, 即opengl最终需要的数据还是 RGBA,  我们可以采用cpu软件计算的方式把 yuv转成rgb,这样计算量大,占用CPU,所以一般用opengl 利用显卡运算,把我我们的yuv数据转成RGBA.
所以这里的工作,主要在于 怎么把 yuv 利用显卡运算转换为 RGBA。

先准备点基础知识:

YUV格式

YUV420p( plane 平面格式):
                             YV12: y0y1y2y3y4y5y6y7  v0v1  u0u1    先平铺完y,再平铺完v,在平铺完u
                             YU12:y0y1y2y3y4y5y6y7   u0u1 v0v1     先平铺完y,再平铺完u,在平铺完v 这个                                也叫做I420

YUV420sp( 平面+交叉)
                             NV12: y0y1y2y3y4y5y6y7  v0u0 v1u1     先平铺完y,再交叉存储 u和v
                             NV21: y0y1y2y3y4y5y6y7  u0v0 u1v1     先平铺完y,再交叉存储 v和u

opengl中纹理的颜色格式

以YUV420p YU12来举例,这计算的原理,主要是准备三个纹理, 将Y U V三个分量分别存储到一个纹理上去,然后在opengl的显卡执行程序中,将这三个分量取出进行一次矩阵运算,这个运算及时 yuv转rgb的矩阵公式,得到rgba,再显示。这里有一个注意的地方, 纹理的GLenum format
这个值有几种选择: 在gl2.h中有宏定义:

#define GL_ALPHA                          0x1906
#define GL_RGB                            0x1907
#define GL_RGBA                           0x1908
#define GL_LUMINANCE                      0x1909
#define GL_LUMINANCE_ALPHA                0x190A
在网络上搜罗了一圈,总结一下:

/*
The difference between the three single-component texture formats, GL_ALPHA, GL_LUMINANCE and GL_INTENSITY, is in the way the four-component RGBA color vector is generated. If the value for a given texel is X, then the RGBA color vector generated is:

* GL_ALPHA: RGBA = (0, 0, 0, X0)
    * GL_LUMINANCE: RGBA = (X0, X0, X0, 1)
    * GL_INTENSITY: RGBA = (X0, X0, X0, X0)
    * GL_LUMINANCE_ALPHA: RGBA = (X0, X0, X0, X1), 这个才是二维交叉的
    * GL_RGB :          RGBA=(X0, X1, X2, 1),三个量交叉
    * GL_RGBA :         RGBA=(X0, X1, X2, X3),四个量交叉
    这里用的 GL_LUMINANCE,每一纹理分量  r g b 是一样的值,都是纹理设定的数据
    其实我们可以改用GL_LUMINANCE_ALPHA, 只需要一个量
*/
对于YUV420p的格式中的yuv分量,我们直接选用GL_LUMINANCE GL_ALPHA  GL_INTENSITY 都可以,平铺格式。 而对于NV12 这种交叉存储的数据,就需要选用GL_LUMINANCE_ALPHA, 利用其中的任意一个 r g b分量和alph分量,可以得到 二维交叉的数据,即uv两个交叉存储。

着色器中的参数说明:

(摘自某一个博主文章https://blog.csdn.net/u012861978/article/details/97117321)

  • 输入参数介绍:
    1.着色器程序(Shader Program,图中没有画出):由 main 申明的一段程序源码或可执行文件,描述在顶点上执行的操作:如坐标变换、计算光照公式产生每个顶点颜色、计算纹理坐标。

    2.属性(Attribute):由 vertext array 提供的顶点数据,如空间位置,法向量,纹理坐标以及顶点颜色,属性可以理解为针对每一个顶点的输入数据。属性只在顶点着色器中才有,片元着色器中没有属性。

    3.统一值(Uniforms): Uniforms保存由应用程序传递给着色器的只读常量数据。在顶点着色器中,这些数据通常是变换矩阵,光照参数,颜色等。由 uniform 修饰符修饰的变量属于全局变量该全局性对顶点着色器与片元着色器均可见,也就是说,这两个着色器如果被连接到同一个应用程序中,它们共享同一份 uniform 全局变量集。因此如果在这两个着色器中都声明了同名的 uniform 变量,要保证这对同名变量完全相同:同名+同类型,因为它们实际是同一个变量。

    4.采样器(Samplers): 一种特殊的 uniform,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。

  • 输出参数介绍:
    1.可变变量(Varying):varying 变量用于存储顶点着色器的输出数据,也存储片元着色器的输入数据。varying 变量会在光栅化处理阶段被线性插值。顶点着色器如果声明了 varying 变量,它必须被传递到片元着色器中才能进一步传递到下一阶段,因此顶点着色器中声明的 varying 变量都应在片元着色器中重新声明为同名同类型的 varying 变量。(所以我们程序里在顶点着色器和片段着色器(片段或片元,叫法不同)中有同一个变量:varying vec2 vTextCoord, 是同一份变量,在顶点着色器中获取纹理输入,在片段着色器中用来计算rgb颜色。)

    1. gl_Position:在顶点着色器阶段至少应输出位置信息-即内建变量

    2. gl_FrontFacing:为back-face culling stage阶段生成的变量,无论精选是否被禁用,该变量都会生成。

    3. gl_PointSize:点大小。

最后绘制的位置:(顶点着色器用来确认每个点的位置)使用的三角形扇区绘制:

不明白它为什么叫顶点“着色器”,不是叫顶点生成器更好?毕竟它和颜色没什么关系,只是确定点的坐标
这里的顶点着色器,1. 没做什么运算,只是单纯原样将我们输入的数据复制到固定变量:gl_Position ,2. 将纹理坐标上下翻转了下(纹理坐标系 SW和计算机图像坐标系 XY,y轴方向刚好相反)
const char *vertexShader_ = GET_STR(
attribute vec4 aPosition;//输入的顶点坐标,会在程序指定将数据输入到该字段
attribute vec2 aTextCoord;//输入的纹理坐标,会在程序指定将数据输入到该字段
varying vec2 vTextCoord;//输出的纹理坐标
void main() {
    //这里其实是将上下翻转过来
    vTextCoord = vec2(aTextCoord.x, 1.0 - aTextCoord.y);
    //直接把传入的坐标值作为传入渲染管线。gl_Position是OpenGL内置的
    gl_Position = aPosition;
}
);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 一个三角形扇以第一个中心顶点作为起始,使用相邻的第三三个顶点连接起来创建第一个三角形,  接着以第二个顶点作为中心起点,第三四个顶点连接起来创建三角形,依次类推。
这里使用的顶点数据是:

static float ver[] = {
            1.0f, -1.0f, 0.0f,
            -1.0f, -1.0f, 0.0f,
            1.0f, 1.0f, 0.0f,
            -1.0f, 1.0f, 0.0f
    };
对应绘制如下图,由两个三角形组成这个矩形区域。 三角形ABC + 三角形BCD 

绘制的纹理颜色:(片段着色器确认最终的颜色)

这里的片段着色器程序的主要工作,就是 取纹理里面的yuv数据,然后进行一个矩阵运算转换为RGBA数据,这就是每一个片段最终的颜色数据,赋值给固定的变量:gl_FragColor
const char *fragNV12_ = GET_STR(
    precision mediump float;
    varying vec2 vTextCoord;
    uniform sampler2D yTexture;
    uniform sampler2D uvTexture;
    void main(void)
    {
        vec3 yuv; 
        vec3 rgb; 
        yuv.x = texture2D(yTexture, vTextCoord.st).r; 
        yuv.y = texture2D(uvTexture, vTextCoord.st).r - 0.5; 
        yuv.z = texture2D(uvTexture, vTextCoord.st).a - 0.5; 
        rgb = mat3( 1,       1,         1, 
                    0,       -0.39465,  2.03211, 
                    1.13983, -0.58060,  0) * yuv; 
        gl_FragColor = vec4(rgb, 1.0); 
    }
);

这里上代码:( 循环读取"/storage/emulated/0/960X544_nv12.yuv" 文件,以30帧每秒的速度渲染)

文件jni_NativeOpengl.cpp

//canok 20210528
//jni_NativeOpengl.cpp 主要实现jni转换
#include <assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"Openglfuncs.h"
#include "GlPragrame.h"
#include "logs.h"static char* jstringToChar(JNIEnv* env, jstring jstr) {char* rtn = NULL;jclass clsstring = env->FindClass("java/lang/String");//LOGD("fine String class------clsstring==NULL?%d",clsstring==NULL);jstring strencode = env->NewStringUTF("utf-8");jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);jsize alen = env->GetArrayLength(barr);jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);if (alen > 0) {rtn = (char*) malloc(alen + 1);memcpy(rtn, ba, alen);rtn[alen] = 0;}env->ReleaseByteArrayElements(barr, ba, 0);return rtn;
}void jStart(JNIEnv *env, jobject obj,jobject jsurface, jstring srcfile, int type, jint w, jint h, int fps){ANativeWindow *pWind= ANativeWindow_fromSurface(env, jsurface);char*file_in = jstringToChar(env,srcfile);//内部会开启另一个线程,并在新线程中初始化EGL环境,然后在新线程中渲染//注意,EGL初的始化的,一定要和渲染 为同一个线程 里面有个调用 eglMakeCurrent 。//如果把EGL的初始化也封装成了 jni借口由java的线程来调用,就不要在这里面单独再开启线程渲染,可以直接使用java的线程体来 执行渲染循环。mygl_start(pWind,file_in,type,w,h,fps);
}void jStop(JNIEnv *env, jobject obj){mygl_stop();
}
static JNINativeMethod gMethods[] = {{"native_start",     "(Landroid/view/Surface;Ljava/lang/String;IIII)V",      (void*)jStart},{"native_stop",  "()V",   (void*)jStop},
};static const char* const kClassPathName = "com/example/opengl_native/NativeOpengl";
static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) {jclass clazz;clazz = env->FindClass(className);if (clazz == NULL) {return JNI_FALSE;}if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {return JNI_FALSE;}return JNI_TRUE;
}
// This function only registers the native methods
static int registerFunctios(JNIEnv *env)
{ALOGD("register [%s]%d",__FUNCTION__,__LINE__);return registerNativeMethods(env,kClassPathName, gMethods, sizeof(gMethods)/sizeof(gMethods[0]));
}jint JNI_OnLoad(JavaVM* vm, void* reserved)
{ALOGD("onloader");JNIEnv* env = NULL;jint result = -1;if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {ALOGD("ERROR: GetEnv failed\n");goto bail;}assert(env != NULL);if (registerFunctios(env) < 0) {ALOGE(" onloader ERROR: MediaPlayer native registration failed\n");goto bail;}ALOGD("onloader register ok ![%s]%d",__FUNCTION__,__LINE__);result = JNI_VERSION_1_4;bail:return result;
}

文件Openglfuncs.cpp

//canok 20210528
//文件openglfuncs.cpp 主要初始化egl环境,读文件进行渲染
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "logs.h"
#include "GlPragrame.h"
#include "Openglfuncs.h"
#define MIN(x,y) (x)<(y)?(x):(y)#define DEBUG_FROME_FILE 1
EGLDisplay megldisplay =NULL;
EGLSurface meglSurface =NULL;
EGLContext meglContext=NULL;
int mbRun=0;
void initGles(){// 1 EGL 初始化EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);if(display == EGL_NO_DISPLAY){ALOGE("eglGetDisplay failed %d", eglGetError());return;}megldisplay = display;if(!eglInitialize(display,NULL,NULL)){ALOGE("eglInitialize failed %d", eglGetError());}//2配置displayEGLConfig config;EGLint configNum;EGLint configSpec[] = {EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE,8,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,EGL_SURFACE_TYPE,EGL_WINDOW_BIT,EGL_NONE};if(EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)){ALOGD("eglChooseConfig failed!");return;}//3 EGLcontext 创建上下文const EGLint ctxAttr[] = {EGL_CONTEXT_CLIENT_VERSION, 2,EGL_NONE};EGLContext  context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);if(context == EGL_NO_CONTEXT){ALOGD("eglCreateContext failed!");return;}meglContext=context;//4创建EGLsurface,把 java端传入的surface和EGL关联起来EGLSurface  eglsurface = eglCreateWindowSurface(display, config, global_Context.nwin, 0);if(eglsurface == EGL_NO_SURFACE){ALOGD("eglCreateWindowSurface failed!");return;}meglSurface=eglsurface;if(EGL_TRUE != eglMakeCurrent(display, eglsurface, eglsurface, context)){ALOGD("eglMakeCurrent failed!");return ;}glClearColor(0.0f,0.0f,0.0f,1.0f);glClear(GL_COLOR_BUFFER_BIT);eglSwapBuffers(megldisplay, meglSurface);
}void *renderthread(void * prame){
#if DEBUG_FROME_FILE//第一步搭建ES环境initGles();ALOGD("mygl_init intglescontext ok ");//第二步 创建显卡可执行程序createprograme();ALOGD("mygl_init createprograme ok %d",global_Context.type);//waring!!!!!!!!//glViewport(0,0,WIDTH,HEIGHT);FILE *fp = fopen(global_Context.file_in,"r");if(fp ==NULL){ALOGD("fopen failed!");return NULL;}global_Context.buflen=global_Context.mW*global_Context.mH*3/2;global_Context.buf = (unsigned char*)malloc( global_Context.buflen);int ret =0;while(mbRun){//这里绘制if( (ret = fread(global_Context.buf,1,global_Context.buflen,fp)) ==global_Context.buflen ){drawFrame(global_Context.buf,global_Context.buflen);eglSwapBuffers(megldisplay, meglSurface);usleep(1000*1000/global_Context.mfps);}else{//循环播放fseek(fp,0,SEEK_SET);}}//销毁if(megldisplay!=NULL && meglSurface!=NULL && meglContext!=NULL){eglDestroySurface(megldisplay,meglSurface);eglDestroyContext(megldisplay,meglContext);}fclose(fp);free(global_Context.file_in);free(global_Context.buf);
#elseinitGles();ALOGD(" intglescontext ok ");createprograme();ALOGD(" createprograme ok ");global_Context.buflen=global_Context.mW*global_Context.mH*3/2;global_Context.buf = (unsigned char*)malloc( global_Context.buflen);if(global_Context.buf == NULL){ALOGE("err to malloc!");return NULL;}while(mbRun){//条件等待pthread_mutex_lock(&global_Context.mCondMutex);pthread_cond_wait(&global_Context.mCond,&global_Context.mCondMutex);pthread_mutex_unlock(&global_Context.mCondMutex);pthread_mutex_lock(&global_Context.bufMutex);do {//渲染,期间不允许同时修改drawFrame(global_Context.buf, global_Context.buflen);eglSwapBuffers(megldisplay, meglSurface);}while(0);pthread_mutex_unlock(&global_Context.bufMutex);}//销毁if(megldisplay!=NULL && meglSurface!=NULL && meglContext!=NULL){eglDestroySurface(megldisplay,meglSurface);eglDestroyContext(megldisplay,meglContext);}free(global_Context.buf);
#endifpthread_exit(NULL);return NULL;
}
void mygl_start(ANativeWindow *pWnid, char*file_in,int type, int w, int h,int fps){global_Context.file_in = file_in;global_Context.type = type;global_Context.nwin = pWnid;global_Context.mW = w;global_Context.mH = h;global_Context.mfps =fps;ALOGD("start run");//创建渲染程序:if(mbRun){ALOGE("had run, return");}mbRun =true;int ret =0;if(0 != (ret = pthread_create(&global_Context.mThread,NULL,renderthread,NULL))){ALOGE("pthread_create erro");}//pthread_detach(pd);
}
void mygl_render(unsigned char *buf,int size){pthread_mutex_lock(&global_Context.bufMutex);do {//拷贝,期间不允许同时修改memcpy(global_Context.buf,buf,MIN(size,global_Context.buflen));}while(0);pthread_mutex_unlock(&global_Context.bufMutex);//释放条件pthread_mutex_lock(&global_Context.mCondMutex);pthread_cond_signal(&global_Context.mCond);pthread_mutex_unlock(&global_Context.mCondMutex);}
void mygl_stop(){mbRun = false;//释放条件pthread_mutex_lock(&global_Context.mCondMutex);pthread_cond_signal(&global_Context.mCond);pthread_mutex_unlock(&global_Context.mCondMutex);//等待结束,避免出现 FORTIFY: pthread_mutex_lock called on a destroyed mutexpthread_join(global_Context.mThread,NULL);
}

文件GlPragrame.cpp

//canok 20210528
//GlPragrame.cpp 主要实现 gl程序
//canok 2021.0528
#include <stdlib.h>
#include "GlPragrame.h"
#include "Openglfuncs.h"
#include "logs.h"
#define GET_STR(x) #x
struct S_CONTEXT global_Context = {.bufMutex=PTHREAD_MUTEX_INITIALIZER,.mCondMutex=PTHREAD_MUTEX_INITIALIZER,
.mCond=PTHREAD_COND_INITIALIZER};const char *vertexShader_ = GET_STR(
attribute vec4 aPosition;//输入的顶点坐标,会在程序指定将数据输入到该字段
attribute vec2 aTextCoord;//输入的纹理坐标,会在程序指定将数据输入到该字段
varying vec2 vTextCoord;//输出的纹理坐标
void main() {//这里其实是将上下翻转过来vTextCoord = vec2(aTextCoord.x, 1.0 - aTextCoord.y);//直接把传入的坐标值作为传入渲染管线。gl_Position是OpenGL内置的gl_Position = aPosition;
}
);
/*
The difference between the three single-component texture formats, GL_ALPHA, GL_LUMINANCE and GL_INTENSITY, is in the way the four-component RGBA color vector is generated. If the value for a given texel is X, then the RGBA color vector generated is:* GL_ALPHA: RGBA = (0, 0, 0, X0)* GL_LUMINANCE: RGBA = (X0, X0, X0, 1)* GL_INTENSITY: RGBA = (X0, X0, X0, X0)* GL_LUMINANCE_ALPHA: RGBA = (X0, X0, X0, X1), 这个才是二维交叉的* GL_RGB :          RGBA=(X0, X1, X2, 1),三个量交叉* GL_RGBA :          RGBA=(X0, X1, X2, X3),四个量交叉这里用的 GL_LUMINANCE,每一纹理分量  r g b 是一样的值,都是纹理设定的数据其实我们可以改用GL_LUMINANCE_ALPHA, 只需要一个量这里整个的原理,即把YUV420S  每一个分量当做一个纹理,然后在片原着色器中利用显卡的高速运算,通过 yuv转rgb 的矩阵公式,把三个纹理分量的值计算转换成rgb接着,然后画这个rgba 的点。
*/const char *fragYUV420P_ = GET_STR(precision mediump float;varying vec2 vTextCoord;//输入的yuv三个纹理uniform sampler2D yTexture;//采样器uniform sampler2D uTexture;//采样器uniform sampler2D vTexture;//采样器void main() {vec3 yuv;vec3 rgb;//分别取yuv各个分量的采样纹理//下面是一个 yuv转 rgb的矩阵运算// 纹理的 r g b相等,都是一个值/**矩阵公式*【】x [[yuv] -[0,0.5,0.5]]*/yuv.x = texture2D(yTexture, vTextCoord).r;yuv.y = texture2D(uTexture, vTextCoord).r - 0.5;yuv.z = texture2D(vTexture, vTextCoord).r - 0.5;rgb = mat3(1.0, 1.0, 1.0,0.0, -0.39465, 2.03211,1.13983, -0.5806, 0.0) * yuv;//gl_FragColor是OpenGL内置的gl_FragColor = vec4(rgb, 1.0);}
);const char *fragNV12_ = GET_STR(precision mediump float;varying vec2 vTextCoord;uniform sampler2D yTexture;uniform sampler2D uvTexture;void main(void){vec3 yuv; vec3 rgb; yuv.x = texture2D(yTexture, vTextCoord.st).r; yuv.y = texture2D(uvTexture, vTextCoord.st).r - 0.5; yuv.z = texture2D(uvTexture, vTextCoord.st).a - 0.5; rgb = mat3( 1,       1,         1, 0,       -0.39465,  2.03211, 1.13983, -0.58060,  0) * yuv; gl_FragColor = vec4(rgb, 1.0); }
);/*** 加载  着色器* @param type     着色器类型* @param shaderSrc  着色源码* @return*/
GLuint LoadShader(GLenum type, const char *shaderSrc) {GLuint shader;GLint compiled;shader = glCreateShader(type);  //  创建 着色器 句柄if (shader == 0) {ALOGE("create shader error");return 0;}// 装载 着色器源码glShaderSource(shader, 1, &shaderSrc, 0);// 编译着色器glCompileShader(shader);// 检测编译状态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, 0, infoLog);ALOGE("%s", infoLog);free(infoLog);}glDeleteShader(shader);return 0;}return shader;
}GLuint LoadProgramYUV(int type){GLint vertexShader = LoadShader(GL_VERTEX_SHADER,vertexShader_);GLint fragShader;if(TYPE_YUV420SP_NV12 == type){fragShader = LoadShader(GL_FRAGMENT_SHADER,fragNV12_);}else{//默认用yuv420pfragShader = LoadShader(GL_FRAGMENT_SHADER,fragYUV420P_);}// 创建渲染程序GLint program = glCreateProgram();if (program == 0){ALOGE("YUV : CreateProgram failure");glDeleteShader(vertexShader);glDeleteShader(fragShader);return 0;}// 先渲染程序中 加入着色器glAttachShader(program,vertexShader);glAttachShader(program,fragShader);// 链接 程序glLinkProgram(program);GLint  status = 0;glGetProgramiv(program,GL_LINK_STATUS,&status);if (status == 0){ALOGE("glLinkProgram failure");glDeleteProgram(program);return 0;}global_Context.glProgram = program;glUseProgram(program);return 1;}void RenderYUVConfig(uint width, uint height, int type) {static float ver[] = {1.0f, -1.0f, 0.0f,-1.0f, -1.0f, 0.0f,1.0f, 1.0f, 0.0f,-1.0f, 1.0f, 0.0f};GLuint apos = (GLuint)(glGetAttribLocation(global_Context.glProgram, "aPosition"));glEnableVertexAttribArray(apos);glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 0, ver);//加入纹理坐标数据static float fragment[] = {1.0f, 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, 1.0f};GLuint aTex = (GLuint)(glGetAttribLocation(global_Context.glProgram, "aTextCoord"));glEnableVertexAttribArray(aTex);glVertexAttribPointer(aTex, 2, GL_FLOAT, GL_FALSE, 0, fragment);ALOGD("can_ok[%s%d] type:%d",__FUNCTION__,__LINE__,type);if(TYPE_YUV420SP_NV12 == type){ALOGD("can_ok[%s%d] type:%d",__FUNCTION__,__LINE__,type);glUniform1i(glGetUniformLocation(global_Context.glProgram, "yTexture"), 0);glUniform1i(glGetUniformLocation(global_Context.glProgram, "uvTexture"), 1);glGenTextures(2, global_Context.mTextures);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,width,height,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL );glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE_ALPHA, width / 2,height / 2,0, GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,NULL );}else{glUniform1i(glGetUniformLocation(global_Context.glProgram, "yTexture"), 0);glUniform1i(glGetUniformLocation(global_Context.glProgram, "uTexture"), 1);glUniform1i(glGetUniformLocation(global_Context.glProgram, "vTexture"), 2);glGenTextures(3, global_Context.mTextures);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,width,height,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,width / 2,height / 2,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[2]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,width / 2,height / 2,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);}
}//平面格式
void RenderYUV420P(GLuint width, GLuint height, unsigned char *buf) {//YglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);glTexSubImage2D(GL_TEXTURE_2D, 0,0, 0,width, height,GL_LUMINANCE, GL_UNSIGNED_BYTE,buf);//UglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);glTexSubImage2D(GL_TEXTURE_2D, 0,0, 0,width / 2, height / 2,GL_LUMINANCE, GL_UNSIGNED_BYTE,buf+width*height);//VglActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[2]);glTexSubImage2D(GL_TEXTURE_2D, 0,0, 0,width / 2, height / 2,GL_LUMINANCE, GL_UNSIGNED_BYTE,buf+width*height*5/4);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//eglSwapBuffers(global_Context.eglDisplay, global_Context.eglSurface);}void RenderNV12(GLuint width, GLuint height, unsigned char * buf){//YglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);glTexSubImage2D(GL_TEXTURE_2D, 0,0, 0,width, height,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);//UVglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);glTexSubImage2D(GL_TEXTURE_2D, 0,0, 0,width/2, height / 2,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,buf+width*height);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void createprograme(){LoadProgramYUV(global_Context.type);RenderYUVConfig(global_Context.mW,global_Context.mH,global_Context.type);
}
void drawFrame(unsigned char* buf, int size) {if(global_Context.type == TYPE_YUV420SP_NV12){ALOGD("[%s%d] render NV12 %dx%d",__FUNCTION__,__LINE__,global_Context.mW,global_Context.mH);RenderNV12(global_Context.mW,global_Context.mH,buf);}else{ALOGD("[%s%d]render YUV420P %dx%d",__FUNCTION__,__LINE__,global_Context.mW,global_Context.mH);RenderYUV420P(global_Context.mW,global_Context.mH,buf);}
}void changeLayout(int width, int height){glViewport(0,0,width,height);
}
//canok 20210528
//头文件:
logs.h
#ifndef __MY_LOGS_HEADER__
#define __MY_LOGS_HEADER__
#ifdef __cplusplus
extern "C" {
#endif
#include <android/log.h>
// 宏定义类似java 层的定义,不同级别的Log LOGI, LOGD, LOGW, LOGE, LOGF。 对就Java中的 Log.i log.d
#define LOG_TAG    "hpc -- JNILOG" // 这个是自定义的LOG的标识
//#undef LOG // 取消默认的LOG
#define ALOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define ALOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define ALOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define ALOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define ALOGF(...)  __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)#ifdef __cplusplus
}
#endif#endifOpenGLfuncs.h
#ifndef __OPENGLFUNCS_HH
#define __OPENGLFUNCS_HH
#include<jni.h>
#include<android/native_window.h>
#include<android/native_window_jni.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include<EGL/egl.h>
#include<EGL/eglext.h>#define TYPE_YUV420P 19
#define TYPE_YUV420SP_NV12 21
void mygl_start(ANativeWindow *pWnid,char*file_in, int type,int w, int h,int fps);
void mygl_stop();//下面函数供其它模块输入渲染数据
void mygl_render(unsigned char *buf,int size);
#endifGlprograme.h
#ifndef __GLPRAGRAME2__H__
#define __GLPRAGRAME2__H__
#include <jni.h>
#include<EGL/egl.h>
#include<EGL/eglext.h>
#include <android/log.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>#include <pthread.h>
#include <unistd.h>
struct S_CONTEXT{GLint glProgram;GLuint mTextures[3];int mW;int mH;int type;int mfps;ANativeWindow *nwin ;unsigned char* buf;int buflen;pthread_mutex_t bufMutex;pthread_mutex_t mCondMutex;pthread_cond_t mCond;pthread_t mThread;char *file_in;
} ;
extern struct S_CONTEXT global_Context;
void createprograme();
void drawFrame(unsigned char* buf, int size) ;
#endif

添加上java 代码:

package com.example.opengl_native;import android.util.Log;
import android.view.Surface;
public class NativeOpengl {private static final String TAG = "NativeOpengl";private Surface mSurface;NativeOpengl(){}private native void native_start(Surface surface,String file_in,int type,int w, int h, int fps);private native void native_stop();static {System.loadLibrary("NativeOpengl");}public void play(Surface surface,String file_in,int type,int w, int h,int fps){String colortype="unsupport_type";if(type ==21){colortype = "NV12_YUV420SP";}else if(type==19){colortype = "YUV420P";}Log.d(TAG, "play: "+surface+"file_in:"+file_in+" type,"+colortype+",size:"+w+"x"+h+"@"+fps);mSurface = surface;native_start(surface,file_in,type,w,h,fps);}}
package com.example.opengl_native;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private Button mButtonPlay;private SurfaceView mView;private NativeOpengl mOpengl;private Surface mSurface;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);verifyStoragePermissions(this);mView = findViewById(R.id.surface);SurfaceHolder holder = mView.getHolder();holder.addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stubLog.i("SURFACE","destroyed");}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubLog.i("SURFACE","create");mSurface =  holder.getSurface();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {// TODO Auto-generated method stub}});mButtonPlay = findViewById(R.id.button);mButtonPlay.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(mSurface!=null && mOpengl==null) {mOpengl = new NativeOpengl();mOpengl.play(mSurface,"/storage/emulated/0/960X544_nv12.yuv",21,960,544,30);}}});}private static final int REQUEST_EXTERNAL_STORAGE = 1;private static String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};public static boolean verifyStoragePermissions(Activity activity) {if(Build.VERSION.SDK_INT < 23){Log.d(TAG, "verifyStoragePermissions: <23");return true;}// Check if we have write permissionint permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.RECORD_AUDIO);if (permission != PackageManager.PERMISSION_GRANTED) {// We don't have permission so prompt the userActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);return false;}else{return true;}}
}

Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := NativeOpengl
LOCAL_SRC_FILES :=  Openglfuncs.cpp \GlPragrame.cpp \jni_NativeOpengl.cppLOCAL_LDLIBS += -lGLESv2 -lEGL \-llog -landroid
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPESinclude $(BUILD_SHARED_LIBRARY)
布局:
<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:layout_editor_absoluteX="73dp"tools:layout_editor_absoluteY="168dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"tools:layout_editor_absoluteX="73dp"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="播放" /></LinearLayout><SurfaceViewandroid:id="@+id/surface"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>清单文件:
package="com.example.opengl_native"><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:requestLegacyExternalStorage="true"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Opengl_native"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>

Android native 层使用opengl渲染YUV420p和NV12相关推荐

  1. android transact,Android Native层Binder.transact()函数调用 Binder.onTransact() 函数失败分析...

    Q:Android Native层Binder.transact()函数调用 Binder.onTransact() 函数失败? 在Android Native层调用Camera.h中的api实现一个 ...

  2. Android Native层

    android native层是 相对于Java 层的底层,一般用c++开发 Java框架层就是常说的Framework,这层里东西很多也很复杂,比如说主要的一些系统服务如ActivityManage ...

  3. android native层进程通信

    我们需要在android native层实现两个进程间的通信,这两个进程一个是native c 进程,一个是apk,两者需要传递一张几百K的图片,natice c 程序写该图片,apk读该图片,而且, ...

  4. Android平台上基于OpenGl渲染yuv视频

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 更多音视频开发文章,请看:音视频开发专栏 介绍一个自己刚出炉的音视频播放录制开源项目 前言 这是我音视频专栏的第一篇实例解析,也算是 ...

  5. android native java_在Android Native层中创建Java虚拟机实例

    前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...

  6. 在Android Native层中创建Java虚拟机实例

    前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...

  7. Android native层Hander原理分析

    目录 概述 源 自实现 下面简单叙述其原理: 如何使用: 注意问题: 概述 本篇探究 Android framework native层多媒体库中的 AHandler+ALooper+AMessage ...

  8. Android Native层错误调试

    前言 Android系统对于Native(C/C++)应用程序的调试手段比单纯的linux系统coredump文件与gdb结合调试的手段.但是Android系统的天然不支持这种调试方式,其在内核中就没 ...

  9. android 渲染yuv数据,Android opengl渲染yuv420例子

    [实例简介] Android下使用OpenGL渲染yuv420p图像并显示.例子中提供了两种类型,一种使用GLSurfaceView在onDrawframe中调用native方法绘制,另外一种使用EG ...

最新文章

  1. Linux下OpenSSL的安装与使用
  2. 阅读react-redux源码 - 零
  3. Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题
  4. java财务对账系统设计_对账系统设计
  5. 细说php完美分页类
  6. 【软件工程】二、需求分析——怎么提需求?,怎么写需求?
  7. 算法音乐往事:二次元女神“初音未来”诞生记
  8. android toast样式 最新,Android 五种不同样式Toast
  9. 从百度指数到微信指数,我们正进入“数据世界”
  10. MSP430F5529-基于ATK-IMU901角度传感器在UART的串口通信于数据显示
  11. 面试官:为什么选择做测试?我对测试特别感兴趣,我喜欢软件测试
  12. windows7 32位系统下好用的一款视频制作工具--Windows live影音制作
  13. LINUX常用的字符串,常用linux 命令 -字符串相关
  14. iOS 的一种设计模式 类别 catagory
  15. 一台手机=半套房?VERTU唐卡手机拍出48.3万
  16. 农业种植大数据平台构建
  17. 毕业前三年如何拿到好绩效
  18. YTU 2451: 股市风云
  19. 使用钉钉创建企业内部机器人
  20. 工业和信息化部关于开展纵深推进APP侵害用户权益专项整治行动的通知(工信部信管函〔2020〕164号)

热门文章

  1. (计算机组成原理)虚拟存储器
  2. Python 多重循环
  3. 快速点击,多次请求同一接口,怎样让结果渲染为最后一次请求的结果
  4. JVM导出dump文件
  5. 编程语言用Python实现九九乘法表的几种方式,入门必备案例!超级简单!
  6. C语言的格式输出 C语言中字符的作用:
  7. Moba连接好虚拟机后怎么创建文件夹和解压配置jdk、hadoop环境变量
  8. 认知科学和设计领域中的 Affordance 是什么?
  9. pdfbox 解析 PDF转HTML、Text文件
  10. 7-33 有理数加法