短视频系统源码开发,通过OpenGL给视频增加不同滤镜效果

首先,新建一个SlideGpufilterGroup类,该类就是我们来实现短视频系统源码开发滤镜效果的核心类

 public class SlideGpufilterGroup {
 }

然后,我们需要一个数组来标记我们所有的滤镜

   private MagicFilterType[] types = new MagicFilterType[]{
            MagicFilterType.NONE,
            MagicFilterType.WARM,
            MagicFilterType.ANTIQUE,
            MagicFilterType.INKWELL,
            MagicFilterType.BRANNAN,
            MagicFilterType.N1977,
            MagicFilterType.FREUD,
            MagicFilterType.HEFE,
            MagicFilterType.HUDSON,
            MagicFilterType.NASHVILLE,
            MagicFilterType.COOL
    };

这里的MagicFilterType,就是我们定义的用来表示滤镜的枚举。
然后定义三个滤镜,分别表示当前的滤镜,左边一个的滤镜,和右边一个的滤镜,对外提供他们的初始化和设置size。

 private GPUImageFilter curFilter;
    private GPUImageFilter leftFilter;
    private GPUImageFilter rightFilter;
 
    public void init() {
        curFilter.init();
        leftFilter.init();
        rightFilter.init();
    }
    private void onFilterSizeChanged(int width, int height) {
        curFilter.onInputSizeChanged(width, height);
        leftFilter.onInputSizeChanged(width, height);
        rightFilter.onInputSizeChanged(width, height);
        curFilter.onDisplaySizeChanged(width, height);
        leftFilter.onDisplaySizeChanged(width, height);
        rightFilter.onDisplaySizeChanged(width, height);
    }

然后定义绘制的画面的宽高,由接口从外部进行设置

  private int width, height;
     public void onSizeChanged(int width, int height) {
        this.width = width;
        this.height = height;
        GLES20.glGenFramebuffers(1, fFrame, 0);
        EasyGlUtils.genTexturesWithParameter(1, fTexture, 0, GLES20.GL_RGBA, width, height);
        onFilterSizeChanged(width, height);
     }

然后再定义一个帧缓冲区和一个纹理,用于绘制图像

  private int[] fFrame = new int[1];
    private int[] fTexture = new int[1];

并且初始化一个Scroller,来完成滑动事件的响应。

  private Scroller scroller;
   scroller = new Scroller(MyApplication.getContext());

初始化一个当前的filter的index,也就是标记位,标识当前是哪一个filter。

 private int curIndex = 0;

然后,在构造函数里面进行一些初始化

  public SlideGpufilterGroup() {
        initFilter();
        scroller = new Scroller(MyApplication.getContext());
    }

initFilter方法其实就是当前三种filter的初始化

public void initFilter() {
    curFilter = getFilter(getCurIndex());
    leftFilter = getFilter(getLeftIndex());
    rightFilter = getFilter(getRightIndex());
}

getFilter其实就是根据filter的index进行filter对象的初始化

  public GPUImageFilter getFilter(int index) {
        GPUImageFilter filter = MagicFilterFactory.initFilters(types[index]);
        if (filter == null) {
            filter = new GPUImageFilter();
        }
        return filter;
    }

而getCurIndex、getLeftIndex、getRightIndex这三个方法,就是分别获取到当前filter的index和左边、右边的filter的index。

 private int getCurIndex() {
        return curIndex;
    }
    private int getLeftIndex() {
        int leftIndex = curIndex - 1;
        if (leftIndex < 0) {
            leftIndex = types.length - 1;
        }
        return leftIndex;
    }
    private int getRightIndex() {
        int rightIndex = curIndex + 1;
        if (rightIndex >= types.length) {
            rightIndex = 0;
        }
        return rightIndex;
    }

当前的filter和左边、右边的filter我们都有了,现在还有两个点,一个就是怎么样具体去绘制当前数据帧,另一个就是怎么进行filter的切换。
首先,我们来看看详细的绘制规则,首先对外提供一个onDrawFrame方法

public void onDrawFrame(int textureId) {
    EasyGlUtils.bindFrameTexture(fFrame[0], fTexture[0]);
    if (direction == 0 && offset == 0) {
        curFilter.onDrawFrame(textureId);
    } else if (direction == 1) {
        onDrawSlideLeft(textureId);
    } else if (direction == -1) {
        onDrawSlideRight(textureId);
    }
    EasyGlUtils.unBindFrameBuffer();
}

该方法,接受一个纹理id,就是我们当前要绘制的纹理。然后将帧缓冲和纹理进行绑定, 下面就是一个if else的判断,这里direction其实就是我们自己定义的,用来表示当前是什么动作的一个int。

int direction;//0为静止,-1为向左滑,1为向右滑

用这样一个int,就可以区分当前的滑动是什么样的状态,如果为0表示没有滑动,那么我们只用绘制curFilter就行了。而上面我们已经进行了curFilter的初始化,它也继承自GPUImageFilter。我们只用调用onDrawFragme把当前的纹理id传入进去就行了。
而这个offset,其实就是我们记录的当前的滑动量,如果offset其实=0的,那表示没有偏移量,那肯定也只用绘制当前这个filter就行了。
如果,当前的状态是向右滑动,也就是direction = 1,我们就调用onDrawSlideLeft函数。

 private void onDrawSlideLeft(int textureId) {
        if (locked && scroller.computeScrollOffset()) {
            offset = scroller.getCurrX();
            drawSlideLeft(textureId);
        } else {
            drawSlideLeft(textureId);
            if (locked) {
                if (needSwitch) {
                    reCreateRightFilter();
                    if (mListener != null) {
                        mListener.onFilterChange(types[curIndex]);
                    }
                }
                offset = 0;
                direction = 0;
                locked = false;
            }
        }
    }

我们先来看看scroller的computeScrollOffset方法 官方注释是

  /**
     * Call this when you want to know the new location.  If it returns true,
     * the animation is not yet finished.
     */ 
    public boolean computeScrollOffset() {

其实就是如果还在滑动的话,就返回true。
而这个locked,其实就是我们定义的一个用来控制流程的boolean值,赋值情况,后面的代码会详细讲解。
我们先来继续看这个if逻辑,如果是true的话,就拿到当前的offset偏移量,然后调用drawSlideLeft方法。

private void drawSlideLeft(int textureId) {
        GLES20.glViewport(0, 0, width, height);
        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
        GLES20.glScissor(0, 0, offset, height);
        leftFilter.onDrawFrame(textureId);
        GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
        GLES20.glViewport(0, 0, width, height);
        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
        GLES20.glScissor(offset, 0, width - offset, height);
        curFilter.onDrawFrame(textureId);
        GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
    }

这个方法,就是绘制左滑情况的具体绘制,首先设置画面大小,然后开启了裁剪功能,然后用leftFilter绘制左侧的画面,用curFilter绘制右侧的画面,两者合起来就是一个完整的画面。offset就是用来确定左边和右边分别应该绘制多宽的值。同样我们也会有一个相同原理,类似代码的drawSlideRight方法

 private void drawSlideRight(int textureId) {
        GLES20.glViewport(0, 0, width, height);
        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
        GLES20.glScissor(0, 0, width - offset, height);
        curFilter.onDrawFrame(textureId);
        GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
        GLES20.glViewport(0, 0, width, height);
        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
        GLES20.glScissor(width - offset, 0, offset, height);
        rightFilter.onDrawFrame(textureId);
        GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
 }

然后继续看if判断的false情况,如果是false的话,说明滑动动画还没停下来,如果locked是true的话,就判断是否需要切换滤镜,needSwitch在什么情况下为true呢,其实他的判断是通过判断滑动事件,如果当前的offset超过了屏幕宽度的1/3,具体的地方我们接下来会说到。就是在滑动动画停止的时候,如果需要切换filter,我们就调用了reCreateRightFilter()方法,并且返回了一个监听回调。而这个reCreateRightFilter方法具体代码如下。

 private void reCreateRightFilter() {
        decreaseCurIndex();
        rightFilter.destroy();
        rightFilter = curFilter;
        curFilter = leftFilter;
        leftFilter = getFilter(getLeftIndex());
        leftFilter.init();
        leftFilter.onDisplaySizeChanged(width, height);
        leftFilter.onInputSizeChanged(width, height);
        needSwitch = false;
    }

这些代码里面的decreaseCurIndex方法,其实就是移动了curIndex,这个值,因为要向左一个切换滤镜,那么curIndex需要-1.

   private void decreaseCurIndex() {
        curIndex--;
        if (curIndex < 0) {
            curIndex = types.length - 1;
        }
    }
      然后就销毁rightFilter。如果把当前的curFilter赋值给rightFilter,当前的curFilter设置为之前的leftFilter,然后再通过getFilter向左一个获取到新的leftFilter。如此就完成了一次滤镜的切换。那么同样的如果需要往右切换filter的话,那肯定有一个类似的方法。    
    private void reCreateLeftFilter() {
        increaseCurIndex();
        leftFilter.destroy();
        leftFilter = curFilter;
        curFilter = rightFilter;
        rightFilter = getFilter(getRightIndex());
        rightFilter.init();
        rightFilter.onDisplaySizeChanged(width, height);
        rightFilter.onInputSizeChanged(width, height);
        needSwitch = false;
    }

这就是核心的onDrawFrame函数,然后在解绑掉帧缓冲和纹理。

 EasyGlUtils.unBindFrameBuffer();

这个类大部分的代码就是这样,最后剩下最重要的函数就是对外提供的onTouchEvent函数

 public void onTouchEvent(MotionEvent event) {
        if (locked) {
            return;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (downX == -1) {
                    return;
                }
                int curX = (int) event.getX();
                if (curX > downX) {
                    direction = 1;
                } else {
                    direction = -1;
                }
                offset = Math.abs(curX - downX);
                break;
            case MotionEvent.ACTION_UP:
                if (downX == -1) {
                    return;
                }
                if (offset == 0) {
                    return;
                }
                locked = true;
                downX = -1;
                if (offset > Constants.screenWidth / 3) {
                    scroller.startScroll(offset, 0, Constants.screenWidth - offset, 0, 100 * (1 - offset / Constants.screenWidth));
                    needSwitch = true;
                } else {
                    scroller.startScroll(offset, 0, -offset, 0, 100 * (offset / Constants.screenWidth));
                    needSwitch = false;
                }
                break;
        }
    }

这个函数,基本上就是在down事件的时候,获取到X值,然后在move事件的时候,就根据当前的X和down时候的X值做对比的时候,判断是像左滑还是右滑。然后在up事件的时候,将偏移量offset和屏幕宽度进行比较,判断是否需要切换滤镜。
短视频系统源码开发,通过OpenGL给视频增加不同滤镜效果实现逻辑,基本上就是这样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值