上一篇是c代码版,想想还是写个java版,毕竟在java上进行很多操作比用c代码编写再编译来的方便很多,比如读取图片,添加gif之类的。
效果就不演示了,和上一篇一样我只加了个黑白滤镜,参考的还是这篇让IjkPlayer支持插入自定义的GPU滤镜,可能是版本问题吧,有些方法是不一样的,我的ijkplayer版本是0.8.4,开始改代码
在ijkmedia/ijksdl/gles2/internal.h的IJK_GLES2_Renderer结构体内添加
typedef struct IJK_GLES2_Renderer { ...//省略代码 int has_filter; void (* func_onCreated)(void); void (* func_onSizeChanged)(int width,int height); void (* func_onDrawFrame)(int textureId); void (* func_onTexcoords)(float *texcoords); void (* func_onVertices)(float *vertices); void (* func_onRelease)(void); int view_width; int view_height; GLuint frame_buffers[1]; GLuint frame_textures[1]; GLfloat texcoords_test[8]; GLfloat vertices_test[8]; } IJK_GLES2_Renderer;在 ijkmedia/ijksdl/ijksdl_vout.h的SDL_Vout和SDL_VoutOverlay结构体内添加
struct SDL_VoutOverlay { ...//省略代码 int has_filter; void (* func_onCreated)(void); void (* func_onSizeChanged)(int width,int height); void (* func_onDrawFrame)(int textureId); void (* func_onTexcoords)(float *texcoords); void (* func_onVertices)(float *vertices); void (* func_onRelease)(void); }; struct SDL_Vout { ...//省略代码 int has_filter; void (* func_onCreated)(void); void (* func_onSizeChanged)(int width,int height); void (* func_onDrawFrame)(int textureId); void (* func_onTexcoords)(float *texcoords); void (* func_onVertices)(float *vertices); void (* func_onRelease)(void); };在 ijkmedia/ijkplayer/android/ijkplayer_android.h添加函数
void ijkmp_android_set_filter(IjkMediaPlayer *mp,int has_filter,void *onCreated,void *onSizeChanged,void *onDrawFrame,void *onTexcoords,void *onVertices,void *onRelease);在 ijkmedia/ijkplayer/android/ijkplayer_android.c内添加函数
void ijkmp_android_set_filter(IjkMediaPlayer *mp,int has_filter,void *onCreated,void *onSizeChanged,void *onDrawFrame,void *onTexcoords,void *onVertices,void *onRelease){ mp->ffplayer->vout->has_filter = has_filter; if(has_filter == 1){ mp->ffplayer->vout->func_onCreated=onCreated; mp->ffplayer->vout->func_onSizeChanged=onSizeChanged; mp->ffplayer->vout->func_onDrawFrame=onDrawFrame; mp->ffplayer->vout->func_onTexcoords=onTexcoords; mp->ffplayer->vout->func_onVertices=onVertices; mp->ffplayer->vout->func_onRelease=onRelease; } }在 ijkmedia/ijksdl/android/ijksdl_vout_android_nativewindow.c的func_create_overlay()函数内添加
static SDL_VoutOverlay *func_create_overlay(int width, int height, int frame_format, SDL_Vout *vout) { SDL_LockMutex(vout->mutex); SDL_VoutOverlay *overlay = func_create_overlay_l(width, height, frame_format, vout); overlay->has_filter=vout->has_filter; overlay->func_onCreated=vout->func_onCreated; overlay->func_onSizeChanged=vout->func_onSizeChanged; overlay->func_onDrawFrame=vout->func_onDrawFrame; overlay->func_onTexcoords=vout->func_onTexcoords; overlay->func_onVertices=vout->func_onVertices; overlay->func_onRelease=vout->func_onRelease; SDL_UnlockMutex(vout->mutex); return overlay; }在 ijkmedia/ijksdl/gles2/renderer.c的IJK_GLES2_Renderer_create()函数内添加
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay) { ...//省略代码 renderer->has_filter=overlay->has_filter; renderer->func_onCreated=overlay->func_onCreated; renderer->func_onSizeChanged=overlay->func_onSizeChanged; renderer->func_onDrawFrame=overlay->func_onDrawFrame; renderer->func_onTexcoords=overlay->func_onTexcoords; renderer->func_onVertices=overlay->func_onVertices; renderer->func_onRelease=overlay->func_onRelease; return renderer; }然后在 ijkmedia/ijkplayer/android/ijkplayer_jni.c内添加代码
static JNIEnv * mEnv; static jmethodID onCreatedMethod; static jmethodID onSizeChangedMethod; static jmethodID onDrawFrameMethod; static jmethodID onTexcoordsMethod; static jmethodID onVerticesMethod; static jmethodID onReleaseMethod; void onCreated(){ if(!mEnv){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); jclass filterClass=(*mEnv)->GetObjectClass(mEnv,mFilter); onCreatedMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onCreated","()V"); onSizeChangedMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onSizeChanged","(II)V"); onDrawFrameMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onDrawFrame","(I)V"); onTexcoordsMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onTexcoords","([F)V"); onVerticesMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onVertices","([F)V"); onReleaseMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onRelease","()V"); (*g_jvm)->DetachCurrentThread(g_jvm); } if(onCreatedMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); (*mEnv)->CallVoidMethod(mEnv,mFilter,onCreatedMethod); (*g_jvm)->DetachCurrentThread(g_jvm); } } void onSizeChanged(int width,int height){ if(onSizeChangedMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); (*mEnv)->CallVoidMethod(mEnv,mFilter,onSizeChangedMethod,width,height); (*g_jvm)->DetachCurrentThread(g_jvm); } } void onDrawFrame(int textureId){ if(onDrawFrameMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); (*mEnv)->CallVoidMethod(mEnv,mFilter,onDrawFrameMethod,textureId); (*g_jvm)->DetachCurrentThread(g_jvm); } } void onTexcoords(float *texcoords){ if(onTexcoordsMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); jfloatArray array = (*mEnv)->NewFloatArray(mEnv,8); (*mEnv)->SetFloatArrayRegion(mEnv,array, 0, 8, texcoords); (*mEnv)->CallVoidMethod(mEnv,mFilter,onTexcoordsMethod,array); (*mEnv)->DeleteLocalRef(mEnv, array); (*g_jvm)->DetachCurrentThread(g_jvm); } } void onVertices(float *vertices){ if(onVerticesMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); jfloatArray array = (*mEnv)->NewFloatArray(mEnv,8); (*mEnv)->SetFloatArrayRegion(mEnv,array, 0, 8, vertices); (*mEnv)->CallVoidMethod(mEnv,mFilter,onVerticesMethod,array); (*mEnv)->DeleteLocalRef(mEnv, array); (*g_jvm)->DetachCurrentThread(g_jvm); } } void onRelease(){ if(onReleaseMethod){ (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL); (*mEnv)->CallVoidMethod(mEnv,mFilter,onReleaseMethod); (*g_jvm)->DetachCurrentThread(g_jvm); } } static void IjkMediaPlayer_native_setGLFilter(JNIEnv *env,jobject thiz,jobject filter) { if(mFilter){ (*env)->DeleteGlobalRef(env,mFilter); } IjkMediaPlayer *mp = jni_get_media_player(env, thiz); if(filter!=NULL){ mFilter=(*env)->NewGlobalRef(env,filter); ijkmp_android_set_filter(mp,1,onCreated,onSizeChanged,onDrawFrame,onTexcoords,onVertices,onRelease); }else{ ijkmp_android_set_filter(mp,0,NULL,NULL,NULL,NULL,NULL,NULL); } }在g_methods[]内添加
{ "_setGLFilter", "(Ltv/danmaku/ijk/media/player/IjkFilter;)V", (void *) IjkMediaPlayer_native_setGLFilter },在函数IjkMediaPlayer_release()内添加
if(mFilter){ (*env)->DeleteGlobalRef(env,mFilter); mFilter=NULL; }
好了,接下来就是改ijkmedia/ijksdl/gles2/renderer.c的代码了,从上到下把要修改的地方都说一下
在IJK_GLES2_Renderer_reset()函数内添加
void IJK_GLES2_Renderer_reset(IJK_GLES2_Renderer *renderer) { ...// 省略代码 if(renderer->frame_textures){ glDeleteTextures(1, renderer->frame_textures); renderer->frame_textures[0] = 0; } if(renderer->frame_buffers){ glDeleteBuffers(1,renderer->frame_buffers); renderer->frame_buffers[0] = 0; } if(renderer->has_filter){ renderer->func_onRelease(); } }在IJK_GLES2_Renderer_create_base()内添加
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_base(const char *fragment_shader_source) { ...//省略代码 renderer->texcoords_test[0] = 0.0f; renderer->texcoords_test[1] = 1.0f; renderer->texcoords_test[2] = 1.0f; renderer->texcoords_test[3] = 1.0f; renderer->texcoords_test[4] = 0.0f; renderer->texcoords_test[5] = 0.0f; renderer->texcoords_test[6] = 1.0f; renderer->texcoords_test[7] = 0.0f; renderer->vertices_test[0] = -1.0f; renderer->vertices_test[1] = -1.0f; renderer->vertices_test[2] = 1.0f; renderer->vertices_test[3] = -1.0f; renderer->vertices_test[4] = -1.0f; renderer->vertices_test[5] = 1.0f; renderer->vertices_test[6] = 1.0f; renderer->vertices_test[7] = 1.0f; return renderer; ...//省略代码 return NULL; }在IJK_GLES2_Renderer_Vertices_apply()添加
static void IJK_GLES2_Renderer_Vertices_apply(IJK_GLES2_Renderer *renderer) { ...//省略代码 if(renderer->has_filter){ renderer->func_onVertices(renderer->vertices); } }修改IJK_GLES2_Renderer_Vertices_reloadVertex()
static void IJK_GLES2_Renderer_Vertices_reloadVertex(IJK_GLES2_Renderer *renderer) { glVertexAttribPointer(renderer->av4_position, 2, GL_FLOAT, GL_FALSE, 0, renderer->vertices_test); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(av2_texcoord)"); glEnableVertexAttribArray(renderer->av4_position); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(av2_texcoord)"); }在IJK_GLES2_Renderer_TexCoords_cropRight()添加
static void IJK_GLES2_Renderer_TexCoords_cropRight(IJK_GLES2_Renderer *renderer, GLfloat cropRight) { ...//省略代码 if(renderer->has_filter){ renderer->func_onTexcoords(renderer->texcoords); } }修改IJK_GLES2_Renderer_TexCoords_reloadVertex()
static void IJK_GLES2_Renderer_TexCoords_reloadVertex(IJK_GLES2_Renderer *renderer) { glVertexAttribPointer(renderer->av2_texcoord, 2, GL_FLOAT, GL_FALSE, 0, renderer->texcoords_test); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(av2_texcoord)"); glEnableVertexAttribArray(renderer->av2_texcoord); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(av2_texcoord)"); }还是那句我忘了IJK_GLES2_Renderer_use()内改了什么就直接贴全部代码
GLboolean IJK_GLES2_Renderer_use(IJK_GLES2_Renderer *renderer) { if (!renderer) return GL_FALSE; assert(renderer->func_use); if (!renderer->func_use(renderer)) return GL_FALSE; IJK_GLES2_Renderer_TexCoords_reset(renderer); IJK_GLES2_Renderer_Vertices_reset(renderer); return GL_TRUE; }差点忘了IJK_GLES2_Renderer_set_view_size()了, 在 ijkmedia/ijksdl/ijksdl_gles2.h内添加函数
void IJK_GLES2_Renderer_set_view_size(IJK_GLES2_Renderer *renderer,int width,int height);在ijkmedia/ijksdl/ijksdl_egl.c的IJK_EGL_prepareRenderer()内添加代码
static EGLBoolean IJK_EGL_prepareRenderer(IJK_EGL* egl, SDL_VoutOverlay *overlay) { ...//省略代码 IJK_GLES2_Renderer_set_view_size(opaque->renderer,egl->width, egl->height); return EGL_TRUE; }在 ijkmedia/ijksdl/gles2/renderer.c内添加
void IJK_GLES2_Renderer_set_view_size(IJK_GLES2_Renderer *renderer,int width,int height){ renderer->view_width = width; renderer->view_height = height; }回到 renderer.c,最后改IJK_GLES2_Renderer_renderOverlay()
GLboolean IJK_GLES2_Renderer_renderOverlay(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay) { GLsizei visible_width = renderer->frame_width; GLsizei visible_height = renderer->frame_height; if (overlay) { visible_width = overlay->w; visible_height = overlay->h; if (renderer->frame_width != visible_width || renderer->frame_height != visible_height || renderer->frame_sar_num != overlay->sar_num || renderer->frame_sar_den != overlay->sar_den) { renderer->frame_width = visible_width; renderer->frame_height = visible_height; renderer->frame_sar_num = overlay->sar_num; renderer->frame_sar_den = overlay->sar_den; renderer->vertices_changed = 1; } renderer->last_buffer_width = renderer->func_getBufferWidth(renderer, overlay); if (!renderer->func_uploadTexture(renderer, overlay)) return GL_FALSE; } else { // NULL overlay means force reload vertice renderer->vertices_changed = 1; } GLsizei buffer_width = renderer->last_buffer_width; if (renderer->vertices_changed || (buffer_width > 0 && buffer_width > visible_width && buffer_width != renderer->buffer_width && visible_width != renderer->visible_width)){ renderer->vertices_changed = 0; IJK_GLES2_Renderer_Vertices_apply(renderer); renderer->buffer_width = buffer_width; renderer->visible_width = visible_width; GLsizei padding_pixels = buffer_width - visible_width; GLfloat padding_normalized = ((GLfloat)padding_pixels) / buffer_width; IJK_GLES2_Renderer_TexCoords_reset(renderer); IJK_GLES2_Renderer_TexCoords_cropRight(renderer, padding_normalized); } if (!renderer || !renderer->func_uploadTexture) return GL_FALSE; if(renderer->has_filter&&!renderer->frame_buffers[0]&&renderer->frame_width>0&&renderer->frame_height>0){ glGenTextures(1,renderer->frame_textures); glBindTexture(GL_TEXTURE_2D,renderer->frame_textures[0]); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,renderer->frame_width,renderer->frame_height,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D,0); glGenFramebuffers(1,renderer->frame_buffers); glBindFramebuffer(GL_FRAMEBUFFER,renderer->frame_buffers[0]); glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,renderer->frame_textures[0],0); glBindFramebuffer(GL_FRAMEBUFFER,0); renderer->func_onCreated(); renderer->func_onSizeChanged(renderer->view_width, renderer->view_height); } if(renderer->has_filter&&renderer->frame_buffers[0]){ glBindFramebuffer(GL_FRAMEBUFFER,renderer->frame_buffers[0]); } renderer->func_use(renderer); glViewport(0, 0, renderer->frame_width, renderer->frame_height); IJK_GLES2_checkError_TRACE("glViewport"); glClear(GL_COLOR_BUFFER_BIT); IJK_GLES2_checkError_TRACE("glClear"); IJK_GLES_Matrix modelViewProj; IJK_GLES2_loadOrtho(&modelViewProj, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); glUniformMatrix4fv(renderer->um4_mvp, 1, GL_FALSE, modelViewProj.m); IJK_GLES2_checkError_TRACE("glUniformMatrix4fv(um4_mvp)"); IJK_GLES2_Renderer_TexCoords_reloadVertex(renderer); IJK_GLES2_Renderer_Vertices_reloadVertex(renderer); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); IJK_GLES2_checkError_TRACE("glDrawArrays"); if(renderer->has_filter&&renderer->frame_buffers[0]){ glBindFramebuffer(GL_FRAMEBUFFER,0); renderer->func_onDrawFrame(renderer->frame_textures[0]); } return GL_TRUE; }好了,可能忘了一些代码,我会在最后放上源码,可以下载来看看
接下来就是改java代码了,java还是很简单的,在tv/danmaku/ijk/media/player文件夹下添加个类
public interface IjkFilter { void onCreated(); void onSizeChanged(int width,int height); void onDrawFrame(int textureId); void onTexcoords(float[] texcoords); void onVertices(float[] vertices); void onRelease(); }在IjkMediaPlayer.java内添加
public void setFilter(IjkFilter filter){ _setGLFilter(filter); } private native void _setGLFilter(IjkFilter filter);好了,这样就可以实现滤镜了,用法和GLSurfaceView.Renderer一样,贴我的实现代码,先贴 shader代码
bitmap_vertext_shader.glsl
precision highp float; varying highp vec2 vTexCoord; attribute highp vec4 aPosition; attribute highp vec2 aTexCoord; void main() { vTexCoord = vec2(aTexCoord.x,1.0-aTexCoord.y); gl_Position = aPosition; }bitmap_fragment_sharder.glsl
precision highp float; varying highp vec2 vTexCoord; uniform lowp sampler2D sTexture; void main() { lowp vec3 rgb = texture2D(sTexture, vTexCoord).rgb; gl_FragColor = vec4(0.299*rgb.r+0.587*rgb.g+0.114*rgb.b); }建一个读取program的类
ShaderUtils.java
public class ShaderUtils { private static final String TAG = "ShaderUtils"; public static void checkGlError(String label) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, label + ": glError " + error); throw new RuntimeException(label + ": glError " + error); } } public static int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } int program = GLES20.glCreateProgram(); if (program != 0) { GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); GLES20.glLinkProgram(program); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } public static int loadShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); if (shader != 0) { GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); int[] compiled = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } public static String readRawTextFile(Context context, int resId) { InputStream inputStream = context.getResources().openRawResource(resId); try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } }然后是我的滤镜实现类
GLRenderer.java
public class GLRenderer implements IjkFilter { private Context context; private FloatBuffer vertexBuffer; private FloatBuffer textureVertexBuffer; private int programId = 0; private int aPositionHandle; private int uTextureSamplerHandle; private int aTextureCoordHandle; public GLRenderer(Context context){ this.context = context; final float[] vertexData = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f }; final float[] textureVertexData = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f }; vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(vertexData); vertexBuffer.position(0); textureVertexBuffer = ByteBuffer.allocateDirect(textureVertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(textureVertexData); textureVertexBuffer.position(0); } @Override public void onCreated() { String vertexShader = ShaderUtils.readRawTextFile(context, R.raw.bitmap_vertext_shader); String fragmentShader = ShaderUtils.readRawTextFile(context, R.raw.bitmap_fragment_sharder); programId = ShaderUtils.createProgram(vertexShader, fragmentShader); aPositionHandle = GLES20.glGetAttribLocation(programId, "aPosition"); uTextureSamplerHandle = GLES20.glGetUniformLocation(programId, "sTexture"); aTextureCoordHandle = GLES20.glGetAttribLocation(programId, "aTexCoord"); } private Rect rect = new Rect(); @Override public void onSizeChanged(int width, int height) { rect.left = 0; rect.top = 0; rect.right = width; rect.bottom = height; } @Override public void onDrawFrame(int textureId) { GLES20.glUseProgram(programId); GLES20.glViewport(0,0,rect.right,rect.bottom); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); GLES20.glEnableVertexAttribArray(aPositionHandle); GLES20.glVertexAttribPointer(aPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); GLES20.glEnableVertexAttribArray(aTextureCoordHandle); GLES20.glVertexAttribPointer(aTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureVertexBuffer); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId); GLES20.glUniform1i(uTextureSamplerHandle, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } public void onTexcoords(float[] texcoords){ textureVertexBuffer.clear(); textureVertexBuffer.put(texcoords); textureVertexBuffer.position(0); } public void onVertices(float[] vertices){ vertexBuffer.clear(); vertexBuffer.put(vertices); vertexBuffer.position(0); } public void onRelease(){ if(programId != 0){ GLES20.glDeleteProgram(programId); } } }
最后在IjkMediaPlayer初始化后调用setFilter函数
GLRenderer renderer = new GLRenderer(getContext()); ijkMediaPlayer.setFilter(renderer);同样的要开启opengl渲染
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");源码: 百度云,里面也有.so和.jar文件,不过还是推荐自己修改编译,我只是简单的测试几下,应该bug还挺多
密码:26dk
用官方的列子测试了几下,像onRelease()都不会调用,也不知道ijkplayer是怎么释放的,看来还是要研究研究