FFmpeg - 视频幻灯片图像之间的过渡效应

时间:2017-04-21 05:05:37

标签: android video ffmpeg android-image

尝试获得完美的图像动画效果命令。

这是我从图像创建视频的命令,但我想在两个图像之间添加图像过渡效果(例如fadein / fadeout)。

  init();
    function init() { 
      var  el=document.getElementById('chart');
      var map = L.map( 'map', {
        center: [20.5937,78.9629],
        minZoom: 2,
        zoom: 4
    });
    L.tileLayer( 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {

        subdomains: ['a','b','c']
    }).addTo( map );




     var mywms = L.tileLayer.wms("http://localhost:7770/geoserver/cite/wms", {
        layers: 'cite:state1',
        format: 'image/png',
        transparent: true

    });
    mywms.addTo(map);
          data=[
        [
                  { x: 1999, y : 1838.21, color: "#ccccff"},
                  { x: 2000, y : 2148.01,color: "#b2b2ff" },
                  { x: 2001, y : 2537.06,color: "#9999ff" },
                  { x: 2002, y : 2089.29 ,color: "#7f7fff"},
                  { x: 2003, y : 3276.78 ,color: "#6666ff"},
                  { x: 2004, y : 2544.60,color: "#4c4cff"},
                  { x: 2005, y : 2676.42 ,color: "#3232ff"}
        ],

          [
                  { x: 1999, y : 1475.22, color: "#ccccff"},
                  { x: 2000, y : 1354.99,color: "#b2b2ff" },
                  { x: 2001, y : 1861.67,color: "#9999ff" },
                  { x: 2002, y : 1427.08 ,color: "#7f7fff"},
                  { x: 2003, y : 1717.2 ,color: "#6666ff"},
                  { x: 2004, y : 1676.91,color: "#4c4cff"},
                  { x: 2005, y : 1900.02 ,color: "#3232ff"}
        ],


        [
                  { x: 1999, y : 2645.43, color: "#ccccff"},
                  { x: 2000, y : 2649.38,color: "#b2b2ff" },
                  { x: 2001, y : 2883.1,color: "#9999ff" },
                  { x: 2002, y : 2689.88 ,color: "#7f7fff"},
                  { x: 2003, y : 3332.6 ,color: "#6666ff"},
                  { x: 2004, y :3473.52,color: "#4c4cff"},
                  { x: 2005, y : 3721.04 ,color: "#3232ff"}
        ] 



     ],
          loc=[


            [25.7532,71.4181],


            [24.7467,72.8043],

            [27.6094,75.1399],

            [25.7711, 73.3234],

            [27.1854,74.0300],

            [28.1289,75.3995],

            [28.3254,74.4057],

            [29.9038,73.8772],

            [29.1547,74.4995],

            [26.9157,70.9083],

            [25.1257,72.1416],

            [26.28, 73.02],

            [28.0229,73.3119],
     ];

      for(var i=0;i<loc.length;i++){
        var marker=L.marker(loc[i]).addTo(map).bindPopup(el),
            options={index:i,marker:marker};
        marker.on('click', onMarkerClick, options);//'options' object will be assigned to 'this' object, when click event occures

 marker.on('mouseover', function(a) {

    //open popup;
      var popup = L.popup()
       .setLatLng(a.loc) 
       .setContent('District:')
       .openOn(map);

    });

     }  

      function onMarkerClick(e) {   
        //'this' object will be having 'options' object
        var index=this.index,
            marker=this.marker;   
         $('#chart').css('display', 'block');
         var chart = new CanvasJS.Chart(el, {
            title:{
                text: "My First Chart in CanvasJS"              
            },

        backgroundColor: "#ffffff",
        zoomEnabled: true,
        animationEnabled: true,
        animationDuration: 4000,

          axisY:{
           title:"GDP (in Rs. Cr.)",
          valueFormatString:"#####.00",
          lineColor: "black",
          labelFontSize: 15,
               labelFontColor: "red",
               titleFontColor: "black", 
           },
          axisX:{
                title:"Year",
               valueFormatString:"####",
               labelFontSize: 15,
               labelFontColor: "red",
               lineColor: "black",
               titleFontColor: "black",
               titleFontSize: 20,
            },
            data: [              
            {
                type: "column",
                xValueFormatString:"Year ####",
            dataPoints:data[index]             
            }
            ]
        });
        chart.render();    
        marker.openPopup();    

      }
    }

1 个答案:

答案 0 :(得分:2)

在此项目中,您可以有4种效果

  • 没有效果
  • 淡入
  • 旋转
  • 的slideIn

  • 将图像添加到Parcelable [],这些将是您的输入图像。(您也可以使用位图数组或其他内容)

  • 将这些图片传递给效果活动类:

<强> 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.)