项目实训—基于AI的智能视频剪辑器(八)剪辑页面

文章目录

  • 前言
  • WorkFragment
    • RecyclerViewAdapter


前言

在获取到后端返回的全部相关人物视频片段后,前端需要提供给用户一个剪辑的窗口。用户可以自由对返回片段进行排序重组、并且对视频片段进行进一步的时长裁剪等调整。


WorkFragment

在进入剪辑页面后,首先需要根据视频片段合成初始的完整视频
返回的视频片段,放入特定文件夹中,需要根据文件路径获取所有的视频片段路径,创建 txt 文件 videolist.txt
由于项目已经集成了 ffmpeg 框架,这里直接可以执行 FFmpegCmd 的方法执行命令

    public void concat_video(){
        String strPath = base_path + "work/" + fileName;
        File dir = new File(strPath);//文件夹dir
        File[] files = dir.listFiles();//文件夹下的所有文件或文件夹
        if (files != null){
            for (int i = 0; i < files.length; i++) {
                PageLog.dTag(TAG,files[i].getAbsolutePath());
                UrlList.add(files[i].getName());
            }
        }
        try {
            File videolist = new File(strPath + "/videolist.txt");
            if(!videolist.exists())
                videolist.createNewFile();
            BufferedWriter out = new BufferedWriter(new FileWriter(videolist,true));
            for(int i = 0 ;i < UrlList.size(); i++){
                String content = "file '" + UrlList.get(i) + "'";
                out.write(content);
                out.newLine();
            }
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String cmd = "ffmpeg -f concat -i " + strPath + "/videolist.txt " + "-c copy " + video_url_work;
        PageLog.dTag(TAG,cmd);
        fFmpegCmd.ffmpeg_cmd(cmd);
    }

剪辑页面主要是视频播放器,此外也需要创建一个 RecyclerView,来展示所有的视频片段

    protected void initViews() {
        mTitles = ResUtils.getStringArray(R.array.work_titles);

        mVideoView = binding.player;
        StandardVideoController controller = new StandardVideoController(this.getContext());
        //根据屏幕方向自动进入/退出全屏
        controller.setEnableOrientation(true);

        PrepareView prepareView = new PrepareView(this.getContext());//准备播放界面
        prepareView.setClickStart();

        ImageView thumb = prepareView.findViewById(R.id.thumb);//封面图
        Glide.with(this).setDefaultRequestOptions(
                new RequestOptions()
                        .frame(0)
                        .centerCrop()
        ).load(video_url_work).placeholder(android.R.color.darker_gray).into(thumb);

//        Glide.with(this).load(THUMB).into(thumb);

        controller.addControlComponent(prepareView);
        controller.addControlComponent(new CompleteView(this.getContext()));//自动完成播放界面
        controller.addControlComponent(new ErrorView(this.getContext()));//错误界面
        TitleView titleView = new TitleView(this.getContext());//标题栏
        controller.addControlComponent(titleView);
        VodControlView vodControlView = new VodControlView(this.getContext());//点播控制条
        controller.addControlComponent(vodControlView);
        GestureView gestureControlView = new GestureView(this.getContext());//滑动控制视图
        controller.addControlComponent(gestureControlView);

        titleView.setTitle(fileName);
        mVideoView.setVideoController(controller);

        //播放状态监听
        mVideoView.addOnStateChangeListener(mOnStateChangeListener);

        mVideoView.setUrl(video_url_work);
        mVideoView.start();

        mRecyclerView = binding.recycleVideoView;
        LinearLayoutManager mLinearLayoutManager  = new LinearLayoutManager(getContext());
        mLinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        mRecyclerView.setLayoutManager(mLinearLayoutManager);
        mAdapter = new RecyclerViewAdapter(base_path, fileName, UrlList);
        mRecyclerView.setAdapter(mAdapter);

        helper.attachToRecyclerView(mRecyclerView);
        mRecyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(mRecyclerView) {
            @Override
            public void onItemClick(RecyclerView.ViewHolder vh) {
            }

            @Override
            public void onItemLongClick(RecyclerView.ViewHolder vh) {
                //如果item不是最后一个,则执行拖拽
                if (vh.getLayoutPosition() != UrlList.size() - 1) {
                    helper.startDrag(vh);
                } else if (!TextUtils.equals(UrlList.get(UrlList.size() - 1), "add")) {
                    helper.startDrag(vh);
                }
            }
        });
    }

RecyclerViewAdapter

RecyclerView 的数据与视图的匹配,需要 Adapter 适配器
这里主要是根据视频 url 获取视频的第一帧图片,并且显示在 ImageView 上

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.VideoHolder>{

    private ArrayList<String> UrlList;
    private String absolut_url;

    public RecyclerViewAdapter(String base_path, String fileName, ArrayList<String> UrlList) {
        this.UrlList = UrlList;
        absolut_url = base_path + "work/" + fileName + "/";
    }

    @NonNull
    @Override
    public VideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_thumb, parent, false);
        return new RecyclerViewAdapter.VideoHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull VideoHolder holder, int position) {
        String url = absolut_url + UrlList.get(position);
        PageLog.dTag("WorkFragment", url);
        // 设置封面图片
        Glide.with(holder.mThumb.getContext()).setDefaultRequestOptions(
                new RequestOptions()
                        .frame(0)
                        .centerCrop()
        ).load(url).placeholder(android.R.color.darker_gray).into(holder.mThumb);
        holder.mPosition = position;
    }

    @Override
    public int getItemCount() {
        return UrlList.size();
    }

    public class VideoHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public int mPosition;
        public ImageView mThumb;

        VideoHolder(View itemView) {
            super(itemView);
            mThumb = itemView.findViewById(R.id.vthumb);
            itemView.setTag(this);
        }

        @Override
        public void onClick(View v) {
        }
    }
}

这里实现了长按视频片段,可以拖动视频,改变视频片段排序的功能,需要一个 ItemTouchHelper,用于实现Recyclerview 拖拽效果的帮助

    private ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //设置监听拖拽的方向
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            int swipeFlags = 0;
            return makeMovementFlags(dragFlags, swipeFlags);
        }

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            int fromPosition = viewHolder.getAdapterPosition();//得到item原来的position
            int toPosition = target.getAdapterPosition();//得到目标position
            if ((toPosition == UrlList.size() - 1 || UrlList.size() - 1 == fromPosition) && TextUtils.equals(UrlList.get(UrlList.size() - 1), "add")) {
                return true;
            }
            //滑动事件
            Collections.swap(UrlList, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            mAdapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            // 修改原视频顺序
            reorder_video();
            return false;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        }

        @Override
        public boolean isLongPressDragEnabled() {
            //是否可拖拽
            return false;
        }

        @Override
        public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
            super.onSelectedChanged(viewHolder, actionState);
            if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
                viewHolder.itemView.setScaleX(1.1f);
                viewHolder.itemView.setScaleY(1.1f);
            }
        }

        @Override
        public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            super.clearView(recyclerView, viewHolder);
            if (!recyclerView.isComputingLayout()) {
                //拖拽结束后恢复view的状态
                viewHolder.itemView.setScaleX(1.0f);
                viewHolder.itemView.setScaleY(1.0f);
            }
        }
    });

需要为 RecyclerView 设置点击每一个子组件的监听,特别为手势点击、长按来进行监听。

public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

        private GestureDetectorCompat mGestureDetector;
        private RecyclerView recyclerView;

        public OnRecyclerItemClickListener(RecyclerView recyclerView) {
            this.recyclerView = recyclerView;
            mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener());
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            mGestureDetector.onTouchEvent(e);
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
            mGestureDetector.onTouchEvent(e);
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        }

        private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null) {
                    RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                    onItemClick(vh);
                }
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null) {
                    RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                    onItemLongClick(vh);
                }
            }
        }

        public abstract void onItemClick(RecyclerView.ViewHolder vh);

        public abstract void onItemLongClick(RecyclerView.ViewHolder vh);

    }

在长按拖动视频片段排序后,需要相应重新调整合成视频的片段排序

    public void reorder_video(){
        String strPath = base_path + "work/" + fileName;
        try {
            File videolist = new File(strPath + "/videolist.txt");
            BufferedWriter out = new BufferedWriter(new FileWriter(videolist,false));
            for(int i = 0 ;i < UrlList.size(); i++){
                String content = "file '" + UrlList.get(i) + "'";
                out.write(content);
                out.newLine();
            }
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String cmd = "ffmpeg -f concat -i " + strPath + "/videolist.txt " + "-c copy " + video_url_work;
        PageLog.dTag(TAG,cmd);
        fFmpegCmd.ffmpeg_cmd(cmd);

        mVideoView.release();
        mVideoView.setUrl(video_url_work);
        mVideoView.start();
    }

你可能感兴趣的:(项目实训,android,java)