《Ndk中使用Mediacode解码》
《android mediacodec 编码demo(java)》
《NDK中使用mediacodec编码h264》
《Android native 层使用opengl渲染YUV420p和NV12》
《android 使用NativeWindow渲染RGB视频》
《opengl 叠加显示文字》
《android studio 编译freeType》
《最原始的yuv图像叠加文字的实现--手动操作像素》
续上一篇《Android native 层使用opengl渲染YUV420p和NV12》
这里想在渲染之前,叠加显示帧率分辨率等等实时的信息。
大概有如下方案(个人见解):
一: 渲染之前直接修改原raw帧,把文字的图形叠加到raw帧图像上去。
1.0 手动修改像素的方式:获取到文字的位图之后,最好也是同我们渲染的视频帧数据一样也是yuv格式的,然后直接操作渲染之前的yuv视频帧,将文字图像数据部分逐个像素逐个像素地赋值到视频帧数据里面去就完成了覆盖。 这是最原始的手动方式,看起来cpu运算量也不小,还要求文字的图像和视频帧数据格式一致。
2.0 搞个ffmpeg,利用里面的drawtext 滤镜画上去就行了。(当然需要移植ffmpeg)
二:不修改视频raw帧数据,额外添加一个纹理给文字图像使用,在opengl显卡程序里面利用显卡的计算能力,将渲染之前的rgb数据和文字图像的这个纹理混合,再渲染。
这里使的第二个方式,不修改视频原始yuv数据,在opengl里面混合。
怎么获取文字的图形?
有书中介绍,回调java层的接口,利用android的api,canvs绘制文字得到图形,这里并不想这么做,感觉又依赖了太多的东西,只是想纯粹的c++实现。所以,移植了个freeType字库,直接在c++层利用freeType字库将字符串转换成一个 rgba32的序列文件。
如下代码的原理,先准备一个大的“低图”(画板),对每一个字符,都需要从字库里面单独提取出其bitmap图,然后我们遍历这个图的像素,更具每一个像素的不透明度,来赋值底图指定位置的像素值,这个时候我们就可以自定义颜色,文字背景就设置alpth--不透明度值为0. 每次更新完一个字符的图,就更新下在“底图”上的位置,就类似于画板。(如下代码有点缺陷,还没有支持到unicode 字符,只是支持ASCII)
怎么在opengl里面混合?
我们希望文字的背景是透明的,不会产生一个方块,就利用alpth不透明度这个分量,混合的时候rgb的值需要有这个alpth加权。(如下代码,还有缺陷,当前准备的文字图像纹理和视频纹理必须是大小一致的,对每一个片段都会进行混合的处理,这样其实比较浪费性能,比较文字部分只占整个图像的一小块)
两张图片叠加,文字的在上层,不透明度为0的像素就不显示,漏出底层的图像,会photoshop就很好理解,和里面的图层的概念一样。
上代码:
使用freeTyepe获取文字图形代码:
DrawText.cpp
//
// Created by xiancan.wang on 6/3/21.
//
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "DrawText.h"
#include "logs.h"
struct freetype_data{
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_Matrix matrix; /* transformation matrix */
FT_Vector pen; /* untransformed origin */
}globalContext;
/* Replace this function with something useful. */
void draw_bitmap( FT_Bitmap* bitmap,unsigned char*out,
FT_Int x,
FT_Int y, int w, int h,
char r,char g,char b )
{
//rgba
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
int pixByte=4;
// for ( i = x, p = 0; i < x_max; i++, p++ )
// {//行
// for ( j = y, q = 0; j < y_max; j++, q++ )
// {//列
// if ( i < 0 || j < 0 ||i >= w || j >= h )
// continue;
//
// out[(j*w+i)*pixByte+3] = bitmap->buffer[q * bitmap->width + p];
// //rgba
// out[(j*w+i)*pixByte] = r;
// out[(j*w+i)*pixByte+1] = g;
// out[(j*w+i)*pixByte+2] =b;
// }
// }
//先列后行,访问连续内存,对于现在的内存来说基本上也没有提高效率
for ( j = y, q = 0; j < y_max; j++, q++ ) {
//列
for (i = x, p = 0; i < x_max; i++, p++) {
//行
if (i < 0 || j < 0 || i >= w || j >= h)
continue;
#if 0
out[(j * w + i) * pixByte + 3] = bitmap->buffer[q * bitmap->width + p];
//rgba
out[(j * w + i) * pixByte] = r;
out[(j * w + i) * pixByte + 1] = g;
out[(j * w + i) * pixByte + 2] = b;
#endif
unsigned char alth_vale = bitmap->buffer[q * bitmap->width + p];
double alth =(alth_vale*1.0)/255;
out[(j*w+i)*pixByte] = r*alth;
out[(j*w+i)*pixByte+1] = g*alth;
out[(j*w+i)*pixByte+2] = b*alth;
out[(j*w+i)*pixByte+3] = alth_vale;
}
}
}
int DT_init(const char*fontfile){
FT_Error error;
error = FT_Init_FreeType( &globalContext.library ); /* initialize library */
/* error handling omitted */
if(error!=0){
ALOGD("FT_Init_FreeType erro:%d",error);
}
error = FT_New_Face( globalContext.library, fontfile, 0, &globalContext.face ); /* create face object */
/* error handling omitted */
if(error!=0){
ALOGD("FT_New_Face erro:%d",error);
}
#if 0
/* use 50pt at 100dpi */
error = FT_Set_Char_Size( globalContext.face, 80 * 64, 0,
100, 0 ); /* set character size */
/* pixels = 50 /72 * 100 = 69 */
#else
//FT_Set_Pixel_Sizes(globalContext.face, 24, 0);
#endif
globalContext.slot = globalContext.face->glyph;
/* set up matrix */
double angle = ( 0.0 / 360 ) * 3.14159 * 2; /* use 25 degrees */
globalContext.matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
globalContext.matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
globalContext.matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
globalContext.matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
return error;
}
int DT_deinit(){
FT_Done_Face ( globalContext.face );
FT_Done_FreeType( globalContext.library);
return 0;
}
int DT_GetBitmapFromText (const char*text,unsigned char* bitmapOut,int w, int h,int fontSize){
int size = strlen(text);
// FT_Set_Pixel_Sizes(globalContext.face, 24, 0);
FT_Set_Char_Size( globalContext.face, 64 * fontSize, 0,
100, 0 ); /* set character size */
/* start at (0,40) relative to the upper left corner */
globalContext.pen.x = 0 * 64;
globalContext.pen.y = 0 * 64;
FT_Error error;
int i =0;
for ( i = 0; i < size; i++ )
{
/* set transformation */
FT_Set_Transform( globalContext.face, &globalContext.matrix, &globalContext.pen );
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( globalContext.face, text[i], FT_LOAD_RENDER );
if ( error )
continue; /* ignore errors */
/* now, draw to our target surface (convert position) */
FT_Bitmap * btMap = &globalContext.slot->bitmap;
printf("[%d]%c x:%d y:%d w:%d, h:%d\n",i,text[i],globalContext.slot->bitmap_left,globalContext.slot->bitmap_top,btMap->width,btMap->rows);
// draw_bitmap( &globalContext.slot->bitmap,
// bitmapOut,
// globalContext.slot->bitmap_left,
//每一个字母顶部对齐
// 0,w,h,0xff,0xff,0x00);
// draw_bitmap( &globalContext.slot->bitmap,
// bitmapOut,
// globalContext.slot->bitmap_left,
// //底部对齐
// (h-globalContext.slot->bitmap_top),w,h,0xff,0xff,0x00);
draw_bitmap( &globalContext.slot->bitmap,
bitmapOut,
globalContext.slot->bitmap_left,
//底部对齐 向上平移
// (h-globalContext.slot->bitmap_top)-(h-fontSize),
fontSize-globalContext.slot->bitmap_top,
w,h,0xff,0xff,0x00);//指定颜色
/* increment pen position */
globalContext.pen.x += globalContext.slot->advance.x;
//globalContext.pen.y += globalContext.slot->advance.y;
}
int imgW= globalContext.slot->bitmap_left+ globalContext.slot->bitmap.width;
return imgW;
}
unsigned char * DT_GetBitmapFromTextAutoWidth(const char*text, int fontSize, int *w, int *h){
int len = strlen(text);
int tempW=fontSize*len*2;
int tempH=fontSize;
// some img wider than fontSize, so we just alloc double
unsigned char *img=(unsigned char*)malloc(fontSize*len*2);
int imgW= DT_GetBitmapFromText(text,img,tempW,tempH,fontSize);
if(w){
*w=imgW;
}
if(h){
*h=tempH;
}
return img;
}
//DrawText.h
//
// Created by xiancan.wang on 6/3/21.
//
#ifndef TEST_CMAKE_DRAWTEXT_H
#define TEST_CMAKE_DRAWTEXT_H
int DT_init(const char*fontfile);
int DT_deinit();
int DT_GetBitmapFromText (const char*text,unsigned char* bitmapOut,int w, int h,int fontSize);
#endif //TEST_CMAKE_DRAWTEXT_H
opengl渲染:
GlPrograme.cpp
//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;//采样器
uniform sampler2D infoTexture;
void main() {
vec3 yuv;
vec3 rgb;
vec4 info;
//分别取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;
//混合文字
info = texture2D(infoTexture, vTextCoord);
rgb.x = info.r*info.a + (1.0 - info.a)*rgb.x;
rgb.y = info.g*info.a + (1.0 - info.a)*rgb.y;
rgb.z = info.b*info.a + (1.0 - info.a)*rgb.z;
//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;
uniform sampler2D infoTexture;
void main(void)
{
vec3 yuv;
vec3 rgb;
vec4 info;
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;
info = texture2D(infoTexture, vTextCoord);
rgb.x = info.r*info.a + (1.0 - info.a)*rgb.x;
rgb.y = info.g*info.a + (1.0 - info.a)*rgb.y;
rgb.z = info.b*info.a + (1.0 - info.a)*rgb.z;
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{
//默认用yuv420p
fragShader = 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;
}
enum LAYOUT {
LAYOUT_2x2,
LAYOUT_3x3,
LAYOUT_6,
};
void GetLayout();
void RenderInfoConfig(int width,int height){
glUniform1i(glGetUniformLocation(global_Context.glProgram, "infoTexture"), 3);
glGenTextures(1, &global_Context.mInfoTexture);
glBindTexture(GL_TEXTURE_2D, global_Context.mInfoTexture);
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_RGBA,
width ,
height ,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL
);
}
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
};
// static float ver[] = {
// 0.0f, -1.0f, 0.0f,
// -1.0f, -1.0f, 0.0f,
// 0.0f, 0.0f, 0.0f,
// -1.0f, 0.0f, 0.0f
//
// //这里加入画info 纹理的坐标
//
// };
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
};
// static float fragment[] = {
// 2.0f, 0.0f,
// 0.0f, 0.0f,
// 2.0f, 2.0f,
// 0.0f, 2.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 RendInfo(GLuint width, GLuint height, unsigned char *buf){
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, global_Context.mInfoTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0,
width, height,
GL_RGBA, GL_UNSIGNED_BYTE,
buf);
}
//平面格式
void RenderYUV420P(GLuint width, GLuint height, unsigned char *buf) {
//Y
glActiveTexture(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);
//U
glActiveTexture(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);
//V
glActiveTexture(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){
//Y
glActiveTexture(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);
//UV
glActiveTexture(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);
RenderInfoConfig(global_Context.mW,global_Context.mH);
}
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 updateInfo(unsigned char* buf, int size){
RendInfo(global_Context.mW,global_Context.mH,buf);
}
void changeLayout(int width, int height){
glViewport(0,0,width,height);
}
GlPrograme.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];
GLuint mInfoTexture;
int mW;
int mH;
int type;
int mfps;
ANativeWindow *nwin ;
unsigned char* buf;
int buflen;
unsigned char*infobuf;
int infobuflen;
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) ;
void updateInfo(unsigned char* buf, int size);
#endif
EGL 环境
Openglfuncs.cpp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "logs.h"
#include "GlPragrame.h"
#include "Openglfuncs.h"
#include "DrawText.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;
int64_t getNowUs(){
#if 1
timeval tv;
gettimeofday(&tv, 0);
return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec;
#else
return ALooper::GetNowUs();
#endif
}
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配置display
EGLConfig 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);
global_Context.infobuflen=global_Context.mW*global_Context.mH*4;
global_Context.infobuf = (unsigned char*)malloc( global_Context.infobuflen);
memset(global_Context.infobuf,0,global_Context.infobuflen);
int error=0;
error = DT_init("/storage/emulated/0/hwyuanti.TTF");
ALOGD("DT_init:%d",error);
int i=0,j=0;
for(i=0;i<100;i++){
for(j=0;j<global_Context.mW*4;j+=4){
global_Context.infobuf[i*global_Context.mW+j]=0xff;
global_Context.infobuf[i*global_Context.mW+j+3]=0xff;
}
}
int ret =0;
char info[128]={0};
int64_t framecount =0;
while(mbRun){
//这里绘制
if( (ret = fread(global_Context.buf,1,global_Context.buflen,fp)) ==global_Context.buflen ){
int64_t time1=getNowUs();
// snprintf(info,sizeof(info),"time:%ld",getNowUs());
snprintf(info,sizeof(info),"%ld",framecount++);
memset(global_Context.infobuf,0,global_Context.infobuflen);
DT_GetBitmapFromText(info,global_Context.infobuf,global_Context.mW,global_Context.mH,60);
updateInfo(global_Context.infobuf, global_Context.infobuflen);
int64_t time2 =getNowUs();
ALOGD("take:%ld",time2-time1);
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);
free(global_Context.infobuf);
DT_deinit();
#else
initGles();
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){
eglMakeCurrent(megldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(megldisplay,meglSurface);
eglDestroyContext(megldisplay,meglContext);
eglTerminate(megldisplay);
}
free(global_Context.buf);
#endif
pthread_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 mutex
pthread_join(global_Context.mThread,NULL);
}
Openglfuncs.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);
#endif
Android.mk
LOCAL_PATH := $(call my-dir)
#这一部分是 用来预构建 freetype库的,freeType库可以从官方网站下载,利用其中的cmake编译,得到交叉编译的库,然后在这里 prebuilt
include $(CLEAR_VARS)
LOCAL_MODULE := freetype
LOCAL_SRC_FILES := ./my_libs/$(TARGET_ARCH_ABI)/libfreetyped.so
MY_FREETYPE_SRCDIR=freeType/freetype-2.10.4.tar/freetype-2.10.4/
MY_ABSULATION_DIR=$(LOCAL_PATH)/$(MY_FREETYPE_SRCDIR)
LOCAL_EXPORT_C_INCLUDES := $(MY_ABSULATION_DIR)/ \
$(MY_ABSULATION_DIR)/include/ \
$(MY_ABSULATION_DIR)/include/freetype/ \
$(MY_ABSULATION_DIR)/include/freetype/config/ \
$(MY_ABSULATION_DIR)/include/freetype/internal/
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeOpengl
LOCAL_SRC_FILES := Openglfuncs.cpp \
GlPragrame.cpp \
DrawText.cpp \
jni_NativeOpengl.cpp
LOCAL_SHARED_LIBRARIES :=freetype
LOCAL_LDLIBS += -lGLESv2 -lEGL \
-llog -landroid
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
include $(BUILD_SHARED_LIBRARY)