2017-04-21 112 views
0

試圖獲得圖像動畫效果的完美命令。FFmpeg - 視頻幻燈片圖像間的過渡效果

這是我的命令從圖像的視頻,但我想添加圖片過渡效果的兩個圖像之間(例如被淡入/淡出)。

public static String[] cmdCreateVideo(int frame,String videoSource, String outPut){ 
    float duration_frame=1.0F; 
    if (frame<= 20) { 
     duration_frame = 1.0F; 
    }else { 
     duration_frame = 0.6F; 
    } 
    String str5 = "-framerate 1/" + duration_frame + " -start_number 0 -i " + videoSource + " -vcodec mpeg4 -q:v 3 -r 20 -vf scale=480x800 " +outPut; 
    return str5.split(" "); 
} 
+0

http://stackoverflow.com/questions/21493797/how-to-fade -two-images-with-ffmpeg – Hangman

+0

它不適合我。 PLZ給出另一種解決方案。 –

+0

@praveens都不[**這些建議**](https://www.google.co.uk/?q=%22ffmpeg%22+video+slideshow+fade+effect)的工作?不管怎麼說最好更好地解釋它是如何_「不工作」 _喜歡有任何** **錯誤消息?另外**顯示你試過的是什麼/如何的代碼**,以便有人可以發現代碼中的任何問題。 –

回答

1

在這個項目中,你可以有4種效果

  • 沒有影響
  • 淡入
  • 旋轉
  • 中的slideIn

  • 圖像添加到Parcelable [ ],這些將是y 。我們的輸入圖像(你也可以使用位圖陣列AOR其他東西)

  • 通過這些圖像效果的活動類:

Effectactivity.java

import android.app.Activity; 
import android.app.AlertDialog; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.graphics.Bitmap; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.os.Parcelable; 
import android.support.v4.view.PagerAdapter; 
import android.support.v4.view.ViewPager; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.ImageView; 
import android.widget.TextView; 
import android.widget.Toast; 

import com.bumptech.glide.Glide; 
import com.bumptech.glide.load.engine.DiskCacheStrategy; 
import com.bumptech.glide.request.target.Target; 
import com.hini.slideshow.R; 
import com.hini.slideshow.SlideApplication; 
import com.hini.slideshow.draw.SlideShow; 
import com.hini.slideshow.encoding.SlideEncoder; 

import java.util.concurrent.ExecutionException; 

/** 
* Created by PrakashSaurav. 
*/ 
public class EffectActivity extends Activity implements View.OnClickListener { 
    private static final String TAG = "EffectActivity"; 
    private ViewPager mViewPager; 
    private Button mBtnBitRate, mBtnTime, mBtnEffect, mBtnNext, mBtnPrev; 
    private TextView mTvBitRate, mTvTime, mTvEffect; 
    private Parcelable[] mParcelableUris; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_effect); 

     mParcelableUris = getIntent().getParcelableArrayExtra("parcelableUris"); 
     if (mParcelableUris == null) { 
      Toast.makeText(getApplicationContext(), "There is no path info for the photo.", Toast.LENGTH_SHORT).show(); 
      return; 
     } 
     mViewPager = (ViewPager) findViewById(R.id.pager_image); 
     mViewPager.setAdapter(new CustomPageAdapter()); 

     mBtnBitRate = (Button) findViewById(R.id.btn_bgm); 
     mTvBitRate = (TextView) findViewById(R.id.tv_bitrate); 

     mBtnTime = (Button) findViewById(R.id.btn_time); 
     mTvTime = (TextView) findViewById(R.id.tv_time); 

     mBtnEffect = (Button) findViewById(R.id.btn_effect); 
     mTvEffect = (TextView) findViewById(R.id.tv_effect); 

     mBtnNext = (Button) findViewById(R.id.btn_next); 
     mBtnPrev = (Button) findViewById(R.id.btn_prev); 

     mBtnBitRate.setOnClickListener(this); 
     mBtnTime.setOnClickListener(this); 
     mBtnEffect.setOnClickListener(this); 
     mBtnNext.setOnClickListener(this); 
     mBtnPrev.setOnClickListener(this); 
    } 

    int timeCheck = 0; 
    int effectCheck = 0; 

    boolean isClicked; 

    @Override 
    public void onClick(View v) { 
     // BGM Setting 
     if (v == mBtnBitRate) { 
      isClicked = !isClicked; 
      int bitRate; 
      if (isClicked) 
       bitRate = 2000 * 1024; 
      else 
       bitRate = 500 * 1024; 
      SlideApplication.BIT_RATE = bitRate; 
      mTvBitRate.setText(String.valueOf(bitRate/1024) + "kbps"); 
      Toast.makeText(getApplicationContext(), SlideApplication.BIT_RATE/1024 + "kbps", Toast.LENGTH_SHORT).show(); 
     } 
     // Time Setting 
     else if (v == mBtnTime) { 
      AlertDialog.Builder builder = new AlertDialog.Builder(this); 
      final String[] items = {"2s", "3s", "5s", "10s"}; 

      if (SlideApplication.SLIDE_TIME == 2) timeCheck = 0; 
      else if (SlideApplication.SLIDE_TIME == 3) timeCheck = 1; 
      else if (SlideApplication.SLIDE_TIME == 5) timeCheck = 2; 
      else if (SlideApplication.SLIDE_TIME == 10) timeCheck = 3; 

      builder.setSingleChoiceItems(items, timeCheck, new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        timeCheck = which; 
        Toast.makeText(getApplicationContext(), items[which], Toast.LENGTH_SHORT).show(); 
       } 
      }); 
      builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        dialog.dismiss(); 
       } 
      }); 
      builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        SlideApplication.SLIDE_TIME = Integer.parseInt(items[timeCheck].split("초")[0]); 
        mTvTime.setText(Integer.toString(SlideApplication.SLIDE_TIME)); 
       } 
      }); 
      AlertDialog dialog = builder.create(); 
      dialog.show(); 
     } 
     // Effect Setting 
     else if (v == mBtnEffect) { 
      AlertDialog.Builder builder = new AlertDialog.Builder(this); 
      final String[] items = {"None", "FadeIn", "Rotate", "SlideIn"}; 
      if (SlideApplication.SLIDE_EFFECT == SlideShow.NONE) effectCheck = 0; 
      else if (SlideApplication.SLIDE_EFFECT == SlideShow.FADE_IN) effectCheck = 1; 
      else if (SlideApplication.SLIDE_EFFECT == SlideShow.ROTATE) effectCheck = 2; 
      else if (SlideApplication.SLIDE_EFFECT == SlideShow.SLIDE_IN) effectCheck = 3; 

      builder.setSingleChoiceItems(items, effectCheck, new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        effectCheck = which; 
        Toast.makeText(getApplicationContext(), items[which], Toast.LENGTH_SHORT).show(); 
       } 
      }); 
      builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        dialog.dismiss(); 
       } 
      }); 
      builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        SlideApplication.SLIDE_EFFECT = effectCheck; 
        mTvEffect.setText(items[SlideApplication.SLIDE_EFFECT]); 
       } 
      }); 
      AlertDialog dialog = builder.create(); 
      dialog.show(); 
     } 
     // Next 
     else if (v == mBtnNext) { 
      new BitmapChangerTask().execute(); 
     } 
     // Prev 
     else if (v == mBtnPrev) { 
      finish(); 
     } 
    } 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
     super.onActivityResult(requestCode, resultCode, data); 
     if (requestCode == 1) { 
      if (resultCode == RESULT_OK) { 
       if (data != null && data.getStringExtra("bgm_path") != null) { 
        SlideApplication.BGM_PATH = data.getStringExtra("bgm_path"); 
       } 
      } 
     } 
    } 

    private class BitmapChangerTask extends AsyncTask<Void, Void, Void> { 

     @Override 
     protected Void doInBackground(Void... params) { 
      for (int i = 0; i < mParcelableUris.length; i++) { 
       try { 
        Bitmap bm = Glide.with(getApplicationContext()).load(mParcelableUris[i].toString()) 
          .asBitmap() 
          .diskCacheStrategy(DiskCacheStrategy.ALL) 
          .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) 
          .get(); 
        int width = bm.getWidth(); 
        int height = bm.getHeight(); 

        // Land (1280 x 720) 
        if (width > height) { 
         bm = Bitmap.createScaledBitmap(bm, SlideEncoder.WIDTH, ((SlideEncoder.WIDTH * height)/width), true); 
        } 
        // Port (720 x 1280) 
        else if (width < height) { 
         bm = Bitmap.createScaledBitmap(bm, ((SlideEncoder.HEIGHT * width)/height), SlideEncoder.HEIGHT, true); 
        } 
        // Square (800 x 800) 
        else { 
         bm = Bitmap.createScaledBitmap(bm, SlideEncoder.WIDTH, SlideEncoder.HEIGHT, true); 
        } 
        SlideApplication.bitmapList.add(bm); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } catch (ExecutionException e) { 
        e.printStackTrace(); 
       } 
      } 
      return null; 
     } 

     @Override 
     protected void onPostExecute(Void aVoid) { 
      super.onPostExecute(aVoid); 
      Intent i = new Intent(getApplicationContext(), EncodingActivity.class); 
      startActivity(i); 
     } 
    } 


    private class CustomPageAdapter extends PagerAdapter { 

     @Override 
     public int getCount() { 
      return mParcelableUris.length; 
     } 

     @Override 
     public int getItemPosition(Object object) { 
      return POSITION_NONE; 
     } 


     @Override 
     public Object instantiateItem(ViewGroup container, int position) { 
      ImageView iv = new ImageView(getApplicationContext()); 
      Glide.with(getApplicationContext()) 
        .load(mParcelableUris[position].toString()) 
        .diskCacheStrategy(DiskCacheStrategy.ALL) 
        .into(iv); 
      try { 
       container.addView(iv); 
      } catch (IllegalStateException ise) { 
       ise.printStackTrace(); 
      } 

      return iv; 
     } 

     @Override 
     public void destroyItem(ViewGroup container, int position, Object object) { 
      container.removeView((View) object); 
     } 

     @Override 
     public boolean isViewFromObject(View view, Object object) { 
      return view == object; 
     } 
    } 
} 










EffectActivity.xml 

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent"> 

    <android.support.v4.view.ViewPager 
     android:id="@+id/pager_image" 
     android:layout_width="match_parent" 
     android:layout_height="350dp"> 
    </android.support.v4.view.ViewPager> 

    <Button 
     android:id="@+id/btn_bgm" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_below="@+id/pager_image" 
     android:layout_marginStart="80dp" 
     android:layout_marginTop="30dp" 
     android:text="BitRate"/> 

    <TextView 
     android:id="@+id/tv_bitrate" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignBaseline="@+id/btn_bgm" 
     android:layout_alignParentEnd="true" 
     android:layout_below="@+id/pager_image" 
     android:layout_marginEnd="80dp" 
     android:singleLine="true" 
     android:ellipsize="marquee" 
     android:text="500Kbps" 
     android:textSize="20dp"/> 

    <Button 
     android:id="@+id/btn_time" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_below="@+id/btn_bgm" 
     android:layout_marginStart="80dp" 
     android:text="time"/> 


    <TextView 
     android:id="@+id/tv_time" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignBaseline="@+id/btn_time" 
     android:layout_alignParentEnd="true" 
     android:layout_below="@+id/pager_image" 
     android:layout_marginEnd="80dp" 
     android:text="2s" 
     android:textSize="20dp"/> 


    <Button 
     android:id="@+id/btn_effect" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_below="@+id/btn_time" 
     android:layout_marginStart="80dp" 
     android:text="EFFECT"/> 


    <TextView 
     android:id="@+id/tv_effect" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignBaseline="@+id/btn_effect" 
     android:layout_alignParentEnd="true" 
     android:layout_below="@+id/pager_image" 
     android:layout_marginEnd="80dp" 
     android:text="none" 
     android:textSize="20dp"/> 

    <Button 
     android:id="@+id/btn_prev" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentBottom="true" 
     android:layout_alignParentStart="true" 
     android:text="Previous"/> 

    <Button 
     android:id="@+id/btn_next" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentBottom="true" 
     android:layout_alignParentEnd="true" 
     android:text="NEXT"/> 
</RelativeLayout> 

EncodingActivity.java

import android.app.Activity; 
import android.app.ProgressDialog; 
import android.content.Intent; 
import android.graphics.Bitmap; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.os.Environment; 
import android.util.Log; 

import com.hini.slideshow.SlideApplication; 
import com.hini.slideshow.draw.SlideShow; 
import com.hini.slideshow.encoding.SlideEncoder; 

import java.io.File; 
import java.io.IOException; 


public class EncodingActivity extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     new EncodingTask().execute(); 
    } 

    class EncodingTask extends AsyncTask<Void, String, Void> { 
     File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() 
       + "/test_" + SlideApplication.BIT_RATE/1024 + "_" + SlideApplication.SLIDE_EFFECT + ".mp4"); 
     ProgressDialog dialog; 

     @Override 
     protected void onPreExecute() { 
      super.onPreExecute(); 
      dialog = new ProgressDialog(EncodingActivity.this); 
      dialog.setTitle("Generating Video.."); 
      dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 
      dialog.setCancelable(false); 
      dialog.show(); 
     } 

     @Override 
     protected Void doInBackground(Void... params) { 
      long startTime = System.currentTimeMillis(); 
      if (f.exists()) f.delete(); 
      SlideEncoder slideEncoder = new SlideEncoder(); 

      try { 
       slideEncoder.prepareEncoder(f); 
       Bitmap prevBm = null; 
       dialog.setMax(SlideApplication.bitmapList.size()); 
       for (int idx = 0; idx < SlideApplication.bitmapList.size(); idx++) { 
        publishProgress(String.valueOf(idx + 1)); 
        SlideShow.init(); 

        if (idx > 0) prevBm = SlideApplication.bitmapList.get(idx - 1); 
        Bitmap curBm = SlideApplication.bitmapList.get(idx); 
        for (int i = 0; i < (SlideApplication.FRAME_PER_SEC * SlideApplication.SLIDE_TIME); i++) { 
         // Drain any data from the encoder into the muxer. 
         slideEncoder.drainEncoder(false); 

         // Generate a frame and submit it. 
         slideEncoder.generateFrame(prevBm, curBm); 
        } 
       } 
       slideEncoder.drainEncoder(true); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } finally { 
       slideEncoder.releaseEncoder(); 
      } 
      Log.e("TAG", "total time : " + (System.currentTimeMillis() - startTime)); 
      return null; 
     } 

     @Override 
     protected void onProgressUpdate(String... values) { 
      super.onProgressUpdate(values); 
      dialog.setProgress(Integer.parseInt(values[0])); 
     } 

     @Override 
     protected void onPostExecute(Void aVoid) { 
      super.onPostExecute(aVoid); 
      if (dialog.isShowing()) dialog.dismiss(); 
      for (Bitmap bm : SlideApplication.bitmapList) bm.recycle(); 
      SlideApplication.bitmapList.clear(); 
      Intent i = new Intent(EncodingActivity.this, MainActivity.class); 
      i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
      startActivity(i); 
     } 
    } 





    *****SlideEncoder.java***** 



import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.media.MediaCodec; 
import android.media.MediaCodecInfo; 
import android.media.MediaFormat; 
import android.media.MediaMuxer; 
import android.view.Surface; 

import com.hini.slideshow.SlideApplication; 
import com.hini.slideshow.draw.SlideShow; 

import java.io.File; 
import java.io.IOException; 
import java.nio.ByteBuffer; 


public class SlideEncoder { 
    private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC; 
    public static final int WIDTH = 800; 
    public static final int HEIGHT = 800; 
    private static final int IFRAME_INTERVAL = 3; 
    private MediaCodec.BufferInfo mBufferInfo; 
    private MediaCodec mEncoder; 
    private Surface mInputSurface; 
    private MediaMuxer mMuxer; 
    private int mTrackIndex; 
    private boolean mMuxerStarted; 
    private long mFakePts; 

    void prepareEncoder(File outputFile) throws IOException { 
     mBufferInfo = new MediaCodec.BufferInfo(); 

     MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT); 

     // Set some properties. Failing to specify some of these can cause the MediaCodec 
     // configure() call to throw an unhelpful exception. 
     format.setInteger(MediaFormat.KEY_COLOR_FORMAT, 
       MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); 
     format.setInteger(MediaFormat.KEY_BIT_RATE, SlideApplication.BIT_RATE); 
     format.setInteger(MediaFormat.KEY_FRAME_RATE, SlideApplication.FRAME_PER_SEC); 
     format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); 

     // Create a MediaCodec encoder, and configure it with our format. Get a Surface 
     // we can use for input and wrap it with a class that handles the EGL work. 
     mEncoder = MediaCodec.createEncoderByType(MIME_TYPE); 
     mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
     mInputSurface = mEncoder.createInputSurface(); 
     mEncoder.start(); 

     // Create a MediaMuxer. We can't add the video track and start() the muxer here, 
     // because our MediaFormat doesn't have the Magic Goodies. These can only be 
     // obtained from the encoder after it has started processing data. 
     // 
     // We're not actually interested in multiplexing audio. We just want to convert 
     // the raw H.264 elementary stream we get from MediaCodec into a .mp4 file. 
     mMuxer = new MediaMuxer(outputFile.toString(), 
       MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 

     mTrackIndex = -1; 
     mMuxerStarted = false; 
    } 

    /** 
    * MediaCodec, InputSurface, MediaMuxer 해제 
    */ 
    public void releaseEncoder() { 
     if (mEncoder != null) { 
      mEncoder.stop(); 
      mEncoder.release(); 
      mEncoder = null; 
     } 
     if (mInputSurface != null) { 
      mInputSurface.release(); 
      mInputSurface = null; 
     } 
     if (mMuxer != null) { 
      mMuxer.stop(); 
      mMuxer.release(); 
      mMuxer = null; 
     } 
    } 

    /** 
    * Extracts all pending data from the encoder. 
    * <p/> 
    * If endOfStream is not set, this returns when there is no more data to drain. If it 
    * is set, we send EOS to the encoder, and then iterate until we see EOS on the output. 
    * Calling this with endOfStream set should be done once, right before stopping the muxer. 
    */ 
    public void drainEncoder(boolean endOfStream) { 
     final int TIMEOUT_USEC = 10000; 

     if (endOfStream) { 
      mEncoder.signalEndOfInputStream(); 
     } 

     ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers(); 
     while (true) { 
      int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); 
      if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
       // no output available yet 
       if (!endOfStream) { 
        break;  // out of while 
       } else { 
       } 
      } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
       // not expected for an encoder 
       encoderOutputBuffers = mEncoder.getOutputBuffers(); 
      } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
       // should happen before receiving buffers, and should only happen once 
       if (mMuxerStarted) { 
        throw new RuntimeException("format changed twice"); 
       } 
       MediaFormat newFormat = mEncoder.getOutputFormat(); 

       // now that we have the Magic Goodies, start the muxer 
       mTrackIndex = mMuxer.addTrack(newFormat); 
       mMuxer.start(); 
       mMuxerStarted = true; 
      } else if (encoderStatus < 0) { 
       // let's ignore it 
      } else { 
       ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 
       if (encodedData == null) { 
        throw new RuntimeException("encoderOutputBuffer " + encoderStatus + 
          " was null"); 
       } 

       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { 
        // The codec config data was pulled out and fed to the muxer when we got 
        // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it. 
        mBufferInfo.size = 0; 
       } 

       if (mBufferInfo.size != 0) { 
        if (!mMuxerStarted) { 
         throw new RuntimeException("muxer hasn't started"); 
        } 

        // adjust the ByteBuffer values to match BufferInfo 
        encodedData.position(mBufferInfo.offset); 
        encodedData.limit(mBufferInfo.offset + mBufferInfo.size); 
        mBufferInfo.presentationTimeUs = mFakePts; 
        long timeStampLength = 1000000L/SlideApplication.FRAME_PER_SEC; 
        mFakePts += timeStampLength; 

        mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo); 
       } 

       mEncoder.releaseOutputBuffer(encoderStatus, false); 

       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
        if (!endOfStream) { 
        } else { 
        } 
        break;  // out of while 
       } 
      } 
     } 
    } 

    /** 
    * Generates a frame, writing to the Surface via the "software" API (lock/unlock). 
    * <p/> 
    * There's no way to set the time stamp. 
    */ 
    public void generateFrame(Bitmap prevBm, Bitmap curBm) { 
     Canvas canvas = mInputSurface.lockCanvas(null); 
     try { 
      SlideShow.draw(canvas, prevBm, curBm); 
     } finally { 
      mInputSurface.unlockCanvasAndPost(canvas); 
     } 
    } 
} 

SlideShow.java

import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 

import com.appwallet.slideshow.SlideApplication; 
import com.appwallet.slideshow.encoding.SlideEncoder; 


public class SlideShow { 
    // effect 
    public static final int NONE = 0; 
    public static final int FADE_IN = 1; 
    public static final int ROTATE = 2; 
    public static final int SLIDE_IN = 3; 

    // 위치 
    private static int curStartX; 
    private static int curStartY; 
    private static int prevStartX; 
    private static int prevStartY; 

    //Variables used for effect 
    private static float in_alpha = 0f; 
    private static float rotate = 0; 
    private static int slideX = SlideEncoder.WIDTH; 
    private static int slideCount = 1; 
    private static float out_alpha = 255f; 

    /** 
    * 
    * @param canvas canvas 
    * @param prevBm Background Bitmap 
    * @param curBm Foregound Bitmap 
    */ 
    public static void draw(Canvas canvas, Bitmap prevBm, Bitmap curBm) { 
     if (canvas == null || curBm == null) return; 

     setLocation(prevBm, curBm); 

     if (SlideApplication.SLIDE_EFFECT == ROTATE && prevBm == null) 
      canvas.drawColor(Color.BLACK); 

     if (prevBm != null) drawFadeOut(canvas, prevBm); 
     switch (SlideApplication.SLIDE_EFFECT) { 
      case NONE: 
       drawNone(canvas, curBm); 
       break; 
      case FADE_IN: 
       drawFadeIn(canvas, curBm); 
       break; 
      case ROTATE: 
       drawRotate(canvas, curBm); 
       break; 
      case SLIDE_IN: 
       drawSlideIn(canvas, curBm); 
       break; 
      default: 
       throw new IllegalStateException("unexpected state"); 
     } 
    } 

    /* 
    *Adjust position according to figure 
    * @param prevBm Background Bitmap 
    * @param curBm Foreground Bitmap 
    */ 
    private static void setLocation(Bitmap prevBm, Bitmap curBm) { 
     if (curBm != null) { 
      int cWidth = curBm.getWidth(); 
      int cHeight = curBm.getHeight(); 

      if (cWidth > cHeight) { 
       curStartX = 0; 
       curStartY = (SlideEncoder.HEIGHT - cHeight)/2; 
      } else if (cHeight > cWidth) { 
       curStartX = (SlideEncoder.WIDTH - cWidth)/2; 
       curStartY = 0; 
      } else { 
       curStartX = 0; 
       curStartY = 0; 
      } 
     } 

     if (prevBm != null) { 
      int pWidth = prevBm.getWidth(); 
      int pHeight = prevBm.getHeight(); 
      if (pWidth > pHeight) { 
       prevStartX = 0; 
       prevStartY = (SlideEncoder.HEIGHT - pHeight)/2; 
      } else if (pHeight > pWidth) { 
       prevStartX = (SlideEncoder.WIDTH - pWidth)/2; 
       prevStartY = 0; 
      } else { 
       prevStartX = 0; 
       prevStartY = 0; 
      } 
     } 
    } 

    /** 
    */ 
    public static void init() { 
     in_alpha = 0f; 
     out_alpha = 255f; 
     rotate = 0f; 
     slideX = 800; 
     slideCount = 1; 
     curStartX = 0; 
     curStartY = 0; 
     prevStartX = 0; 
     prevStartY = 0; 
    } 

    /** 
    * drawNone 
    * 
    * @param c canvas 
    * @param bm bitmap 
    */ 
    private static void drawNone(Canvas c, Bitmap bm) { 
     c.drawBitmap(bm, curStartX, curStartY, null); 
    } 


    * Fade in effect * 


    private static void drawFadeIn(Canvas c, Bitmap bm) { 
     Paint p = new Paint(); 
     int ratio = (int) Math.ceil(255/SlideApplication.FRAME_PER_SEC); 
     in_alpha += ratio; 
     if (in_alpha > 255f) in_alpha = 255f; 
     p.setAlpha((int) in_alpha); 
     c.drawBitmap(bm, curStartX, curStartY, p); 
    } 


    Rotate effect 

    private static void drawRotate(Canvas c, Bitmap bm) { 
     Matrix matrix = new Matrix(); 
     matrix.preTranslate(curStartX, curStartY); 
     float ratio = 360/SlideApplication.FRAME_PER_SEC; 
     rotate += Math.ceil(ratio); 
     if (rotate > 360) rotate = 360; 
     matrix.postRotate(rotate, SlideEncoder.WIDTH/2, SlideEncoder.HEIGHT/2); 
     c.drawBitmap(bm, matrix, null); 
    } 

    /** 
    * drawSlideIn drawSlideIn 
    * 
    * @param c canvas 
    * @param bm bitmap 
    */ 

    private static void drawSlideIn(Canvas c, Bitmap bm) { 
     Matrix matrix = new Matrix(); 
     int ratio = 1; 
     if (slideCount < 30) ratio = (int) Math.pow(slideCount++, 1.4); 
     slideX -= ratio; 
     if (slideX < curStartX) slideX = curStartX; 
     matrix.setTranslate(slideX, curStartY); 
     c.drawBitmap(bm, matrix, null); 

    } 

    /** 
    * 
    Fade-out effect (applies only to pictures that are drawn behind) 
    * 
    * @param c canvas 
    * @param bm bitmap 
    */ 

    private static void drawFadeOut(Canvas c, Bitmap bm) { 
     c.drawColor(Color.BLACK); 
     Paint p = new Paint(); 
     int ratio = (int) Math.ceil(255/SlideApplication.FRAME_PER_SEC); 
     out_alpha -= ratio; 
     if (out_alpha < 0f) out_alpha = 0f; 
     p.setAlpha((int) out_alpha); 
     c.drawBitmap(bm, prevStartX, prevStartY, p); 
    } 
} 

SlideApplication.java(爲了節省變量)

import android.app.Application; 
import android.graphics.Bitmap; 
import android.net.Uri; 
import android.os.Parcelable; 
import android.view.View; 

import com.appwallet.slideshow.draw.SlideShow; 

import java.util.ArrayList; 

public class SlideApplication extends Application { 
    public static String BGM_PATH = ""; 
    public static int SLIDE_TIME = 2; 
    public static int SLIDE_EFFECT = SlideShow.NONE; 
    public static int BIT_RATE = 2000 * 1024; 
    public static int FRAME_PER_SEC = 30; 
    public static ArrayList<Bitmap> bitmapList = new ArrayList<>(); 
    public static int overlayEffect=0; 
    public static Parcelable[] images; 
    public static String deleteFolderPath; 


} 

This is the effect editing activity,When you press NEXT video will be generated,(I upgraded the UI so after you paste code it wont look exactly like this.)