欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android高仿抖音照片电影功能的实现代码

程序员文章站 2023-12-03 13:48:22
photomovie (https://github.com/yellowcath/photomovie) 可轻松实现类似抖音、微视、美拍的照片电影功能。 效果如下 滤...

photomovie (https://github.com/yellowcath/photomovie) 可轻松实现类似抖音、微视、美拍的照片电影功能。

效果如下

滤镜效果

Android高仿抖音照片电影功能的实现代码

转场效果

Android高仿抖音照片电影功能的实现代码

使用

首先在项目根目录的build.gradle文件里添加maven库

allprojects {
    repositories {
      ...
      maven { url 'https://www.jitpack.io' }
    }
  }

再在需要用的module的build.gradle里添加依赖

dependencies {
  compile 'com.github.yellowcath:photomovie:1.5.0'
}

基本用法

可参照 demopresenter

//添加图片
    list<photodata> photodatalist = new linkedlist<photodata>();
    photodatalist.add(new simplephotodata(context,photopath1,photodata.state_local));
    ...
    photodatalist.add(new simplephotodata(context,photopathn,photodata.state_local));
    //生成图片源
    photosource photosource = new photosource(photodatalist);
    //生成照片电影(使用预定义的水平转场动画)
    photomovie photomovie = photomoviefactory.generatephotomovie(photosource, photomoviefactory.photomovietype.horizontal_trans);
    //生成负责绘制电影内容的movierenderer
    movierenderer movierenderer = new gltexturemovierender(gltextureview);
    /**
     * or movierenderer movierenderer = new glsurfacemovierenderer(glsurfaceview);
     */
    //照片电影播放器
    photomovieplayer photomovieplayer = new photomovieplayer(context);
    photomovieplayer.setmovierenderer(mmovierenderer);
    photomovieplayer.setmovielistener(...);
    photomovieplayer.setloop(true);
    photomovieplayer.setonpreparedlistener(new photomovieplayer.onpreparedlistener() {
      @override
      public void onpreparing(photomovieplayer movieplayer, float progress) {
      }

      @override
      public void onprepared(photomovieplayer movieplayer, int prepared, int total) {
         mphotomovieplayer.start();
      }

      @override
      public void onerror(photomovieplayer movieplayer) {
      }
    });
    photomovieplayer.prepare();

轻松扩展

photomovie使用模块化的设计,每个部分都可以自定义然后替换,

  • moviesegment:电影片段,每个电影片段都有特定的时长,在这段时间之内以特定的方式播放图片,例如scalesegment会对图片做缩放动画、endgaussianblursegment会对图片做从清晰到模糊的高斯模糊动画
  • photomovie:核心类,代表照片电影本身,由图片源(photosource)和若干电影片段(moviesegment)组成一个完整的照片电影,图片通过photoallocator分配给moviesegment
  • movielayer:为moviesegment扩展绘制多层特效的功能,例如subtitlelayer提供字幕展示
  • imoviefilter:为整个照片电影提供滤镜
  • movierenderer:负责把照片电影渲染到指定的输出界面,例如textureview(gltexturemovierender)、glsurfaceview(glsurfacemovierenderer)
  • photomovieplayer:提供类似mediaplayer的接口,负责播放照片电影,播放进度由imovietimer控制

扩展电影类型

目前内置了6种类型,后两种即是抖音的左右切换和上下切换,thaw和window仿自美拍

public enum photomovietype {
    thaw, //融雪
    scale, //缩放
    scale_trans, //缩放 & 平移
    window, //窗扉
    horizontal_trans,//横向平移
    vertical_trans//纵向平移
  }

这里以微视的渐变特效为例展示如何扩展

分析得出,渐变特效首先图片居中放置,然后全程做一个微弱的放大动画,后半部分同时透明度变化消失

可见需要两个不同的片段类型

首先创建fitcenterscalesegment,继承fitcentersegment,实现单张图片的放大动画

public class fitcenterscalesegment extends fitcentersegment {
  /**
   * 缩放动画范围
   */
  private float mscalefrom;
  private float mscaleto;

  private float mprogress;

  /**
   * @param duration 片段时长
   * @param scalefrom 缩放范围
   * @param scaleto  缩放范围
   */
  public fitcenterscalesegment(int duration, float scalefrom, float scaleto) {
    super(duration);
    mscalefrom = scalefrom;
    mscaleto = scaleto;
  }

  @override
  protected void ondataprepared() {
    super.ondataprepared();
  }

  @override
  public void drawframe(glescanvas canvas, float segmentprogress) {
    mprogress = segmentprogress;
    if (!mdataprepared) {
      return;
    }
    drawbackground(canvas);
    float scale = mscalefrom + (mscaleto - mscalefrom) * mprogress;
    //fitcentersegment已经具有缩放能力,这里传缩放值即可
    drawcontent(canvas, scale);
  }
  //提升这两个函数的访问权限,供转场时使用
    @override
  public void drawcontent(glescanvas canvas, float scale) {
    super.drawcontent(canvas, scale);
  }

  @override
  public void drawbackground(glescanvas canvas) {
    super.drawbackground(canvas);
  }
}

然后创建转场片段gradienttransfersegment,其父类transitionsegment同时持有上一个与下一个片段,可以在其基础上实现任意转场功能

public class gradienttransfersegment extends transitionsegment<fitcenterscalesegment, fitcenterscalesegment> {
  /**
   * 缩放动画范围
   */
  private float mprescalefrom;
  private float mprescaleto;
  private float mnextscalefrom;
  private float mnextscaleto;

  public gradienttransfersegment(int duration,
                  float prescalefrom, float prescaleto,
                  float nextscalefrom, float nextscaleto) {
    mprescalefrom = prescalefrom;
    mprescaleto = prescaleto;
    mnextscalefrom = nextscalefrom;
    mnextscaleto = nextscaleto;
    setduration(duration);
  }

  @override
  protected void ondataprepared() {

  }

  @override
  public void drawframe(glescanvas canvas, float segmentprogress) {
    //下一个片段开始放大
    float nextscale = mnextscalefrom + (mnextscaleto - mnextscalefrom) * segmentprogress;
    mnextsegment.drawcontent(canvas, nextscale);

    //上一个片段继续放大同时变透明
    float prescale = mprescalefrom + (mprescaleto - mprescalefrom) * segmentprogress;
    float alpha = 1 - segmentprogress;
    mpresegment.drawbackground(canvas);
    canvas.save();
    canvas.setalpha(alpha);
    mpresegment.drawcontent(canvas, prescale);
    canvas.restore();
  }

创建照片电影

private static photomovie initgradientphotomovie(photosource photosource) {
    list<moviesegment> segmentlist = new arraylist<>(photosource.size());
    for (int i = 0; i < photosource.size(); i++) {
      if (i == 0) {
        segmentlist.add(new fitcenterscalesegment(1600, 1f, 1.1f));
      } else {
        segmentlist.add(new fitcenterscalesegment(1600, 1.05f, 1.1f));
      }
      if (i < photosource.size() - 1) {
        segmentlist.add(new gradienttransfersegment(800, 1.1f, 1.15f, 1.0f, 1.05f));
      }
    }
    return new photomovie(photosource, segmentlist);
  }

然后将这个photomovie正常播放即可,效果如下

Android高仿抖音照片电影功能的实现代码

扩展滤镜

目前内置了9个滤镜

public enum filtertype {
  none,
  cameo,//浮雕
  gray,//黑白
  kuwahara,//水彩
  snow,//飘雪(动态)
  lut1,
  lut2,
  lut3,
  lut4,
  lut5,
}

先看imoviefilter

public interface imoviefilter {
  void dofilter(photomovie photomovie,int elapsedtime, fbotexture inputtexture, fbotexture outputtexture);
  void release();
}

外部会提供一个输入纹理,然后由imoviefilter处理之后绘制到输出纹理上,即实现了滤镜效果

basemoviefilter已经实现了基本的输入输出流程,例如要做最基本的黑白滤镜,只需更换fragment_shader即可

public class graymoviefilter extends basemoviefilter {
  protected static final string fragment_shader = "" +
      "varying highp vec2 texturecoordinate;\n" +
      " \n" +
      "uniform sampler2d inputimagetexture;\n" +
      " \n" +
      "void main()\n" +
      "{\n" +
      "   mediump vec4 color = texture2d(inputimagetexture, texturecoordinate);\n" +
      "   mediump float gray = color.r*0.3+color.g*0.59+color.b*0.11;\n"+
      "   gl_fragcolor = vec4(gray,gray,gray,1.0);\n"+
      "}";
  public graymoviefilter(){
    super(vertex_shader,fragment_shader);
  }
}

同时photomovie提供了对lut滤镜的支持

lut其实就是lookup table(颜色查找表),根据原图的rgb值去相应的lut图里面查找对应转换后的rgb值,从而实现各种滤镜效果

Android高仿抖音照片电影功能的实现代码

lut(原图)

Android高仿抖音照片电影功能的实现代码

lut_2.jpg

Android高仿抖音照片电影功能的实现代码

原图

Android高仿抖音照片电影功能的实现代码

滤镜效果图

public class lutmoviefilter extends twotexturemoviefilter {

  public lutmoviefilter(bitmap lutbitmap){
    super(loadshaderfromassets("shader/two_vertex.glsl"),loadshaderfromassets("shader/lut.glsl"));
    setbitmap(lutbitmap);
  }
}

在lutmoviefilter的构造函数传入上面表格里的lut图,即可实现相应的滤镜效果,前面提到的黑白滤镜也可用这个方式实现

录制功能

glmovierecorder 提供了将照片电影录制为mp4的功能

可参照 demopresenter 的savevideo()函数

glmovierecorder recorder = new glmovierecorder();
    recorder.configoutput(width, height(), bitrate,framerate,iframeinterval, outputpath);
    recorder.setdatasource(movierenderer);
    recorder.startrecord(new glmovierecorder.onrecordlistener() {
      @override
      public void onrecordfinish(boolean success) {
        ......
      }

      @override
      public void onrecordprogress(int recordedduration, int totalduration) {
        ......
      }
    });

背景音乐

 mphotomovieplayer.setmusic(context, mmusicuri);

photomovie只提供了播放背景音乐的功能,录制完成之后需自行合成,demo里使用了

videoprocessor 进行合成

 videoprocessor.mixaudiotrack(context, videpath, audiopath,outputpath, null, null, 0,100, 1f, 1f);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。