OpenGL是一个图形API,并不是一个独立的平台。包含了一系列可以操作图形、图像的函数。基于Texture纹理强大的功能,本篇文章实现Android OpenGL Texture C++ 预览Camera视频流的功能。
项目github地址:https://github.com/wangyongyao1989/WyFFmpeg
一、代码实现步骤及图示预览:
二、Camera数据获取:
Android Camera可以获取图片数据的视频流信息。
1、 打开Camera后,获取ImageReader读取视频对应的相片数据。
/*** Opens the camera.*/@SuppressLint({"WrongConstant", "MissingPermission"})public void openCamera() {if (checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {return;}mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, IMAGE_BUFFER_SIZE);mImageReader.setOnImageAvailableListener(mVideoCapture, mBackgroundHandler);Log.i(TAG, "openCamera");CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);try {if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {throw new RuntimeException("Time out waiting " +"to lock camera opening.");}manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "Cannot " +"access the camera " + e);} catch (InterruptedException e) {throw new RuntimeException("Interrupted while " +"trying to lock camera opening.", e);}}
2、在监听ImageReader.OnImageAvailableListener中读取ImageReader并转换成YUV_420_888的数据。
@Overridepublic void onImageAvailable(ImageReader imageReader) {Image image = imageReader.acquireLatestImage();if (image != null) {if (mPreviewFrameHandler != null) {mPreviewFrameHandler.onPreviewFrame(YUV_420_888_data(image), image.getWidth(), image.getHeight());}image.close();}}private static byte[] YUV_420_888_data(Image image) {final int imageWidth = image.getWidth();final int imageHeight = image.getHeight();final Image.Plane[] planes = image.getPlanes();byte[] data = new byte[imageWidth * imageHeight *ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];int offset = 0;for (int plane = 0; plane < planes.length; ++plane) {final ByteBuffer buffer = planes[plane].getBuffer();final int rowStride = planes[plane].getRowStride();// Experimentally, U and V planes have |pixelStride| = 2, which// essentially means they are packed.final int pixelStride = planes[plane].getPixelStride();final int planeWidth = (plane == 0) ? imageWidth : imageWidth / 2;final int planeHeight = (plane == 0) ? imageHeight : imageHeight / 2;if (pixelStride == 1 && rowStride == planeWidth) {// Copy whole plane from buffer into |data| at once.buffer.get(data, offset, planeWidth * planeHeight);offset += planeWidth * planeHeight;} else {// Copy pixels one by one respecting pixelStride and rowStride.byte[] rowData = new byte[rowStride];for (int row = 0; row < planeHeight - 1; ++row) {buffer.get(rowData, 0, rowStride);for (int col = 0; col < planeWidth; ++col) {data[offset++] = rowData[col * pixelStride];}}// Last row is special in some devices and may not contain the full// |rowStride| bytes of data.// See http://developer.android.com/reference/android/media/Image.Plane.html#getBuffer()buffer.get(rowData, 0, Math.min(rowStride, buffer.remaining()));for (int col = 0; col < planeWidth; ++col) {data[offset++] = rowData[col * pixelStride];}}}return data;}
三、设置OpenGL的使用场景:
GLTextureCPlusVideoPlayerView继承GLSurfcaeView,实现GLSurfcaeView.Renderer接口:
package com.wangyongyao.glplay.view;import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;import com.wangyongyao.glplay.OpenGLPlayCallJni;
import com.wangyongyao.glplay.camerahelper.camerahelper.CameraDataHelper;
import com.wangyongyao.glplay.camerahelper.camerahelper.CameraDataListener;
import com.wangyongyao.glplay.utils.OpenGLPlayFileUtils;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;/*** author : wangyongyao https://github.com/wangyongyao1989* Create Time : 2024/9/3 23:57* Descibe : MyyFFmpeg com.example.myyffmpeg.utils*/
public class GLTextureCPlusVideoPlayerView extends GLSurfaceViewimplements GLSurfaceView.Renderer, CameraDataListener {private static String TAG = GLTextureCPlusVideoPlayerView.class.getSimpleName();private OpenGLPlayCallJni mJniCall;private Context mContext;private int mWidth;private int mHeight;private CameraDataHelper mCameraHelper;public GLTextureCPlusVideoPlayerView(Context context, OpenGLPlayCallJni jniCall) {super(context);mContext = context;mJniCall = jniCall;init();}public GLTextureCPlusVideoPlayerView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;init();}private void init() {getHolder().addCallback(this);setEGLContextClientVersion(3);setEGLConfigChooser(8, 8, 8, 8, 16, 0);String fragPath = OpenGLPlayFileUtils.getModelFilePath(mContext, "texture_video_play_frament.glsl");String vertexPath = OpenGLPlayFileUtils.getModelFilePath(mContext, "texture_video_play_vert.glsl");String picSrc1 = OpenGLPlayFileUtils.getModelFilePath(mContext, "wall.jpg");mCameraHelper = new CameraDataHelper(getContext(), this);mCameraHelper.startCamera();mJniCall.glTextureVideoPlayCreate(0, vertexPath, fragPath);setRenderer(this);setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);}private void stopCameraPreview() {mCameraHelper.destroy();}public void onDrawFrame(GL10 gl) {if (mJniCall != null) {mJniCall.glTextureVideoPlayRender();}}public void onSurfaceChanged(GL10 gl, int width, int height) {Log.e(TAG, "onSurfaceChanged width:" + width + ",height" + height);if (mJniCall != null) {mJniCall.glTextureVideoPlayInit(null, null, width, height);}mWidth = width;mHeight = height;mCameraHelper.initialize(width, height);}@Overridepublic void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {Log.e(TAG, "onSurfaceCreated:");}@Overridepublic void onPreviewFrame(byte[] yuvData, int width, int height) {mJniCall.glTextureVideoPlayDraw(yuvData, width, height, 90);requestRender();}public void destroyRender() {mJniCall.glTextureVideoPlayDestroy();stopCameraPreview();}}
这里需要注意的是要设置setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY),等待onPreviewFrame回调数据之后在进行Texture的帧渲染。
/*** The renderer only renders* when the surface is created, or when {@link #requestRender} is called.** @see #getRenderMode()* @see #setRenderMode(int)* @see #requestRender()*/public final static int RENDERMODE_WHEN_DIRTY = 0;
四、JNI层把Java的数据传入C++层:
1、Java层实现:
定义的执行流程的顺序方法:glTextureVideoPlayeCreate -> glTextureVideoPlayeInit -> glTextureVideoPlayeCreate -> glTextureVideoPlayeDraw -> glTextureVideoPlayeRender 。
/*********************** OpenGL Texture显示视频********************/public void glTextureVideoPlayCreate(int type, String vertexPath, String fragPath) {native_texture_video_play_create(type, vertexPath, fragPath);}public void glTextureVideoPlayDestroy() {native_texture_video_play_destroy();}public void glTextureVideoPlayInit(Surface surface, AssetManager assetManager, int width, int height) {native_texture_video_play_init(surface, assetManager, width, height);}public void glTextureVideoPlayRender() {native_texture_video_play_render();}public void glTextureVideoPlayDraw(byte[] data, int width, int height, int rotation) {native_texture_video_play_draw(data, width, height, rotation);}public void glTextureVideoPlaySetParameters(int params) {native_texture_video_play_set_parameters(params);}public int glTextureVideoPlayGetParameters() {return native_texture_video_play_get_parameters();}private native void native_texture_video_play_create(int type, String vertexPath, String fragPath);private native void native_texture_video_play_destroy();private native void native_texture_video_play_init(Surface surface, AssetManager assetManager, int width, int height);private native void native_texture_video_play_render();private native void native_texture_video_play_draw(byte[] data, int width, int height, int rotation);private native void native_texture_video_play_set_parameters(int params);private native int native_texture_video_play_get_parameters();
2、JNI层实现:
/*********************** OpenGL Texture预览Camera视频********************/
extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_creat(JNIEnv *env, jobject thiz, jint type,jstring vertex,jstring frag) {const char *vertexPath = env->GetStringUTFChars(vertex, nullptr);const char *fragPath = env->GetStringUTFChars(frag, nullptr);if (textureVideoRender == nullptr)textureVideoRender = new OpenglesTexureVideoRender();textureVideoRender->setSharderPath(vertexPath, fragPath);env->ReleaseStringUTFChars(vertex, vertexPath);env->ReleaseStringUTFChars(frag, fragPath);
}extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_destroy(JNIEnv *env, jobject thiz) {}extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_init(JNIEnv *env, jobject thiz,jobject surface,jobject assetManager,jint width,jint height) {if (textureVideoRender != nullptr) {ANativeWindow *window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;auto *aAssetManager = assetManager ? AAssetManager_fromJava(env, assetManager) : nullptr;textureVideoRender->init(window, aAssetManager, (size_t) width, (size_t) height);}
}extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_render(JNIEnv *env, jobject thiz) {if (textureVideoRender != nullptr) {textureVideoRender->render();}
}extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_draw(JNIEnv *env, jobject obj, jbyteArray data, jint width, jint height,jint rotation) {jbyte *bufferPtr = env->GetByteArrayElements(data, nullptr);jsize arrayLength = env->GetArrayLength(data);if (textureVideoRender != nullptr) {textureVideoRender->draw((uint8_t *) bufferPtr, (size_t) arrayLength, (size_t) width,(size_t) height,rotation);}env->ReleaseByteArrayElements(data, bufferPtr, 0);
}extern "C"
JNIEXPORT void JNICALL
cpp_texture_video_play_setParameters(JNIEnv *env, jobject thiz, jint p) {if (textureVideoRender != nullptr) {textureVideoRender->setParameters((uint32_t) p);}}extern "C"
JNIEXPORT jint JNICALL
cpp_texture_video_play_getParameters(JNIEnv *env, jobject thiz) {if (textureVideoRender != nullptr) {textureVideoRender->getParameters();}return 0;}static const JNINativeMethod methods[] = {/*********************** OpenGL Texture显示视频********************/{"native_texture_video_play_create", "(I""Ljava/lang/String;""Ljava/lang/String;)V", (void *) cpp_texture_video_play_creat},{"native_texture_video_play_destroy", "()V", (void *) cpp_texture_video_play_destroy},{"native_texture_video_play_init", "(Landroid/view/Surface;""Landroid/content/res""/AssetManager;II)V", (void *) cpp_texture_video_play_init},{"native_texture_video_play_render", "()V", (void *) cpp_texture_video_play_render},{"native_texture_video_play_draw", "([BIII)V", (void *) cpp_texture_video_play_draw},{"native_texture_video_play_set_parameters", "(I)V", (void *) cpp_texture_video_play_setParameters},{"native_texture_video_play_get_parameters", "()I", (void *) cpp_texture_video_play_getParameters},};// 定义注册方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {LOGD("动态注册");JNIEnv *env;if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {LOGD("动态注册GetEnv fail");return JNI_ERR;}// 获取类引用jclass clazz = env->FindClass(rtmp_class_name);// 注册native方法jint regist_result = env->RegisterNatives(clazz, methods,sizeof(methods) / sizeof(methods[0]));if (regist_result) { // 非零true 进ifLOGE("动态注册 fail regist_result = %d", regist_result);} else {LOGI("动态注册 success result = %d", regist_result);}return JNI_VERSION_1_6;
}
五、C++层的OpenGL Texture渲染实现:
这里OpenGL相关的代码基于我的github项目:GitHub - wangyongyao1989/AndroidLearnOpenGL: OpenGL基础及运用
抽取过来进行实现的。
感兴趣的可以去阅读我的关于OpenGL的相关的博客:https://blog.csdn.net/wangyongyao1989/category_6943979.html?spm=1001.2014.3001.5482
1、着色器程序:
着色器程序是GLSL的文件,把存放文件夹地址传入C++层的OpenGLShader.cpp中。
- texture_video_play_vert.glsl顶点着色器:
#version 320 esout vec2 v_texcoord;in vec4 position;
in vec2 texcoord;void main() {v_texcoord = texcoord;gl_Position = position;
}
- texture_video_play_fragment.glsl片段着色器:
#version 320 esprecision mediump float;in vec2 v_texcoord;uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;out vec4 gl_FragColor;void main() {float y, u, v, r, g, b;y = texture(s_textureY, v_texcoord).r;u = texture(s_textureU, v_texcoord).r;v = texture(s_textureV, v_texcoord).r;u = u - 0.5;v = v - 0.5;r = y + 1.403 * v;g = y - 0.344 * u - 0.714 * v;b = y + 1.770 * u;gl_FragColor = vec4(r, g, b, 1.0);}
2、OpenGLShader.cpp中着色器程序编译、连接、使用。
//
// Created by MMM on 2024/8/8.
//#include "OpenGLShader.h"GLuint
OpenGLShader::createProgram() {vertexShader = loadShader(GL_VERTEX_SHADER, gVertexShaderCode);LOGI("=====gVertexShaderCode :%s", gVertexShaderCode);LOGI("======gFragmentShaderCode :%s", gFragmentShaderCode);if (!vertexShader) {checkGlError("loadShader GL_VERTEX_SHADER");return 0;}fraShader = loadShader(GL_FRAGMENT_SHADER, gFragmentShaderCode);if (!fraShader) {checkGlError("loadShader GL_FRAGMENT_SHADER");return 0;}shaderId = glCreateProgram(); //创建一个着色程序对象if (shaderId) {glAttachShader(shaderId, vertexShader); //把着色器附加到了程序对象上checkGlError("glAttachShader");glAttachShader(shaderId, fraShader);checkGlError("glAttachShader");glLinkProgram(shaderId); //链接程序对象GLint linkStatus = GL_FALSE;glGetProgramiv(shaderId, GL_LINK_STATUS, &linkStatus); //检测链接着色器程序是否失败if (linkStatus != GL_TRUE) {GLint bufLength = 0;glGetProgramiv(shaderId, GL_INFO_LOG_LENGTH, &bufLength);if (bufLength) {char *buf = (char *) malloc(bufLength);if (buf) {glGetProgramInfoLog(shaderId, bufLength, NULL, buf);LOGE("Could not link shaderId:\n%s\n", buf);free(buf);}}glDeleteProgram(shaderId); //shaderId = 0;}}return shaderId;
}/*** 加载着色器* @param shaderType* @param pSource* @return*/
GLuint OpenGLShader::loadShader(GLenum shaderType, const char *pSource) {GLuint shader = glCreateShader(shaderType); //创建着色器if (shader) {glShaderSource(shader, 1, &pSource, NULL); //着色器源码附加到着色器对象上glCompileShader(shader); //编译着着色器GLint compiled = 0;glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);if (!compiled) {GLint infoLen = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);if (infoLen) {char *buf = (char *) malloc(infoLen);if (buf) {glGetShaderInfoLog(shader, infoLen, NULL, buf);LOGE("Could not compile shader %d:\n%s\n",shaderType, buf);free(buf);}glDeleteShader(shader); //删除着色器对象shader = 0;}}}return shader;
}bool OpenGLShader::getSharderPath(const char *vertexPath, const char *fragmentPath) {ifstream vShaderFile;ifstream fShaderFile;// ensure ifstream objects can throw exceptions:vShaderFile.exceptions(ifstream::failbit | ifstream::badbit);fShaderFile.exceptions(ifstream::failbit | ifstream::badbit);try {// open filesvShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);stringstream vShaderStream, fShaderStream;// read file's buffer contents into streamsvShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// close file handlersvShaderFile.close();fShaderFile.close();// convert stream into stringvertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();}catch (ifstream::failure &e) {LOGE("Could not getSharderPath error :%s", e.what());return false;}gVertexShaderCode = vertexCode.c_str();gFragmentShaderCode = fragmentCode.c_str();return true;
}void OpenGLShader::printGLString(const char *name, GLenum s) {const char *v = (const char *) glGetString(s);LOGI("OpenGL %s = %s\n", name, v);
}void OpenGLShader::checkGlError(const char *op) {for (GLint error = glGetError(); error; error = glGetError()) {LOGI("after %s() glError (0x%x)\n", op, error);}
}OpenGLShader::~OpenGLShader() {if (vertexShader) {glDeleteShader(vertexShader);}if (fraShader) {glDeleteShader(fraShader);}vertexCode.clear();fragmentCode.clear();gVertexShaderCode = nullptr;gFragmentShaderCode = nullptr;
}OpenGLShader::OpenGLShader() {}
3、OpenGLTextureVideoRender.cpp进行YUV的Texture渲染:
大致流程为:createProgram() -> createTextures() -> draw() -> render()。
- createProgram()创建程序,获取着色器中的输入顶点坐标、输入纹理顶点坐标及uniform的参数:
int
OpenglesTexureVideoRender::createProgram() {m_program = lightColorShader->createProgram();m_vertexShader = lightColorShader->vertexShader;m_pixelShader = lightColorShader->fraShader;LOGI("OpenglesTexureVideoRender createProgram m_program:%d", m_program);if (!m_program) {LOGE("Could not create program.");return 0;}//Get Uniform Variables Locationm_vertexPos = (GLuint) glGetAttribLocation(m_program, "position");m_textureYLoc = glGetUniformLocation(m_program, "s_textureY");m_textureULoc = glGetUniformLocation(m_program, "s_textureU");m_textureVLoc = glGetUniformLocation(m_program, "s_textureV");m_textureLoc = (GLuint) glGetAttribLocation(m_program, "texcoord");return m_program;
}
- createTextures()分别创建YUV三个通道的纹理:
bool OpenglesTexureVideoRender::createTextures() {auto widthY = (GLsizei) m_width;auto heightY = (GLsizei) m_height;glActiveTexture(GL_TEXTURE0);glGenTextures(1, &m_textureIdY);glBindTexture(GL_TEXTURE_2D, m_textureIdY);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);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthY, heightY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,nullptr);if (!m_textureIdY) {
// check_gl_error("Create Y texture");return false;}GLsizei widthU = (GLsizei) m_width / 2;GLsizei heightU = (GLsizei) m_height / 2;glActiveTexture(GL_TEXTURE1);glGenTextures(1, &m_textureIdU);glBindTexture(GL_TEXTURE_2D, m_textureIdU);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);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthU, heightU, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,nullptr);if (!m_textureIdU) {
// check_gl_error("Create U texture");return false;}GLsizei widthV = (GLsizei) m_width / 2;GLsizei heightV = (GLsizei) m_height / 2;glActiveTexture(GL_TEXTURE2);glGenTextures(1, &m_textureIdV);glBindTexture(GL_TEXTURE_2D, m_textureIdV);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);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthV, heightV, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,nullptr);if (!m_textureIdV) {
// check_gl_error("Create V texture");return false;}return true;
}
-
draw()分离出YUV的每个通道的数据集:
void OpenglesTexureVideoRender::draw(uint8_t *buffer, size_t length, size_t width, size_t height,float rotation) {m_length = length;m_rotation = rotation;video_frame frame{};frame.width = width;frame.height = height;frame.stride_y = width;frame.stride_uv = width / 2;frame.y = buffer;frame.u = buffer + width * height;frame.v = buffer + width * height * 5 / 4;updateFrame(frame);
}void OpenglesTexureVideoRender::updateFrame(const video_frame &frame) {m_sizeY = frame.width * frame.height;m_sizeU = frame.width * frame.height / 4;m_sizeV = frame.width * frame.height / 4;if (m_pDataY == nullptr || m_width != frame.width || m_height != frame.height) {m_pDataY = std::make_unique<uint8_t[]>(m_sizeY + m_sizeU + m_sizeV);m_pDataU = m_pDataY.get() + m_sizeY;m_pDataV = m_pDataU + m_sizeU;isProgramChanged = true;}m_width = frame.width;m_height = frame.height;if (m_width == frame.stride_y) {memcpy(m_pDataY.get(), frame.y, m_sizeY);} else {uint8_t *pSrcY = frame.y;uint8_t *pDstY = m_pDataY.get();for (int h = 0; h < m_height; h++) {memcpy(pDstY, pSrcY, m_width);pSrcY += frame.stride_y;pDstY += m_width;}}if (m_width / 2 == frame.stride_uv) {memcpy(m_pDataU, frame.u, m_sizeU);memcpy(m_pDataV, frame.v, m_sizeV);} else {uint8_t *pSrcU = frame.u;uint8_t *pSrcV = frame.v;uint8_t *pDstU = m_pDataU;uint8_t *pDstV = m_pDataV;for (int h = 0; h < m_height / 2; h++) {memcpy(pDstU, pSrcU, m_width / 2);memcpy(pDstV, pSrcV, m_width / 2);pDstU += m_width / 2;pDstV += m_width / 2;pSrcU += frame.stride_uv;pSrcV += frame.stride_uv;}}isDirty = true;
}
-
render()每个渲染时更新YUV三个纹理:
void OpenglesTexureVideoRender::render() {
// LOGI("OpenglesTexureVideoRender render");glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glClearColor(0.0f, 0.0f, 0.0f, 1.0f);if (!updateTextures() || !useProgram()) return;glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}bool OpenglesTexureVideoRender::updateTextures() {if (!m_textureIdY && !m_textureIdU&& !m_textureIdV && !createTextures()) return false;// LOGI("OpenglesTexureVideoRender updateTextures");if (isDirty) {glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_textureIdY);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width, (GLsizei) m_height, 0,GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataY.get());glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, m_textureIdU);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width / 2, (GLsizei) m_height / 2,0,GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataU);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, m_textureIdV);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width / 2, (GLsizei) m_height / 2,0,GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataV);isDirty = false;return true;}return false;
}