目录

1、概述

2、模块原理

2.1、 播放器

2.2、 显示渲染

2.3、 颜色滤镜

3、总结

 

1、概述

本文介绍一个在Android上面实现的一个实时调整滤镜参数的播放器例子。市面上一些美颜,画质增强等播放器的大致原理都是如此。通过调整图1中的三个参数就可以实时看到画面颜色、明暗、艳丽程度的变化。

图1

 

 

2、模块原理

这个例子可以分为三个模块:解码播放、显示渲染、颜色参数调整转化。本文重点是颜色滤镜参数的调整。

2.1、 播放器

这里采用的是系统的媒体播放器MediapPayer播放一个放在工程raw资源路径下的MP4文件。播放代码如下:

 private void setupPlayer() {        try {            mMediaPlayer = MediaPlayer.create(this, R.raw.testfile);            mMediaPlayer.setSurface(mSurface);            mMediaPlayer.setLooping(true);        } catch (Exception e) {            e.printStackTrace();        }        mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {            @Override            public void onPrepared(MediaPlayer mediaPlayer) {                mMediaPlayer.start();            }        });    }

这个播放器的输入是testfile.mp4,解码出来的画面会通过Surface传给渲染层。

 

2.2、 显示渲染

这里渲染采用的是OpenGl ES 2.0,由于跨平台,兼容性好被广泛应用。Android对OpenGL ES支持非常好,可以在JNI中开发也有对应的JAVA接口,本文采用的是JAVA。

下面是使用OpenGL ES的步骤:

1、在layout文件中配置一个GLSurfaceView

    

2、在Activity文件中设置GlSurfaceView。这里主要是设置Render回调,onSurfaveCreate是初始化一些值,onDrawFrame是画每一帧。

mVideoView = findViewById(R.id.video_view);mVideoView.setEGLContextClientVersion(2);mVideoView.setRenderer(new GLSurfaceView.Renderer() {            @Override            public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {                mReactShape = new RectShape();                int textureId = GLUtil.generateOESTexture();                mSurfaceTexture = new SurfaceTexture(textureId);                mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {                    @Override                    public void onFrameAvailable(SurfaceTexture surfaceTexture) {                        mFrameAvailable = true;                    }                });                mSurface = new Surface(mSurfaceTexture);                mReactShape.setTextureId(textureId);                setupPlayer();            }            @Override            public void onSurfaceChanged(GL10 gl10, int i, int i1) {            }            @Override            public void onDrawFrame(GL10 gl10) {                if (mFrameAvailable) {                    mSurfaceTexture.updateTexImage();                    mFrameAvailable = false;                }                float[] colorFilter = mColorFilterMatrixUtil.getColorFilterArray16();                mReactShape.setColorFilterArray(colorFilter);                mReactShape.draw();            }        });

3、在第二步是配置渲染的线程(GLSurfaceView 有自己独立的线程),具体内容显示还需要一个载体。这里采用的是一个矩形的ReactShape来画每一帧。

package com.test.videocolorfilter;import android.opengl.GLES11Ext;import android.opengl.GLES20;import android.opengl.Matrix;import android.util.Log;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;public class RectShape {    private float width = 2;    private float height = 2;    private int mTextureId = 0;    private int mProgram = -1;    private int mPositionHandle;    private int mTexCoordHandle;    private int mMVPHandle;    private int mColorFilterHandle;    private FloatBuffer mVertices, mTexCoord;    private float[] mModelProjection = new float[16];    private float[] mColorFilterArray = new float[16];    private final static String VERTEX_SHADER =            "uniform mat4 uMVPMatrix;\n" +                    "attribute vec4 a_Position;\n" +                    "attribute vec2 aTexCoor;\n" +                    "varying vec2 vTextureCoord;\n" +                    "uniform float uAngle;\n" +                    "void main(){\n" +                    "    gl_Position = uMVPMatrix * a_Position;\n" +                    "    vTextureCoord = aTexCoor;\n" +                    "}\n";    private final static String FRAGMENT_SHADER =            "#extension GL_OES_EGL_image_external : require\n" +                    "precision highp float;\n" +                    "varying vec2 vTextureCoord;\n" +                    "uniform samplerExternalOES uTexture;\n" +                    "uniform mat4 uColorFilterMatrix;\n" +                    "void main() {\n" +                    "     vec4 val = texture2D(uTexture, vTextureCoord); \n" +                    "     gl_FragColor = val*uColorFilterMatrix; \n" +                    "}\n";    public RectShape() {        init();    }    protected void init() {        Matrix.setIdentityM(mModelProjection, 0);        Matrix.setIdentityM(mColorFilterArray, 0);        float[] vertices = new float[]{                -width / 2, -height / 2, 0,                width / 2, height / 2, 0,                -width / 2, height / 2, 0,                width / 2, height / 2, 0,                -width / 2, -height / 2, 0,                width / 2, -height / 2, 0,        };        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);        vbb.order(ByteOrder.nativeOrder());        mVertices = vbb.asFloatBuffer();        mVertices.put(vertices);        mVertices.position(0);        // create program        mProgram = GLUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "a_Position");        mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoor");        mMVPHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");        mColorFilterHandle = GLES20.glGetUniformLocation(mProgram, "uColorFilterMatrix");        //setup buffers for the texture 2D coordinates        float[] texCoord = new float[]{                0.0f, 1.0f,                1.0f, 0.0f,                0.0f, 0.0f,                1.0f, 0.0f,                0.0f, 1.0f,                1.0f, 1.0f,        };        ByteBuffer cbb = ByteBuffer.allocateDirect(texCoord.length * 4);        cbb.order(ByteOrder.nativeOrder()); //set the byte order        mTexCoord = cbb.asFloatBuffer(); //convert to byte buffer        mTexCoord.put(texCoord); //load data to byte buffer        mTexCoord.position(0); //set the start point    }    public void setTextureId(int textureId) {        mTextureId = textureId;    }    public void draw() {        draw(mTextureId);    }    public void draw(int textureId) {//        Log.d("RectShape", "draw "+ textureId);        GLES20.glUseProgram(mProgram);        GLES20.glUniformMatrix4fv(mMVPHandle, 1, false, mModelProjection, 0);        GLES20.glUniformMatrix4fv(mColorFilterHandle, 1, false, mColorFilterArray, 0);        if (textureId > 0) {            // render texture            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);        }        // draw triangles        GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertices);        GLES20.glEnableVertexAttribArray(mPositionHandle);        GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexCoord);        GLES20.glEnableVertexAttribArray(mTexCoordHandle);        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertices.capacity() / 3);    }    public void setColorFilterArray(float filter[]) {        System.arraycopy(filter, 0, mColorFilterArray, 0, 16);    }    public void release() {        if (mProgram >= 0) {            GLES20.glDeleteProgram(mProgram);            mProgram = -1;        }    }}

这个类最主要的是部分是两个shader和draw()函数。

shader采用的是GLSL语言,在openGL ES 2.0以后开始使用,分为顶点着色器和片元着色器;顶点着色器指定了顶点转化关系和几何姿态的调整以及投影关系;片元着色器指定的是像素色彩以及光照等变化,这部分通过编译最终执行在GPU中。

draw()函数是将纹理,几何坐标,纹理坐标、姿态矩阵、颜色矩阵等值传给shader。这些值可以每一帧都不一样。

 

2.3、 颜色滤镜

看道这篇文章的同学应该都比较喜欢或熟悉用RGB来描述图像,RGB三基色描述在图像数字化确实比较方便。但是对于眼睛或者设计师调整却并不方便。在滤镜调整过程中大家其实更偏向另外一种颜色模型HSL(hue,saturation,lightness)下图是HSL的原理模型:

图 2

Android对HSL调整有很好的支持,在SDK的android.graphic包中有一个ColorMatrix类。这个类时管理一个5x4的颜色矩阵,支持色相、饱和度、明度调整接口。ColorMatrix T表示如下:

       

颜色C表示如下:

        

新颜色C`

 

R' = a*R + b*G + c*B + d*A + e;
           G' = f*R + g*G + h*B + i*A + j;
           B' = k*R + l*G + m*B + n*A + o;
           A' = p*R + q*G + r*B + s*A + t;

从上图模型可以看出,hue(色相)是调整颜色属性,直观地说就是让颜色偏绿点还是偏红点,效果如下图:

图 3-色相调整效果

 

saturation(饱和度)指的是色彩纯度。是色彩的构成要素之一。纯度越高,表现越鲜明,纯度较低,表现则较黯淡。效果如下图;

图4-饱和度调整效果

lightness(明度)是调整颜色的能量程度,直白说就是黑还是白,调整效果如下图:

图5-明度调整效果

 

3、总结

  • 本文的滤镜只是采用的颜色调整,还没有实现锐化、模糊、马赛克等像素位置变化的效果。
  • 播放器采用的最简单的本地MediaPlayer对于一些直播可能需要替换支持跟多协议的播放器如ijkplayer。
  • 本文的配套的工程github地址https://github.com/liyang-hello/VideoColorFilter,如有兴趣可以下载。

更多相关文章

  1. Android(安卓)中自定义组件例子一(中级)
  2. Android(安卓)studio 自定义logcat各种信息输出颜色
  3. [Android]如何在Android(安卓)studio中增加一个selector资源(用于
  4. android 显示16色的图片:输入用颜色矩阵,显示对应的16色位图
  5. Button 或 ImageButton 背景设为 透明 或半透明
  6. TextView颜色
  7. Android(安卓)ProgressBar圆形进度条颜色设置
  8. Android(安卓)自定义码表图
  9. Android游戏设计中的音频控制——音量调整

随机推荐

  1. Android中sqlite事物控制
  2. Android开发之自定义Notification(源代码
  3. Work 工作子线程更新UI控件--解决Only th
  4. Android(安卓)studio将项目转换为jar文件
  5. 最近有几个开源项目值得介绍下
  6. Android基于zxing-android-embedded实现
  7. 底部导航栏:利用viewpager实现Android底部
  8. android 在各种硬件平台上的测评
  9. android Content Provider 共享数据库
  10. android第四天晚:绘图和handle