溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

發(fā)布時(shí)間:2020-08-31 02:14:06 來(lái)源:腳本之家 閱讀:266 作者:yellowcath 欄目:移動(dòng)開發(fā)

PhotoMovie (https://github.com/yellowcath/PhotoMovie) 可輕松實(shí)現(xiàn)類似抖音、微視、美拍的照片電影功能。

效果如下

濾鏡效果

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

轉(zhuǎn)場(chǎng)效果

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

使用

首先在項(xiàng)目根目錄的build.gradle文件里添加maven庫(kù)

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,photoPath2,PhotoData.STATE_LOCAL));
    ...
    photoDataList.add(new SimplePhotoData(context,photoPathN,PhotoData.STATE_LOCAL));
    //生成圖片源
    PhotoSource photoSource = new PhotoSource(photoDataList);
    //生成照片電影(使用預(yù)定義的水平轉(zhuǎn)場(chǎng)動(dòng)畫)
    PhotoMovie photoMovie = PhotoMovieFactory.generatePhotoMovie(photoSource, PhotoMovieFactory.PhotoMovieType.HORIZONTAL_TRANS);
    //生成負(fù)責(zé)繪制電影內(nèi)容的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();

輕松擴(kuò)展

PhotoMovie使用模塊化的設(shè)計(jì),每個(gè)部分都可以自定義然后替換,

  • MovieSegment:電影片段,每個(gè)電影片段都有特定的時(shí)長(zhǎng),在這段時(shí)間之內(nèi)以特定的方式播放圖片,例如ScaleSegment會(huì)對(duì)圖片做縮放動(dòng)畫、EndGaussianBlurSegment會(huì)對(duì)圖片做從清晰到模糊的高斯模糊動(dòng)畫
  • PhotoMovie:核心類,代表照片電影本身,由圖片源(PhotoSource)和若干電影片段(MovieSegment)組成一個(gè)完整的照片電影,圖片通過(guò)PhotoAllocator分配給MovieSegment
  • MovieLayer:為MovieSegment擴(kuò)展繪制多層特效的功能,例如SubtitleLayer提供字幕展示
  • IMovieFilter:為整個(gè)照片電影提供濾鏡
  • MovieRenderer:負(fù)責(zé)把照片電影渲染到指定的輸出界面,例如TextureView(GLTextureMovieRender)、GLSurfaceView(GLSurfaceMovieRenderer)
  • PhotoMoviePlayer:提供類似MediaPlayer的接口,負(fù)責(zé)播放照片電影,播放進(jìn)度由IMovieTimer控制

擴(kuò)展電影類型

目前內(nèi)置了6種類型,后兩種即是抖音的左右切換和上下切換,Thaw和WINDOW仿自美拍

public enum PhotoMovieType {
    THAW, //融雪
    SCALE, //縮放
    SCALE_TRANS, //縮放 & 平移
    WINDOW, //窗扉
    HORIZONTAL_TRANS,//橫向平移
    VERTICAL_TRANS//縱向平移
  }

這里以微視的漸變特效為例展示如何擴(kuò)展

分析得出,漸變特效首先圖片居中放置,然后全程做一個(gè)微弱的放大動(dòng)畫,后半部分同時(shí)透明度變化消失

可見需要兩個(gè)不同的片段類型

首先創(chuàng)建FitCenterScaleSegment,繼承FitCenterSegment,實(shí)現(xiàn)單張圖片的放大動(dòng)畫

public class FitCenterScaleSegment extends FitCenterSegment {
  /**
   * 縮放動(dòng)畫范圍
   */
  private float mScaleFrom;
  private float mScaleTo;

  private float mProgress;

  /**
   * @param duration 片段時(shí)長(zhǎng)
   * @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已經(jīng)具有縮放能力,這里傳縮放值即可
    drawContent(canvas, scale);
  }
  //提升這兩個(gè)函數(shù)的訪問(wèn)權(quán)限,供轉(zhuǎn)場(chǎng)時(shí)使用
    @Override
  public void drawContent(GLESCanvas canvas, float scale) {
    super.drawContent(canvas, scale);
  }

  @Override
  public void drawBackground(GLESCanvas canvas) {
    super.drawBackground(canvas);
  }
}

然后創(chuàng)建轉(zhuǎn)場(chǎng)片段GradientTransferSegment,其父類TransitionSegment同時(shí)持有上一個(gè)與下一個(gè)片段,可以在其基礎(chǔ)上實(shí)現(xiàn)任意轉(zhuǎn)場(chǎng)功能

public class GradientTransferSegment extends TransitionSegment<FitCenterScaleSegment, FitCenterScaleSegment> {
  /**
   * 縮放動(dòng)畫范圍
   */
  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) {
    //下一個(gè)片段開始放大
    float nextScale = mNextScaleFrom + (mNextScaleTo - mNextScaleFrom) * segmentProgress;
    mNextSegment.drawContent(canvas, nextScale);

    //上一個(gè)片段繼續(xù)放大同時(shí)變透明
    float preScale = mPreScaleFrom + (mPreScaleTo - mPreScaleFrom) * segmentProgress;
    float alpha = 1 - segmentProgress;
    mPreSegment.drawBackground(canvas);
    canvas.save();
    canvas.setAlpha(alpha);
    mPreSegment.drawContent(canvas, preScale);
    canvas.restore();
  }

創(chuàng)建照片電影

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);
  }

然后將這個(gè)PhotoMovie正常播放即可,效果如下

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

擴(kuò)展濾鏡

目前內(nèi)置了9個(gè)濾鏡

public enum FilterType {
  NONE,
  CAMEO,//浮雕
  GRAY,//黑白
  KUWAHARA,//水彩
  SNOW,//飄雪(動(dòng)態(tài))
  LUT1,
  LUT2,
  LUT3,
  LUT4,
  LUT5,
}

先看IMovieFilter

public interface IMovieFilter {
  void doFilter(PhotoMovie photoMovie,int elapsedTime, FboTexture inputTexture, FboTexture outputTexture);
  void release();
}

外部會(huì)提供一個(gè)輸入紋理,然后由IMovieFilter處理之后繪制到輸出紋理上,即實(shí)現(xiàn)了濾鏡效果

BaseMovieFilter已經(jīng)實(shí)現(xiàn)了基本的輸入輸出流程,例如要做最基本的黑白濾鏡,只需更換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);
  }
}

同時(shí)PhotoMovie提供了對(duì)Lut濾鏡的支持

Lut其實(shí)就是Lookup Table(顏色查找表),根據(jù)原圖的RGB值去相應(yīng)的lut圖里面查找對(duì)應(yīng)轉(zhuǎn)換后的RGB值,從而實(shí)現(xiàn)各種濾鏡效果

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

lut(原圖)

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

lut_2.jpg

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

原圖

Android高仿抖音照片電影功能的實(shí)現(xiàn)代碼

濾鏡效果圖

public class LutMovieFilter extends TwoTextureMovieFilter {

  public LutMovieFilter(Bitmap lutBitmap){
    super(loadShaderFromAssets("shader/two_vertex.glsl"),loadShaderFromAssets("shader/lut.glsl"));
    setBitmap(lutBitmap);
  }
}

在LutMovieFilter的構(gòu)造函數(shù)傳入上面表格里的lut圖,即可實(shí)現(xiàn)相應(yīng)的濾鏡效果,前面提到的黑白濾鏡也可用這個(gè)方式實(shí)現(xiàn)

錄制功能

GLMovieRecorder 提供了將照片電影錄制為mp4的功能

可參照 DemoPresenter 的saveVideo()函數(shù)

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) {
        ......
      }
    });

背景音樂(lè)

 mPhotoMoviePlayer.setMusic(context, mMusicUri);

PhotoMovie只提供了播放背景音樂(lè)的功能,錄制完成之后需自行合成,Demo里使用了

VideoProcessor 進(jìn)行合成

 VideoProcessor.mixAudioTrack(context, videPath, audioPath,outputPath, null, null, 0,100, 1f, 1f);

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI