Browse Source

增加视频封面选择模块代码

Pq 1 year ago
parent
commit
b55ce80f2c
21 changed files with 1880 additions and 4 deletions
  1. 1 0
      BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt
  2. 32 0
      BaseLibrary/src/main/java/com/cooleshow/base/utils/FileUtils.java
  3. 67 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/adapter/SelectCoverAdapter.java
  4. 42 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/bean/LoadThumbResultBean.java
  5. 8 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/bean/VideoInfo.java
  6. 221 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/manager/CoverSelectorManager.java
  7. 22 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/manager/ICoverSelector.java
  8. 237 2
      musicMerge/src/main/java/com/cooleshow/musicmerge/ui/SelectVideoFrameActivity.java
  9. 228 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/CoverSliderView.java
  10. 363 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/RangeSlider.java
  11. 181 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/ShortVideoSelectCover.java
  12. 55 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/ThumbView.java
  13. 283 0
      musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/VideoPlayer.java
  14. 6 0
      musicMerge/src/main/res/drawable/cover_selector_slider.xml
  15. 7 0
      musicMerge/src/main/res/drawable/shape_video_cover_top_bg.xml
  16. 27 2
      musicMerge/src/main/res/layout/ac_select_video_frame_layout.xml
  17. 31 0
      musicMerge/src/main/res/layout/item_sv_cover_view.xml
  18. 42 0
      musicMerge/src/main/res/layout/video_selector_layout.xml
  19. 13 0
      musicMerge/src/main/res/values/attrs.xml
  20. 5 0
      musicMerge/src/main/res/values/dimens.xml
  21. 9 0
      musicMerge/src/main/res/values/strings.xml

+ 1 - 0
BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt

@@ -212,6 +212,7 @@ object RouterPath {
         companion object {
             const val MUSIC_TUNER_PAGE = "/musictuner/musictuner/MusicTunerActivity"
             const val MUSIC_MERGE_PAGE = "/musicmerge/musicmerge/MusicHandleActivity"
+            const val MUSIC_SELECT_VIDEO_FRAME = "/musicmerge/musicmerge/SelectVideoFrameActivity"
         }
     }
 

+ 32 - 0
BaseLibrary/src/main/java/com/cooleshow/base/utils/FileUtils.java

@@ -1742,6 +1742,38 @@ public final class FileUtils {
         }
     }
 
+    public static File saveImgToLocalFile(Bitmap bitmap) {
+        File parentFile = new File(FileUtils.getCacheDir(Utils.getApp()) + File.separator + "share");
+        if (!parentFile.exists()) {
+            parentFile.mkdirs();
+        }
+        String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
+        File file = new File(parentFile, targetFileName);
+        saveImageToLocalForJPEG(bitmap, file.getAbsolutePath());
+        return new File(file.getAbsolutePath());
+    }
+
+
+    /**
+     * 保存图片到图库
+     *
+     * @param bmp
+     */
+    public static void saveImageToLocalForJPEG(Bitmap bmp, String path) {
+        // 首先保存图片
+        File file = new File(path);
+        try {
+            FileOutputStream fos = new FileOutputStream(file);
+            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
+            fos.flush();
+            fos.close();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
     public static String getCompressPath() {
         String compressPath = getCacheDir(Utils.getApp()) + File.separator + "compress";
         File file = new File(compressPath);

+ 67 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/adapter/SelectCoverAdapter.java

@@ -0,0 +1,67 @@
+package com.cooleshow.musicmerge.adapter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.cooleshow.musicmerge.R;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class SelectCoverAdapter extends RecyclerView.Adapter<SelectCoverAdapter.ViewHolder> {
+    private final Context mContext;
+    @NonNull
+    private ArrayList<Bitmap> data = new ArrayList<Bitmap>();
+
+    public SelectCoverAdapter(Context context) {
+        mContext = context;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        int height = mContext.getResources().getDimensionPixelOffset(R.dimen.item_thumb_height);
+        int width = mContext.getResources().getDimensionPixelOffset(R.dimen.item_thumb_width);
+        ImageView view = new ImageView(parent.getContext());
+        view.setLayoutParams(new ViewGroup.LayoutParams(width, height));
+        view.setScaleType(ImageView.ScaleType.FIT_XY);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        Glide.with(mContext).load(data.get(position)).into(holder.thumb);
+    }
+
+    @Override
+    public int getItemCount() {
+        return data.size();
+    }
+
+    public void add(int position, Bitmap b) {
+        data.add(b);
+        notifyItemInserted(position);
+    }
+
+    public void clearAllBitmap() {
+        data.clear();
+        notifyDataSetChanged();
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        @NonNull
+        private final ImageView thumb;
+
+        public ViewHolder(@NonNull View itemView) {
+            super(itemView);
+            thumb = (ImageView) itemView;
+        }
+    }
+
+}

+ 42 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/bean/LoadThumbResultBean.java

@@ -0,0 +1,42 @@
+package com.cooleshow.musicmerge.bean;
+
+import android.graphics.Bitmap;
+
+/**
+ * Author by pq, Date on 2023/11/10.
+ */
+public class LoadThumbResultBean {
+    private int finalIndex;
+    private int time;
+    private Bitmap bitmap;
+
+    public LoadThumbResultBean(int finalIndex, int time, Bitmap bitmap) {
+        this.finalIndex = finalIndex;
+        this.time = time;
+        this.bitmap = bitmap;
+    }
+
+    public int getFinalIndex() {
+        return finalIndex;
+    }
+
+    public void setFinalIndex(int finalIndex) {
+        this.finalIndex = finalIndex;
+    }
+
+    public int getTime() {
+        return time;
+    }
+
+    public void setTime(int time) {
+        this.time = time;
+    }
+
+    public Bitmap getBitmap() {
+        return bitmap;
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        this.bitmap = bitmap;
+    }
+}

+ 8 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/bean/VideoInfo.java

@@ -0,0 +1,8 @@
+package com.cooleshow.musicmerge.bean;
+
+public class VideoInfo {
+
+    public long duration;
+    public String videoPath;
+    public String coverPath;
+}

+ 221 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/manager/CoverSelectorManager.java

@@ -0,0 +1,221 @@
+package com.cooleshow.musicmerge.manager;
+
+import android.graphics.Bitmap;
+import android.media.MediaMetadataRetriever;
+
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.musicmerge.bean.LoadThumbResultBean;
+import com.cooleshow.musicmerge.bean.VideoInfo;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+import static android.media.MediaMetadataRetriever.OPTION_CLOSEST;
+
+public class CoverSelectorManager {
+
+    //    private CoverSelectorManager sCoverSelectorManager;
+    private MediaMetadataRetriever mmr = null;
+    private ICoverSelector.ThumbnailListener mThumbnailListener;
+    private ICoverSelector.OnFrameAtTimeListener mOnFrameAtTimeListener;
+    private VideoInfo mVideoInfo;
+    private boolean isRelease = false;
+
+    public CoverSelectorManager() {
+
+    }
+
+    private void initMediaMetadataRetriever() {
+        mmr = new MediaMetadataRetriever();
+    }
+
+    public void setVideoInfo(VideoInfo videoInfo) {
+        initMediaMetadataRetriever();
+        mVideoInfo = videoInfo;
+        LOG.i("mmr setDataSource");
+        mmr.setDataSource(mVideoInfo.videoPath);
+    }
+
+//    public void loadThumbnail(final VideoInfo videoInfo) {
+//        //最大取10张图片
+////        double durationS = (videoInfo.duration / 1000d);
+////        final int interval = (int) (videoInfo.duration / durationS);
+//        int interval = (int) (videoInfo.duration / 10);
+//        int coverNumber = (int) (videoInfo.duration / interval);
+//        for (int index = 0; index < coverNumber; index++) {
+//            final int finalIndex = index;
+//            final int time = interval * finalIndex;
+//            LOG.i("index:" + index + " time:" + time + " interval:" + interval);
+//            mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+//            mScheduledExecutorService.execute(new Runnable() {
+//                @Override
+//                public void run() {
+//                    final Bitmap bitmap = mmr.getFrameAtTime(interval * finalIndex * 1000, OPTION_CLOSEST);
+//                    ThreadUtils.runOnUiThread(new Runnable() {
+//                        @Override
+//                        public void run() {
+//                            if (mThumbnailListener != null) {
+//                                int size = (int) (videoInfo.duration / interval);
+//                                mThumbnailListener.onThumbnail(finalIndex, time, bitmap);
+//                                if (finalIndex == size - 1) {
+//                                    mThumbnailListener.loadComplete();
+//                                }
+//                            }
+//                        }
+//                    });
+//                }
+//            });
+//        }
+//    }
+
+    public void loadThumbnail2(final VideoInfo videoInfo) {
+        if (mThumbnailDisposable != null) {
+            mThumbnailDisposable.dispose();
+        }
+        int interval = (int) (videoInfo.duration / 10);
+        int coverNumber = (int) (videoInfo.duration / interval);
+        Observable.create(new ObservableOnSubscribe<LoadThumbResultBean>() {
+                    @Override
+                    public void subscribe(@NonNull ObservableEmitter<LoadThumbResultBean> emitter) {
+                        for (int index = 0; index < coverNumber; index++) {
+                            if (isRelease) {
+                                return;
+                            }
+                            final int finalIndex = index;
+                            final int time = interval * finalIndex;
+                            LOG.i("index:" + index + " time:" + time + " interval:" + interval);
+                            if (mmr != null) {
+                                final Bitmap bitmap = mmr.getFrameAtTime(interval * finalIndex * 1000, OPTION_CLOSEST);
+                                emitter.onNext(new LoadThumbResultBean(finalIndex, time, bitmap));
+                            }
+                        }
+                    }
+                }).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<LoadThumbResultBean>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+                        mThumbnailDisposable = d;
+                    }
+
+                    @Override
+                    public void onNext(@NonNull LoadThumbResultBean result) {
+                        if (mThumbnailListener != null) {
+                            int size = (int) (videoInfo.duration / interval);
+                            mThumbnailListener.onThumbnail(result.getFinalIndex(), result.getTime(), result.getBitmap());
+                            if (result.getFinalIndex() == size - 1) {
+                                mThumbnailListener.loadComplete();
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        e.printStackTrace();
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+    }
+
+    Disposable mDisposable;
+    Disposable mThumbnailDisposable;
+
+    public void getFrameAtTime2(final long startTime) {
+        if (mDisposable != null) {
+            mDisposable.dispose();
+        }
+        Observable.create(new ObservableOnSubscribe<Bitmap>() {
+                    @Override
+                    public void subscribe(@NonNull ObservableEmitter<Bitmap> emitter) throws Throwable {
+                        if (isRelease) {
+                            return;
+                        }
+                        if (mmr != null) {
+                            final Bitmap bitmap = mmr.getFrameAtTime(startTime * 1000, OPTION_CLOSEST);
+                            emitter.onNext(bitmap);
+                        } else {
+                            emitter.onNext(null);
+                        }
+                    }
+                }).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<Bitmap>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+                        mDisposable = d;
+                    }
+
+                    @Override
+                    public void onNext(@NonNull Bitmap bitmap) {
+                        if (mOnFrameAtTimeListener != null) {
+                            mOnFrameAtTimeListener.onFrameAtTime(bitmap);
+                        }
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        e.printStackTrace();
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+    }
+
+//    public void getFrameAtTime(final long startTime) {
+//        mScheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
+//        mScheduledExecutorService1.submit(new Runnable() {
+//            @Override
+//            public void run() {
+//                final Bitmap bitmap = mmr.getFrameAtTime(startTime * 1000, OPTION_CLOSEST);
+//                ThreadUtils.runOnUiThread(new Runnable() {
+//                    @Override
+//                    public void run() {
+//                        if (mOnFrameAtTimeListener != null) {
+//                            mOnFrameAtTimeListener.onFrameAtTime(bitmap);
+//                        }
+//                    }
+//                });
+//            }
+//        });
+//    }
+
+    public void setThumbnailListener(ICoverSelector.ThumbnailListener thumbnailListener) {
+        this.mThumbnailListener = thumbnailListener;
+    }
+
+    public void setOnFrameAtTimeListener(ICoverSelector.OnFrameAtTimeListener onFrameAtTimeListener) {
+        this.mOnFrameAtTimeListener = onFrameAtTimeListener;
+    }
+
+    public void release() {
+        isRelease = true;
+        if (mmr != null) {
+            //这个release调用之后会释放所有内存,快速进退页面会导致MediaMetadataRetriever虽然不是同一个对象,但是都被release了内存,导致获取失败
+            mmr.release();
+        }
+        try {
+            if (mDisposable != null) {
+                mDisposable.dispose();
+            }
+
+            if (mThumbnailDisposable != null) {
+                mThumbnailDisposable.dispose();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 22 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/manager/ICoverSelector.java

@@ -0,0 +1,22 @@
+package com.cooleshow.musicmerge.manager;
+
+import android.graphics.Bitmap;
+
+public interface ICoverSelector {
+
+
+    interface ThumbnailListener {
+        void onThumbnail(int pos, long timeMs, final Bitmap bitmap);
+
+        void loadComplete();
+    }
+
+    interface OnFrameAtTimeListener{
+        void onFrameAtTime(Bitmap bitmap);
+    }
+
+    interface OnSliderMoveListener {
+        void onSliderMove(long startTime, int type, int moveX);
+    }
+
+}

+ 237 - 2
musicMerge/src/main/java/com/cooleshow/musicmerge/ui/SelectVideoFrameActivity.java

@@ -1,25 +1,211 @@
 package com.cooleshow.musicmerge.ui;
 
-import com.cooleshow.base.ui.activity.BaseActivity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.cooleshow.base.constanst.Constants;
+import com.cooleshow.base.router.RouterPath;
 import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.MyFileUtils;
+import com.cooleshow.base.utils.SizeUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.UiUtils;
+import com.cooleshow.base.utils.Utils;
+import com.cooleshow.musicmerge.R;
+import com.cooleshow.musicmerge.callback.ResultCallback;
+import com.cooleshow.musicmerge.contract.VideoFileHandleContract;
 import com.cooleshow.musicmerge.databinding.AcSelectVideoFrameLayoutBinding;
+import com.cooleshow.musicmerge.helper.MixHelper;
 import com.cooleshow.musicmerge.presenter.VideoFileHandlePresenter;
+import com.cooleshow.musicmerge.widget.cover.ShortVideoSelectCover;
+
+import java.io.File;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
 
 /**
  * Author by pq, Date on 2023/11/8.
  */
-public class SelectVideoFrameActivity extends BaseMVPActivity<AcSelectVideoFrameLayoutBinding, VideoFileHandlePresenter> {
+@Route(path = RouterPath.MusicTuner.MUSIC_SELECT_VIDEO_FRAME)
+public class SelectVideoFrameActivity extends BaseMVPActivity<AcSelectVideoFrameLayoutBinding, VideoFileHandlePresenter> implements VideoFileHandleContract, View.OnClickListener {
+    private final static String OUT_PATH = MyFileUtils.getCacheDir(Utils.getApp(), "videoFrame");
+    private String mVideoPath;
+
     @Override
     protected void initView() {
+        initMidTitleToolBar(viewBinding.toolbarInclude.toolbar, "编辑封面");
+        viewBinding.toolbarInclude.toolbar.setBackground(getResources().getDrawable(R.drawable.shape_video_cover_top_bg));
+        viewBinding.toolbarInclude.tvRightText.setWidth(SizeUtils.dp2px(64));
+        viewBinding.toolbarInclude.tvRightText.setHeight(SizeUtils.dp2px(32));
+        viewBinding.toolbarInclude.tvRightText.setVisibility(View.VISIBLE);
+        viewBinding.toolbarInclude.tvRightText.setText("保存");
+        viewBinding.toolbarInclude.tvRightText.setBackground(getResources().getDrawable(com.cooleshow.base.R.drawable.shape_16dp_white));
+        viewBinding.toolbarInclude.tvRightText.setGravity(Gravity.CENTER);
+        viewBinding.toolbarInclude.tvRightText.setTextColor(getResources().getColor(R.color.main_style_color));
+        viewBinding.toolbarInclude.tvRightText.setOnClickListener(this);
+
 
     }
 
     @Override
     public void initData() {
         super.initData();
+        mVideoPath = getIntent().getStringExtra("videoFilePath");
+        boolean isLocalFile = getIntent().getBooleanExtra("isLocalFile", true);
+        if (TextUtils.isEmpty(mVideoPath)) {
+            finish();
+            return;
+        }
+        if (isLocalFile) {
+            toPreView();
+        } else {
+            if (checkParamsIsError()) {
+                return;
+            }
+            boolean b = checkUrlToDownload(mVideoPath);
+            if (b) {
+                toPreView();
+            }
+        }
+        viewBinding.videoCutterLayout.setOnGetSampleImageListener(new ShortVideoSelectCover.OnGetSampleImageListener() {
+            @Override
+            public void start() {
+
+            }
+
+            @Override
+            public void complete(Bitmap img) {
+                if (img != null) {
+                    saveFile(img);
+                }
+            }
+        });
     }
 
+    private boolean checkParamsIsError() {
+        boolean isVideo = MyFileUtils.isVideoFromUrl(mVideoPath);
+        if (!isVideo) {
+            ToastUtil.getInstance().showShort("文件解析失败");
+            finish();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean checkUrlToDownload(String recordUrl) {
+        String fileEndSuffix = MyFileUtils.getWAVOrMp4FileSuffix(recordUrl);
+        String filePath = MixHelper.getInstance().getDownloadSavePathForMp4(recordUrl);
+        LOG.i("pq", "checkUrlToDownload:" + filePath);
+        File file = new File(filePath);
+        if (file.exists()) {
+            mVideoPath = filePath;
+            return true;
+        }
+        setLoadingCancelable(false);
+        showLoading("视频下载中");
+        MixHelper.getInstance().download(recordUrl, fileEndSuffix, new ResultCallback<String>() {
+            @Override
+            public void onSuccess(String s) {
+                if(!checkActivityExist()){
+                    return;
+                }
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        hideLoading();
+                        mVideoPath = s;
+                        toPreView();
+                    }
+                });
+            }
+
+            @Override
+            public void onProgress(int progressPercent) {
+                if(!checkActivityExist()){
+                    return;
+                }
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        updateLoadingText("视频下载中" + progressPercent + "%");
+                    }
+                });
+            }
+
+            @Override
+            public void onFail(int errorCode, String errorStr) {
+                if(!checkActivityExist()){
+                    return;
+                }
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        ToastUtil.getInstance().showShort("视频下载失败,请重试");
+                        hideLoading();
+                    }
+                });
+            }
+        });
+        return false;
+    }
+
+    private void toPreView() {
+        viewBinding.videoCutterLayout.prePlayVideo(mVideoPath);
+    }
+
+    private void saveFile(Bitmap img) {
+        Observable.create(new ObservableOnSubscribe<String>() {
+                    @Override
+                    public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
+                        File file = FileUtils.saveImgToLocalFile(img);
+                        if (emitter != null) {
+                            emitter.onNext(file.getAbsolutePath());
+                        }
+                    }
+                }).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<String>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(@NonNull String s) {
+                        reBackData(s);
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        ToastUtil.getInstance().showShort("获取封面失败,请重试");
+                    }
+
+                    @Override
+                    public void onComplete() {
+                        hideLoading();
+                    }
+                });
+    }
 
+    private void reBackData(String s) {
+        Intent intent = new Intent();
+        intent.putExtra(Constants.COMMON_EXTRA_KEY, s);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
 
     @Override
     protected AcSelectVideoFrameLayoutBinding getLayoutView() {
@@ -30,4 +216,53 @@ public class SelectVideoFrameActivity extends BaseMVPActivity<AcSelectVideoFrame
     protected VideoFileHandlePresenter createPresenter() {
         return new VideoFileHandlePresenter();
     }
+
+    @Override
+    public void finish() {
+        super.finish();
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onBackPressed();
+    }
+
+    @Override
+    public void onDestroy() {
+        release();
+        super.onDestroy();
+    }
+
+    boolean isNeedRelease = true;
+
+    private void release() {
+        if (!isNeedRelease) {
+            return;
+        }
+        try {
+            if (viewBinding != null) {
+                viewBinding.videoCutterLayout.release();
+            }
+            isNeedRelease = false;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == com.cooleshow.base.R.id.tv_right_text) {
+            if (UiUtils.isFastClick()) {
+                return;
+            }
+            toGetCover();
+            return;
+        }
+    }
+
+    private void toGetCover() {
+        showLoading("获取封面中");
+        viewBinding.videoCutterLayout.getCover();
+    }
 }

+ 228 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/CoverSliderView.java

@@ -0,0 +1,228 @@
+package com.cooleshow.musicmerge.widget.cover;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+import com.cooleshow.musicmerge.R;
+import com.cooleshow.musicmerge.adapter.SelectCoverAdapter;
+import com.cooleshow.musicmerge.bean.VideoInfo;
+import com.cooleshow.musicmerge.manager.ICoverSelector;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class CoverSliderView extends RelativeLayout implements RangeSlider.OnRangeChangeListener {
+
+    private Context mContext;
+
+    private RecyclerView mRecyclerView;
+    private RangeSlider mRangeSlider;
+    private float mCurrentScroll;
+    private int mSingleWidth;
+    private int mAllWidth;
+
+    private long mVideoDuration;
+
+    private long mViewMaxDuration;
+
+    private long mStartTime = 0;
+
+    private int mSelectTime;
+
+    private long mVideoStartPos;
+
+    private SelectCoverAdapter mAdapter;
+
+    private ICoverSelector.OnSliderMoveListener mSliderMoveListener;
+    private int position;
+    private float mViewRate;
+
+    public CoverSliderView(Context context) {
+        super(context);
+
+        init(context);
+    }
+
+    public CoverSliderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        init(context);
+    }
+
+    public CoverSliderView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        init(context);
+    }
+
+    private void init(Context context) {
+        mContext = context;
+        inflate(getContext(), R.layout.item_sv_cover_view, this);
+
+        mRangeSlider = (RangeSlider) findViewById(R.id.range_slider);
+        mRangeSlider.setRangeChangeListener(this);
+
+        LinearLayoutManager manager = new LinearLayoutManager(mContext);
+        manager.setOrientation(LinearLayoutManager.HORIZONTAL);
+        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+        mRecyclerView.setLayoutManager(manager);
+        mRecyclerView.addOnScrollListener(mOnScrollListener);
+
+        mAdapter = new SelectCoverAdapter(mContext);
+        mRecyclerView.setAdapter(mAdapter);
+
+        mSingleWidth = mContext.getResources().getDimensionPixelOffset(R.dimen.item_thumb_width);
+    }
+
+    /**
+     * 设置缩略图个数
+     *
+     * @param count
+     */
+    public void setCount(int count) {
+        ViewGroup.LayoutParams layoutParams = getLayoutParams();
+        int width = count * mSingleWidth;
+        mAllWidth = width;
+        Resources resources = getResources();
+        DisplayMetrics dm = resources.getDisplayMetrics();
+        int screenWidth = dm.widthPixels;
+        if (width > screenWidth) {
+            width = screenWidth;
+        }
+        layoutParams.width = width;
+        setLayoutParams(layoutParams);
+    }
+
+    public void setSliderMoveListener(ICoverSelector.OnSliderMoveListener listener) {
+        mSliderMoveListener = listener;
+    }
+
+    public void setMediaFileInfo(VideoInfo videoInfo) {
+        if (videoInfo == null) {
+            return;
+        }
+        mViewMaxDuration = videoInfo.duration;
+        mSelectTime = 0;
+        mVideoStartPos = 0;
+    }
+
+    public void addBitmap(int index, Bitmap bitmap) {
+        mAdapter.add(index, bitmap);
+    }
+
+    public void clearAllBitmap() {
+        mAdapter.clearAllBitmap();
+    }
+
+    @Override
+    public void onKeyDown(int type) {
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAdapter != null) {
+            mAdapter.clearAllBitmap();
+        }
+    }
+
+
+    @Override
+    public void onKeyMove(int type, int leftPinIndex, int moveX) {
+        mSelectTime = (int) (mViewMaxDuration * leftPinIndex * mViewRate / 100); //ms
+        onTimeChangedMove(moveX);
+    }
+
+    private void onTimeChangedMove(int moveX) {
+        mVideoStartPos = mStartTime + mSelectTime;
+        if (mSliderMoveListener != null) {
+            mSliderMoveListener.onSliderMove((int) mVideoStartPos, getRangeSlider().getLeftIndex(), moveX);
+        }
+    }
+
+    private void onTimeChanged() {
+        mVideoStartPos = mStartTime + mSelectTime;
+    }
+
+    @NonNull
+    private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
+        @Override
+        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+            super.onScrollStateChanged(recyclerView, newState);
+            switch (newState) {
+                case RecyclerView.SCROLL_STATE_IDLE:
+                    onTimeChanged();
+                    break;
+                case RecyclerView.SCROLL_STATE_DRAGGING:
+                    break;
+                case RecyclerView.SCROLL_STATE_SETTLING:
+
+                    break;
+            }
+        }
+
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            super.onScrolled(recyclerView, dx, dy);
+            mCurrentScroll = mCurrentScroll + dx;
+            float rate = mCurrentScroll / mAllWidth;
+            if (mCurrentScroll + mRecyclerView.getWidth() > mAllWidth) {
+                mStartTime = mVideoDuration - mViewMaxDuration;
+            } else {
+                mStartTime = (int) (rate * mVideoDuration);
+            }
+        }
+    };
+
+    public RangeSlider getRangeSlider() {
+        return mRangeSlider;
+    }
+
+    public void setVideoInfo(VideoInfo videoInfo) {
+        mVideoDuration = videoInfo.duration;
+        int durationS = (int) (mVideoDuration / 1000);
+        setMediaFileInfo(videoInfo);
+        setCount(10);
+    }
+
+    public void addThumbnail(int index, Bitmap bitmap) {
+        this.addBitmap(index, bitmap);
+    }
+
+    public void clearThumbnail() {
+        this.clearAllBitmap();
+    }
+
+    public void setPosition(int position) {
+        this.position = position;
+        mRangeSlider.setRangeIndex(position, 100);
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public void loadComplete(boolean mComplete) {
+        mViewRate = (float) mRecyclerView.getWidth() / mAllWidth;
+        mRangeSlider.loadComplete(mComplete);
+    }
+
+    public void startScrollBy(int i) {
+        mRecyclerView.smoothScrollBy(i, 0);
+    }
+
+    public void startScrollTo(int i) {
+        mRecyclerView.scrollBy(i, 0);
+    }
+
+    public float getmCurrentScroll() {
+        return mCurrentScroll;
+    }
+
+}

+ 363 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/RangeSlider.java

@@ -0,0 +1,363 @@
+package com.cooleshow.musicmerge.widget.cover;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.cooleshow.base.utils.SizeUtils;
+import com.cooleshow.musicmerge.R;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+
+public class RangeSlider extends ViewGroup {
+    private static final int DEFAULT_THUMB_WIDTH = 7;
+    private static final int DEFAULT_TICK_START = 0;
+    private static final int DEFAULT_TICK_END = 5;
+    private static final int DEFAULT_TICK_INTERVAL = 1;
+    private static int DEFAULT_MASK_BACKGROUND = 0xA0000000;
+    private static int DEFAULT_LINE_COLOR = 0xFFFF584C;
+    public static final int TYPE_LEFT = 1;
+
+    @NonNull
+    private final ThumbView mThumb;
+    private final Paint mBgPaint;
+    private final VideoPlayer videoPlayer;
+    private final TextureView textureView;
+    @Nullable
+    private Drawable mIcon;
+
+    private int mTouchSlop;
+    private int mOriginalX, mLastX;
+
+    private int mThumbWidth;
+
+    private int mTickStart = DEFAULT_TICK_START;
+    private int mTickEnd = DEFAULT_TICK_END;
+    private int mTickInterval = DEFAULT_TICK_INTERVAL;
+    private int mTickCount = (mTickEnd - mTickStart) / mTickInterval;
+
+    private boolean mIsDragging;
+
+    private OnRangeChangeListener mRangeChangeListener;
+    private boolean mComplete;
+    private long mStartTime;
+
+    public RangeSlider(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public RangeSlider(@NonNull Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RangeSlider(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RangeSlider, 0, 0);
+        mThumbWidth = array.getDimensionPixelOffset(R.styleable.RangeSlider_thumbWidth, DEFAULT_THUMB_WIDTH);
+
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mBgPaint = new Paint();
+        mBgPaint.setColor(array.getColor(R.styleable.RangeSlider_maskColor, DEFAULT_MASK_BACKGROUND));
+
+        mIcon = array.getDrawable(R.styleable.RangeSlider_leftThumbDrawable);
+//        mThumb = new ThumbView(context, mThumbWidth, mIcon == null ? new ColorDrawable(DEFAULT_LINE_COLOR) : mIcon);
+        mThumb = new ThumbView(context, mThumbWidth, new ColorDrawable(Color.TRANSPARENT));
+        setTickCount(array.getInteger(R.styleable.RangeSlider_tickCount, DEFAULT_TICK_END));
+        setRangeIndex(array.getInteger(R.styleable.RangeSlider_leftThumbIndex, DEFAULT_TICK_START),
+                array.getInteger(R.styleable.RangeSlider_rightThumbIndex, mTickCount));
+        array.recycle();
+        videoPlayer = new VideoPlayer();
+        View borderView =new View(getContext());
+        borderView.setBackground(mIcon);
+        textureView = new TextureView(getContext());
+        textureView.setVisibility(VISIBLE);
+        videoPlayer.setTextureView(textureView);
+        int i = SizeUtils.dp2px(4);
+        FrameLayout.LayoutParams layoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.MATCH_PARENT);
+        layoutParams.setMargins(i,i,i,i);
+        mThumb.addView(textureView,layoutParams);
+        mThumb.addView(borderView);
+        addView(mThumb);
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mThumb.measure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int lThumbWidth = mThumb.getMeasuredWidth();
+        final int lThumbHeight = mThumb.getMeasuredHeight();
+        mThumb.layout(0, 0, lThumbWidth, lThumbHeight);
+    }
+
+    @Override
+    protected void onDraw(@NonNull Canvas canvas) {
+        final int width = getMeasuredWidth();
+        final int height = getMeasuredHeight();
+        final float lThumbOffset = mThumb.getX();
+        canvas.drawRect(0, 0, width, height, mBgPaint);
+//        canvas.drawRect(0, 0, lThumbOffset, height, mBgPaint);
+//        canvas.drawRect(lThumbOffset+mThumbWidth, 0, width, height, mBgPaint);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        moveThumbByIndex(mThumb, mThumb.getRangeIndex());
+    }
+
+    @Override
+    public boolean onTouchEvent(@NonNull MotionEvent event) {
+        if (!isEnabled()) {
+            return false;
+        }
+        if (!mComplete){
+            return false;
+        }
+
+        boolean handle = false;
+
+        switch (event.getAction()) {
+
+            case MotionEvent.ACTION_DOWN:
+                int x = (int) event.getX();
+                int y = (int) event.getY();
+                textureView.setVisibility(VISIBLE);
+                mIsDragging = false;
+
+                if (!mThumb.isPressed()) {
+                    mThumb.setPressed(true);
+                    handle = true;
+                    if (mRangeChangeListener != null) {
+                        mRangeChangeListener.onKeyDown(TYPE_LEFT);
+                    }
+                    int moveX = (int) (x - (mThumb.getX()+ mThumb.getWidth()/2));
+                    getParent().requestDisallowInterceptTouchEvent(true);
+                    moveLeftThumbByPixel(moveX);
+                    invalidate();
+                    if (mRangeChangeListener != null) {
+                        mRangeChangeListener.onKeyMove(TYPE_LEFT, mThumb.getRangeIndex(),moveX);
+                    }
+                }
+                mLastX = mOriginalX = x;
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mIsDragging = false;
+                mOriginalX = mLastX = 0;
+                textureView.setVisibility(VISIBLE);
+                getParent().requestDisallowInterceptTouchEvent(false);
+                if (mThumb.isPressed()) {
+                    releaseLeftThumb();
+                    invalidate();
+                    handle = true;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                x = (int) event.getX();
+
+                if (!mIsDragging && Math.abs(x - mOriginalX) > mTouchSlop) {
+                    mIsDragging = true;
+                }
+                if (mIsDragging) {
+                    int moveX = x - mLastX;
+                    if (mThumb.isPressed()) {
+                        getParent().requestDisallowInterceptTouchEvent(true);
+                        moveLeftThumbByPixel(moveX);
+                        handle = true;
+                        invalidate();
+                        if (mRangeChangeListener != null) {
+                            mRangeChangeListener.onKeyMove(TYPE_LEFT, mThumb.getRangeIndex(), moveX);
+                        }
+                    }
+                }
+
+                mLastX = x;
+                break;
+        }
+
+        return handle;
+    }
+
+    private boolean isValidTickCount(int tickCount) {
+        return (tickCount > 1);
+    }
+
+    private boolean indexOutOfRange(int leftThumbIndex, int rightThumbIndex) {
+        return (leftThumbIndex < 0 || leftThumbIndex > mTickCount
+                || rightThumbIndex < 0
+                || rightThumbIndex > mTickCount);
+    }
+
+    private float getRangeLength() {
+        int width = getMeasuredWidth();
+        if (width < mThumbWidth) {
+            return 0;
+        }
+        return width - mThumbWidth;
+    }
+
+    private float getIntervalLength() {
+        return getRangeLength() / mTickCount;
+    }
+
+    public int getNearestIndex(float x) {
+        return Math.round(x / getIntervalLength());
+    }
+
+    public int getLeftIndex() {
+        return mThumb.getRangeIndex();
+    }
+
+
+    private void notifyRangeChange(int type) {
+        if (mRangeChangeListener != null) {
+//            mRangeChangeListener.onRangeChange(this, type, mThumb.getRangeIndex(), mRightThumb.getRangeIndex());
+        }
+    }
+
+    public void setRangeChangeListener(OnRangeChangeListener rangeChangeListener) {
+        mRangeChangeListener = rangeChangeListener;
+    }
+
+    /**
+     * Sets the tick count in the RangeSlider.
+     *
+     * @param count Integer specifying the number of ticks.
+     */
+    public void setTickCount(int count) {
+        int tickCount = (count - mTickStart) / mTickInterval;
+        if (isValidTickCount(tickCount)) {
+            mTickEnd = count;
+            mTickCount = tickCount;
+        } else {
+            throw new IllegalArgumentException("tickCount less than 2; invalid tickCount.");
+        }
+    }
+
+    /**
+     * The location of the thumbs according by the supplied index.
+     * Numbered from 0 to mTickCount - 1 from the left.
+     *
+     * @param leftIndex  Integer specifying the index of the left thumb
+     * @param rightIndex Integer specifying the index of the right thumb
+     */
+    public void setRangeIndex(int leftIndex, int rightIndex) {
+        if (indexOutOfRange(leftIndex, rightIndex)) {
+            throw new IllegalArgumentException(
+                    "mThumb index left " + leftIndex + ", or right " + rightIndex
+                            + " is out of bounds. Check that it is greater than the minimum ("
+                            + mTickStart + ") and less than the maximum value ("
+                            + mTickEnd + ")");
+        } else {
+            if (mThumb.getRangeIndex() != leftIndex) {
+                mThumb.setTickIndex(leftIndex);
+            }
+        }
+    }
+
+    private boolean moveThumbByIndex(@NonNull ThumbView view, int index) {
+        view.setX(index * getIntervalLength());
+        if (view.getRangeIndex() != index) {
+            view.setTickIndex(index);
+            return true;
+        }
+        return false;
+    }
+
+    private void moveLeftThumbByPixel(int pixel) {
+        float x = mThumb.getX() + pixel;
+        float interval = getIntervalLength();
+        float start = mTickStart / mTickInterval * interval;
+        float end = mTickEnd / mTickInterval * interval;
+
+        if (x > start && x < end) {
+            mThumb.setX(x);
+            int index = getNearestIndex(x);
+            if (mThumb.getRangeIndex() != index) {
+                mThumb.setTickIndex(index);
+                notifyRangeChange(TYPE_LEFT);
+            }
+        }
+    }
+
+    private void releaseLeftThumb() {
+        mThumb.setPressed(false);
+    }
+
+    public void setStartTime(long startTime) {
+        mStartTime = startTime;
+    }
+
+    public void playVideo(final String videoUrl) {
+        videoPlayer.reset();
+        videoPlayer.setOnStateChangeListener(new VideoPlayer.OnStateChangeListener() {
+            @Override
+            public void onPrepared() {
+                videoPlayer.seekTo(mStartTime);
+            }
+
+            @Override
+            public void onReset() {
+            }
+
+            @Override
+            public void onRenderingStart() {
+            }
+
+            @Override
+            public void onProgressUpdate(float per) {
+            }
+
+            @Override
+            public void onPause() {
+            }
+
+            @Override
+            public void onStop() {
+            }
+
+            @Override
+            public void onComplete() {
+            }
+        });
+        videoPlayer.setDataSource(videoUrl);
+        videoPlayer.prepare();
+    }
+
+
+    public void loadComplete(boolean complete) {
+        mComplete = complete;
+    }
+
+    public void seekTo(long startTime) {
+        videoPlayer.seekTo(startTime);
+    }
+
+    public interface OnRangeChangeListener {
+        void onKeyDown(int type);
+
+        void onKeyMove(int typeLeft, int rangeIndex, int moveX);
+    }
+
+}

+ 181 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/ShortVideoSelectCover.java

@@ -0,0 +1,181 @@
+package com.cooleshow.musicmerge.widget.cover;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.TextureView;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.SizeUtils;
+import com.cooleshow.musicmerge.R;
+import com.cooleshow.musicmerge.bean.VideoInfo;
+import com.cooleshow.musicmerge.manager.CoverSelectorManager;
+import com.cooleshow.musicmerge.manager.ICoverSelector;
+
+
+public class ShortVideoSelectCover extends RelativeLayout {
+    private boolean mComplete = false;
+    private CoverSliderView mVideoCutLayout;
+    private ImageView mImgCover;
+    private String mVideoPath = "";
+    private long mStartTime;
+    private VideoPlayer videoPlayer;
+    private TextureView textureView;
+    private FrameLayout flVideo;
+    private VideoInfo mVideoInfo;
+    private CoverSelectorManager mCoverSelectorManager;
+
+    public ShortVideoSelectCover(Context context) {
+        super(context);
+        initViews();
+    }
+
+    public ShortVideoSelectCover(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initViews();
+    }
+
+    public ShortVideoSelectCover(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initViews();
+    }
+
+    private void initViews() {
+        inflate(getContext(), R.layout.video_selector_layout, this);
+        mCoverSelectorManager = new CoverSelectorManager();
+        videoPlayer = new VideoPlayer();
+        textureView = new TextureView(getContext());
+        videoPlayer.setTextureView(textureView);
+        flVideo = findViewById(R.id.flVideo);
+        flVideo.addView(textureView);
+        mVideoCutLayout = findViewById(R.id.video_cut_layout);
+        mImgCover = findViewById(R.id.img_cover);
+    }
+
+    public void prePlayVideo(String videoPath) {
+        mVideoPath = videoPath;
+        mVideoInfo = new VideoInfo();
+        mVideoInfo.videoPath = mVideoPath;
+        playVideo(mVideoPath);
+        mVideoCutLayout.getRangeSlider().playVideo(mVideoPath);
+        Log.i("pq", "mVideoPath:" + mVideoPath);
+    }
+
+    private void loadVideoInfo(VideoInfo videoInfo) {
+        mVideoCutLayout.setVideoInfo(videoInfo);
+        mVideoCutLayout.clearThumbnail();
+        mVideoCutLayout.setSliderMoveListener(new ICoverSelector.OnSliderMoveListener() {
+
+            @Override
+            public void onSliderMove(long startTime, int type, int moveX) {
+                LOG.i("onSliderMove type:" + type + " moveX:" + moveX);
+                mStartTime = startTime;
+                videoPlayer.seekTo(startTime);
+                mVideoCutLayout.getRangeSlider().seekTo(startTime);
+                mImgCover.setVisibility(GONE);
+                if (type > 80 && moveX > 0) {
+                    mVideoCutLayout.startScrollBy(SizeUtils.dp2px(150));
+                } else if (type < 20 && moveX < 0) {
+                    mVideoCutLayout.startScrollBy(-SizeUtils.dp2px(150));
+                }
+            }
+        });
+        mCoverSelectorManager.setVideoInfo(videoInfo);
+        mCoverSelectorManager.setThumbnailListener(new ICoverSelector.ThumbnailListener() {
+            @Override
+            public void onThumbnail(int pos, long timeMs, Bitmap bitmap) {
+                mComplete = false;
+                mVideoCutLayout.addThumbnail(pos, bitmap);
+            }
+
+            @Override
+            public void loadComplete() {
+                mComplete = true;
+                mVideoCutLayout.loadComplete(true);
+            }
+        });
+        mCoverSelectorManager.loadThumbnail2(videoInfo);
+    }
+
+    public void getCover() {
+        if (onGetSampleImageListener != null) {
+            onGetSampleImageListener.start();
+        }
+        mCoverSelectorManager.setOnFrameAtTimeListener(new ICoverSelector.OnFrameAtTimeListener() {
+            @Override
+            public void onFrameAtTime(Bitmap bitmap) {
+                if (onGetSampleImageListener != null) {
+                    onGetSampleImageListener.complete(bitmap);
+                }
+            }
+        });
+        mCoverSelectorManager.getFrameAtTime2(mStartTime);
+    }
+
+    private void playVideo(final String videoUrl) {
+        videoPlayer.reset();
+        videoPlayer.setOnStateChangeListener(new VideoPlayer.OnStateChangeListener() {
+            @Override
+            public void onPrepared() {
+                mComplete = false;
+                videoPlayer.seekTo(0);
+                int duration = videoPlayer.getDuration();
+                LOG.i("duration :" + duration);
+                mVideoInfo.duration = duration;
+                loadVideoInfo(mVideoInfo);
+            }
+
+            @Override
+            public void onReset() {
+            }
+
+            @Override
+            public void onRenderingStart() {
+            }
+
+            @Override
+            public void onProgressUpdate(float per) {
+            }
+
+            @Override
+            public void onPause() {
+            }
+
+            @Override
+            public void onStop() {
+            }
+
+            @Override
+            public void onComplete() {
+            }
+        });
+        videoPlayer.setDataSource(videoUrl);
+        videoPlayer.prepare();
+    }
+
+    private OnGetSampleImageListener onGetSampleImageListener;
+
+    public void setOnGetSampleImageListener(OnGetSampleImageListener onGetSampleImageListener) {
+        this.onGetSampleImageListener = onGetSampleImageListener;
+    }
+
+    public interface OnGetSampleImageListener {
+        void start();
+
+        void complete(Bitmap img);
+    }
+
+    public void release() {
+        if (mCoverSelectorManager != null) {
+            mCoverSelectorManager.release();
+        }
+        if (videoPlayer != null) {
+            videoPlayer.release();
+        }
+    }
+
+}

+ 55 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/ThumbView.java

@@ -0,0 +1,55 @@
+package com.cooleshow.musicmerge.widget.cover;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+
+public class ThumbView extends FrameLayout {
+
+    private static final int EXTEND_TOUCH_SLOP = 15;
+
+    private Drawable mThumbDrawable;
+
+    private boolean mPressed;
+
+    private int mThumbWidth;
+    private int mTickIndex;
+
+    public ThumbView(@NonNull Context context, int thumbWidth, Drawable drawable) {
+        super(context);
+        mThumbWidth = thumbWidth;
+        mThumbDrawable = drawable;
+        setBackgroundDrawable(mThumbDrawable);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(MeasureSpec.makeMeasureSpec(mThumbWidth, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY));
+        mThumbDrawable.setBounds(0, 0, mThumbWidth, getMeasuredHeight());
+    }
+
+    public void setThumbWidth(int thumbWidth) {
+        mThumbWidth = thumbWidth;
+    }
+
+    public int getRangeIndex() {
+        return mTickIndex;
+    }
+
+    public void setTickIndex(int tickIndex) {
+        mTickIndex = tickIndex;
+    }
+
+    @Override
+    public boolean isPressed() {
+        return mPressed;
+    }
+
+    @Override
+    public void setPressed(boolean pressed) {
+        mPressed = pressed;
+    }
+}

+ 283 - 0
musicMerge/src/main/java/com/cooleshow/musicmerge/widget/cover/VideoPlayer.java

@@ -0,0 +1,283 @@
+package com.cooleshow.musicmerge.widget.cover;
+
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+import android.view.TextureView;
+
+import java.io.IOException;
+
+/**
+ * Video Player
+ */
+public class VideoPlayer {
+    private MediaPlayer mediaPlayer;
+    private State state = State.IDLE;
+    private OnStateChangeListener onStateChangeListener;
+    private Handler handler;
+    private boolean prePause;
+    private TextureView textureView;
+    private boolean isPrepared;
+
+    public VideoPlayer() {
+        handler = new Handler();
+        mediaPlayer = new MediaPlayer();
+        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+            @Override
+            public void onPrepared(MediaPlayer mp) {
+                if (state == State.PREPAREING) {
+                    if (prePause) {
+                        state = State.PAUSE;
+                        prePause = false;
+                    }
+//                    else {
+//                        start();
+//                    }
+                    if (onStateChangeListener != null) {
+                        onStateChangeListener.onPrepared();
+                    }
+                }
+                isPrepared = true;
+            }
+        });
+        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                state = State.COMPLETE;
+                if (onStateChangeListener != null) {
+                    onStateChangeListener.onComplete();
+                }
+            }
+        });
+        mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(MediaPlayer mp, int what, int extra) {
+                Log.e("mediaplayer错误", "what:" + what + "  extra:" + extra);
+                return true;
+            }
+        });
+        mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
+            @Override
+            public boolean onInfo(MediaPlayer mp, int what, int extra) {
+                if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
+                    if (onStateChangeListener != null) {
+                        onStateChangeListener.onRenderingStart();
+                    }
+                    refreshProgress();
+                }
+                return false;
+            }
+        });
+    }
+
+    /**
+     * bind TextureView
+     */
+    public void setTextureView(TextureView textureView) {
+        this.textureView = textureView;
+        if (textureView.isAvailable()) {
+            mediaPlayer.setSurface(new Surface(textureView.getSurfaceTexture()));
+        } else {
+            textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
+                @Override
+                public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+                    mediaPlayer.setSurface(new Surface(surface));
+                }
+
+                @Override
+                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+                }
+
+                @Override
+                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+                    return false;
+                }
+
+                @Override
+                public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+                }
+            });
+        }
+    }
+
+    public void reset() {
+        mediaPlayer.reset();
+        state = State.IDLE;
+        isPrepared = false;
+        if (onStateChangeListener != null) {
+            onStateChangeListener.onReset();
+            onStateChangeListener = null;
+        }
+    }
+
+    /**
+     * play url
+     */
+    public void setDataSource(String url) {
+        try {
+            mediaPlayer.setDataSource(url);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void prepare() {
+        try {
+            isPrepared = false;
+            mediaPlayer.prepareAsync();
+            state = State.PREPAREING;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void start() {
+        if (state == State.PREPAREING || state == State.COMPLETE || state == State.PAUSE) {
+            mediaPlayer.start();
+            state = State.PLAYING;
+        }
+    }
+
+    public void seekTo(long startTime) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            mediaPlayer.seekTo(startTime, MediaPlayer.SEEK_CLOSEST);
+        } else {
+            mediaPlayer.seekTo((int) (startTime / 1000));
+        }
+    }
+
+    private void refreshProgress() {
+        if (state != State.PLAYING) {
+            return;
+        }
+        handler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (mediaPlayer == null) {
+                    return;
+                }
+                if (onStateChangeListener != null) {
+                    onStateChangeListener.onProgressUpdate(mediaPlayer.getCurrentPosition() * 1f / mediaPlayer.getDuration());
+                }
+                refreshProgress();
+            }
+        }, 100);
+    }
+
+    public void pause() {
+        if (state == State.PREPAREING && !prePause) {
+            prePause = true;
+            if (onStateChangeListener != null) {
+                onStateChangeListener.onPause();
+            }
+        } else if (state == State.PLAYING) {
+            mediaPlayer.pause();
+            state = State.PAUSE;
+            if (onStateChangeListener != null) {
+                onStateChangeListener.onPause();
+            }
+        }
+    }
+
+    public boolean startOrPause() {
+        if (state == State.PREPAREING || state == State.COMPLETE || state == State.PAUSE) {
+            mediaPlayer.start();
+            state = State.PLAYING;
+            return true;
+        } else {
+            pause();
+            return false;
+        }
+    }
+
+    public boolean isPrepared() {
+        return isPrepared;
+    }
+
+    public void stop() {
+        mediaPlayer.stop();
+        state = State.STOP;
+        if (onStateChangeListener != null) {
+            onStateChangeListener.onStop();
+        }
+    }
+
+    public void release() {
+        if (mediaPlayer != null) {
+            mediaPlayer.release();
+            mediaPlayer = null;
+        }
+        handler.removeCallbacksAndMessages(null);
+    }
+
+    private float getRateFloat(float video, int device) {
+        if (video < device) {
+            return device / video;
+        } else {
+            return video / device;
+        }
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public int getDuration() {
+        return mediaPlayer.getDuration();
+    }
+
+    public void setVolume(float leftVolume, float rightVolume) {
+        mediaPlayer.setVolume(leftVolume, rightVolume);
+    }
+
+    public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) {
+        this.onStateChangeListener = onStateChangeListener;
+    }
+
+
+    public interface OnStateChangeListener {
+        void onPrepared();
+
+        void onReset();
+
+        void onRenderingStart();
+
+        void onProgressUpdate(float per);
+
+        void onPause();
+
+        void onStop();
+
+        void onComplete();
+    }
+
+    public enum State {
+        /**
+         * 空闲
+         */
+        IDLE,
+        /**
+         * 准备中
+         */
+        PREPAREING,
+        /**
+         * 播放中
+         */
+        PLAYING,
+        /**
+         * 暂停中
+         */
+        PAUSE,
+        /**
+         * 播放停止
+         */
+        STOP,
+        /**
+         * 播放结束
+         */
+        COMPLETE
+    }
+}

+ 6 - 0
musicMerge/src/main/res/drawable/cover_selector_slider.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="10dp" />
+    <stroke android:width="4dp" android:color="#279FFE"/>
+</shape>

+ 7 - 0
musicMerge/src/main/res/drawable/shape_video_cover_top_bg.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient android:startColor="#0D0D0D"
+        android:endColor="#00000000"
+        android:angle="270"/>
+
+</shape>

+ 27 - 2
musicMerge/src/main/res/layout/ac_select_video_frame_layout.xml

@@ -1,6 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+
+
+    <com.cooleshow.musicmerge.widget.cover.ShortVideoSelectCover
+        android:id="@+id/video_cutter_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <include
+        android:id="@+id/toolbar_include"
+        layout="@layout/common_toolbar_layout_white"/>
+
+<!--    <androidx.recyclerview.widget.RecyclerView-->
+<!--        android:id="@+id/recyclerView"-->
+<!--        android:layout_marginBottom="22dp"-->
+<!--        app:layout_constraintBottom_toBottomOf="parent"-->
+<!--        android:layout_width="match_parent"-->
+<!--        android:layout_height="84dp"/>-->
+
+<!--    <View-->
+<!--        app:layout_constraintLeft_toLeftOf="parent"-->
+<!--        app:layout_constraintTop_toTopOf="@+id/recyclerView"-->
+<!--        android:background="#52000000"-->
+<!--        android:layout_width="match_parent"-->
+<!--        android:layout_height="84dp"/>-->
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 31 - 0
musicMerge/src/main/res/layout/item_sv_cover_view.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:seekbar="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:layout_centerVertical="true"
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="83dp"
+        android:overScrollMode="never" />
+
+    <View
+        android:background="#52000000"
+        android:layout_centerVertical="true"
+        android:layout_width="match_parent"
+        android:layout_height="83dp"/>
+
+    <com.cooleshow.musicmerge.widget.cover.RangeSlider
+        android:id="@+id/range_slider"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        seekbar:leftThumbDrawable="@drawable/cover_selector_slider"
+        seekbar:leftThumbIndex="0"
+        seekbar:lineColor="#00000000"
+        seekbar:maskColor="#00000000"
+        seekbar:thumbWidth="128dp"
+        seekbar:tickCount="100" />
+
+</RelativeLayout>

+ 42 - 0
musicMerge/src/main/res/layout/video_selector_layout.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="#F5F5F5"
+    android:orientation="vertical">
+
+    <FrameLayout
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <FrameLayout
+            android:id="@+id/flVideo"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:keepScreenOn="true" />
+
+        <ImageView
+            android:id="@+id/img_cover"
+            android:layout_width="match_parent"
+            android:scaleType="centerCrop"
+            android:visibility="gone"
+            android:layout_height="match_parent" />
+
+    </FrameLayout>
+
+
+    <com.cooleshow.musicmerge.widget.cover.CoverSliderView
+        android:id="@+id/video_cut_layout"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:layout_width="wrap_content"
+        android:layout_height="87dp"
+        android:layout_marginBottom="22dp"
+        android:layout_gravity="center_horizontal" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 13 - 0
musicMerge/src/main/res/values/attrs.xml

@@ -13,4 +13,17 @@
     <declare-styleable name="musicView">
         <attr name="itemColor" format="color"></attr>
     </declare-styleable>
+
+    <declare-styleable name="RangeSlider">
+        <attr name="lineHeight" format="dimension" />
+        <attr name="thumbWidth" format="dimension" />
+        <attr name="lineColor" format="color" />
+        <attr name="maskColor" format="color" />
+        <attr name="tickCount" format="integer" />
+        <attr name="leftThumbIndex" format="integer" />
+        <attr name="rightThumbIndex" format="integer" />
+        <attr name="leftThumbDrawable" format="reference" />
+        <attr name="rightThumbDrawable" format="reference" />
+        <attr name="midThumbDrawable" format="reference" />
+    </declare-styleable>
 </resources>

+ 5 - 0
musicMerge/src/main/res/values/dimens.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="item_thumb_height">84dp</dimen>
+    <dimen name="item_thumb_width">100dp</dimen>
+</resources>

+ 9 - 0
musicMerge/src/main/res/values/strings.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="updload_draft_tip">正在上传草稿</string>
+    <string name="upload_works_tip">正在上传作品</string>
+    <string name="video_merge_tip">视频合成中</string>
+    <string name="audio_merge_tip">音频合成中</string>
+    <string name="save_draft_tip">正在保存草稿</string>
+    <string name="loading_str">加载中</string>
+</resources>