最近在研究ijkplayer 源码,刚开始的时候看的有点眼晕,这个嵌套在那个里,那个又嵌套在这个里,特别是java文件是用c代码生成的,熟悉一些了,感觉对自己还是很有帮助的,言归正传。
去网上搜了下ijkplayer添加滤镜只找到这篇让IjkPlayer支持插入自定义的GPU滤镜参考了一下,他的是在c层使用opengl的fbo得到视频纹理再把纹理传到java层进行操作,可能我和他的ijkplayer版本不一样,虽然成功了可是加了很多他那篇文章里没有的代码,也熟悉了ijkplayer源码,既然他是在java层来进行操作,那我就在c层来实现滤镜吧,思路一样也是用fbo,可能我的方法在其他版本不适用所以说一下我的ijkplayer版本0.8.4,先看我实现的效果
当然这是简单的黑白滤镜,既然想添加滤镜,那就要添加shader代码,ijkplayer的shader代码都在ijkmedia/ijksdl/gles2/fsh文件夹下边,可以直接进行更改,不过每个文件都要改很麻烦,所以新建一个,我就随便建一个,在ijkmedia/ijksdl/gles2下边新建了个test_opengl.c文件,添加代码
#include "internal.h" static const char g_shader[] = IJK_GLES_STRING( 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); } ); const char *IJK_GLES2_getFragmentShader_test() { return g_shader; } static const char v_shader[] = IJK_GLES_STRING( 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; } ); const char *IJK_GLES2_getVertexShader_test() { return v_shader; }这是我的黑白滤镜shader,然后在ijkmedia/ijksdl/gles2/internal.h内添加函数
const char *IJK_GLES2_getFragmentShader_test(); const char *IJK_GLES2_getVertexShader_test();新建的文件要编译进去,修改ijkmedia/ijksdl/Android.mk添加新建文件
LOCAL_SRC_FILES += gles2/test_opengl.c
保存后开始修改代码,主要修改的都是ijkmedia/ijksdl/gles2文件夹下边的代码
先修改renderer_rgb.c,renderer_yuv420p.c,renderer_yuv420sp.c,renderer_yuv420sp_vtb.m,renderer_yuv444p10le.c的代码
只是简单的修改初始化纹理代码比如把renderer_yuv420p.c的yuv420p_use函数内代码
if (0 == renderer->plane_textures[0]) glGenTextures(3, renderer->plane_textures); for (int i = 0; i < 3; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]); 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); glUniform1i(renderer->us2_sampler[i], i); }改成
if (0 == renderer->plane_textures[0]){ glGenTextures(3, renderer->plane_textures); for (int i = 0; i < 3; ++i) { glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]); 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); } } for (int i = 0; i < 3; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]); glUniform1i(renderer->us2_sampler[i], i); }其他的也这样改,初始化一次因为这个函数要调用很多次,再修改internal.h代码,在IJK_GLES2_Renderer结构体内添加代码
typedef struct IJK_GLES2_Renderer { ...//省略代码 int view_width; int view_height; GLuint frame_buffers[1]; GLuint frame_textures[1]; GLuint program_test; GLuint vertex_shader_test; GLuint fragment_shader_test; GLuint position_test; GLuint texcoord_test; GLuint sampler_test; GLfloat texcoords_test[8]; GLfloat vertices_test[8]; } IJK_GLES2_Renderer;
在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了,而且是大改
先补完上边的最后一步,在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; }初始化program_test等参数
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_base(const char *fragment_shader_source) { ...//省略代码 renderer->vertex_shader_test = IJK_GLES2_loadShader(GL_VERTEX_SHADER, IJK_GLES2_getVertexShader_test()); if (!renderer->vertex_shader_test) goto fail; renderer->fragment_shader_test = IJK_GLES2_loadShader(GL_FRAGMENT_SHADER, IJK_GLES2_getFragmentShader_test()); if (!renderer->fragment_shader_test) goto fail; renderer->program_test = glCreateProgram(); IJK_GLES2_checkError("glCreateProgram"); if (!renderer->program_test) goto fail; glAttachShader(renderer->program_test, renderer->vertex_shader_test); IJK_GLES2_checkError("glAttachShader(vertex)"); glAttachShader(renderer->program_test, renderer->fragment_shader_test); IJK_GLES2_checkError("glAttachShader(fragment)"); glLinkProgram(renderer->program_test); IJK_GLES2_checkError("glLinkProgram"); GLint link_status_test = GL_FALSE; glGetProgramiv(renderer->program_test, GL_LINK_STATUS, &link_status_test); if (!link_status_test) goto fail; renderer->position_test = glGetAttribLocation(renderer->program_test, "aPosition"); IJK_GLES2_checkError_TRACE("glGetAttribLocation(aPosition)"); renderer->texcoord_test = glGetAttribLocation(renderer->program_test, "aTexCoord"); IJK_GLES2_checkError_TRACE("glGetAttribLocation(aTexCoord)"); renderer->sampler_test = glGetUniformLocation(renderer->program_test, "sTexture"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(sTexture)"); 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; }
释放
void IJK_GLES2_Renderer_reset(IJK_GLES2_Renderer *renderer) { ...//省略代码 if (renderer->vertex_shader_test) glDeleteShader(renderer->vertex_shader_test); if (renderer->fragment_shader_test) glDeleteShader(renderer->fragment_shader_test); if (renderer->program_test) glDeleteProgram(renderer->program_test); if(renderer->frame_textures) glDeleteTextures(1, renderer->frame_textures); if(renderer->frame_buffers) glDeleteBuffers(1,renderer->frame_buffers); }修改IJK_GLES2_Renderer_Vertices_reloadVertex和IJK_GLES2_Renderer_TexCoords_reloadVertex函数
static void IJK_GLES2_Renderer_Vertices_reloadVertex(IJK_GLES2_Renderer *renderer) { //glVertexAttribPointer(renderer->av4_position, 2, GL_FLOAT, GL_FALSE, 0, renderer->vertices); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(av2_texcoord)"); //glEnableVertexAttribArray(renderer->av4_position); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(av2_texcoord)"); 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)"); }
static void IJK_GLES2_Renderer_TexCoords_reloadVertex(IJK_GLES2_Renderer *renderer) { //glVertexAttribPointer(renderer->av2_texcoord, 2, GL_FLOAT, GL_FALSE, 0, renderer->texcoords); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(av2_texcoord)"); //glEnableVertexAttribArray(renderer->av2_texcoord); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(av2_texcoord)"); 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_renderOverlay函数,也是用opengl的fbo所以有些代码直接复制了上面那篇博文的
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->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); } if(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->frame_buffers[0]){ glBindFramebuffer(GL_FRAMEBUFFER,0); } glUseProgram(renderer->program_test); IJK_GLES2_checkError_TRACE("glUseProgram"); glViewport(0, 0, renderer->view_width, renderer->view_height); IJK_GLES2_checkError_TRACE("glViewport"); glClear(GL_COLOR_BUFFER_BIT); IJK_GLES2_checkError_TRACE("glClear"); glClearColor(1.0f,1.0f,0.0f,1.0f); glVertexAttribPointer(renderer->texcoord_test, 2, GL_FLOAT, GL_FALSE, 0, renderer->texcoords); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(texcoord_test)"); glEnableVertexAttribArray(renderer->texcoord_test); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(texcoord_test)"); glVertexAttribPointer(renderer->position_test, 2, GL_FLOAT, GL_FALSE, 0, renderer->vertices); IJK_GLES2_checkError_TRACE("glVertexAttribPointer(position_test)"); glEnableVertexAttribArray(renderer->position_test); IJK_GLES2_checkError_TRACE("glEnableVertexAttribArray(position_test)"); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,renderer->frame_textures[0]); glUniform1i(renderer->sampler_test, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); IJK_GLES2_checkError_TRACE("glDrawArrays"); return GL_TRUE; }
还要说一下ijkplayer默认是不开启opengl渲染的要在例子的java代码中修改
// String pixelFormat = mSettings.getPixelFormat(); // if (TextUtils.isEmpty(pixelFormat)) { // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32); // } else { // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat); // } ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");