ffmpeg - 视频裁剪

ffmpeg - 视频裁剪

最近遇到一个项目,需要实现一个视频在多个屏幕上播放,windows可以用硬件方案实现,在安卓系统上目测好像没有现成的硬件方案,只能硬着头皮用软件实现。

开始了解需求时还不知道要怎么做,由于之前用过ffmpeg做过视频截图,觉得很强大,就想到了能不能用ffmpeg分割视频,和其他人员商量了下觉得可行,接下来几天就是找资料,了解ffmpeg。

最后不负所望,做出来个demo,虽然还很多细节要考虑,但是前期工作已经完成了很大的一步,特在此记录下,也让有需要的参考下,让大佬提提建议(优化裁剪视频的时间)。

参考资料:
http://ffmpeg.org/ffmpeg.html#Video-and-Audio-file-format-conversion
https://www.it610.com/article/1280132859394277376.htm

ffmpeg linux环境安装教程:

  1. 安装:yasm
    wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
    ./configure --prefix=/usr/local/yasm
    make
    make install
  2. 安装:x264
    http://download.videolan.org/x264/snapshots/x264-snapshot-20191216-2245.tar.bz2
    ./configure --prefix=/usr/local/x264 --enable-shared --enable-static --enable-yasm
    make
    make install
  3. 安装ffmpeg
    下载:
    sudo git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
    sudo ./configure --prefix=/usr/local/ffmpeg --enable-shared --enable-yasm --enable-libx264 --enable-gpl --enable-pthreads --extra-cflags=-I/usr/local/x264/include --extra-ldflags=-L/usr/local/x264/lib
    make
    make install
  4. 执行ffmpeg命令
    如遇问题:ffmpeg: error while loading shared libraries: libavdevice.so.58: cannot open shared object file: No such file or directory
    问题解决:
    sudo vi /etc/ld.so.conf
    在文件中添加路径:
    /usr/local/ffmpeg/lib
    #usr/local/ffmpeg 目录是ffmpeg的安装目录,根据个人不同安装目录修改。
    更新环境变量:
    sudo ldconfig

视频裁剪Java代码:

/**
     * 视频裁剪   一张一张裁剪
     * @param url        文件路径
     * @param dirPath    存放裁剪后的文件夹地址
     * @param destRow    裁剪的行数
     * @param destColumn 裁剪的列数
     * @return
     */
    public static List<String> cut2(String url, String dirPath, int destRow, int destColumn) {
        log.info("视频裁剪入口2: url:{}, path:{}, destRow:{}, destColumn", url, dirPath, destRow, destColumn);
        List<String> resultList = new ArrayList<>();
        String suffix = url.substring(url.lastIndexOf("."));
        List<CommandDTO> commandDTOS = generateCoordinateAndFileName(suffix, destRow, destColumn);
        for (CommandDTO commandDTO : commandDTOS) {
            String destFileName = dirPath + "/" + commandDTO.getFileName();
            List<String> commands = new ArrayList<>();
            commands.add("ffmpeg");
            commands.add("-i");
            commands.add(url);
            commands.add("-vf");
            commands.add("crop=" + commandDTO.getCommand());
            commands.add("-vcodec");
            commands.add("libx264"); // 视频编码 需要安装x264编码库
            commands.add(destFileName);

            if(runCommand(commands) == 0) {
                resultList.add(destFileName);
            }
        }

        return resultList;
    }

    /**
     * 视频裁剪 一次性裁剪完成
     * @param url        文件路径
     * @param dirPath    存放裁剪后的文件夹地址
     * @param destRow    裁剪的行数
     * @param destColumn 裁剪的列数
     * @return
     */
    public static List<String> cut3(String url, String dirPath, int destRow, int destColumn) {
        log.info("视频裁剪入口3: url:{}, path:{}, destRow:{}, destColumn:{}", url, dirPath, destRow, destColumn);
        List<String> resultList = new ArrayList<>();
        String suffix = url.substring(url.lastIndexOf("."));
        List<CommandDTO> commandDTOS = generateCoordinateAndFileName(suffix, destRow, destColumn);

        List<String> commands = new ArrayList<>();
        commands.add("ffmpeg");
        commands.add("-i");
        commands.add(url);

        List<String> regions = new ArrayList<>();
        List<String> fileMaps = new ArrayList<>();
        for (CommandDTO commandDTO : commandDTOS) {
            String destFileName = dirPath + "/" + commandDTO.getFileName();
            regions.add("-filter_complex");
            regions.add("[0:v]crop=" + commandDTO.getCommand() + "[" + commandDTO.getCoordinate() + "]");

            fileMaps.add("-map");
            fileMaps.add("[" + commandDTO.getCoordinate() + "]");
            fileMaps.add("-map");
            fileMaps.add("0:a");
            fileMaps.add(destFileName);

            resultList.add(destFileName);
        }

        commands.addAll(regions);
        commands.addAll(fileMaps);
        log.info("即将执行的命令 command:{}", Arrays.toString(commands.toArray()));
        if(runCommand(commands) == 0) {
            return resultList;
        }

        return new ArrayList<>();
    }

    private static int runCommand(List<String> commands) {
        log.info("执行命令 command:{}", Arrays.toString(commands.toArray()));
        ProcessBuilder builder = new ProcessBuilder();
        builder.command(commands);
        try {
            Process process = builder.start();
            int i = process.waitFor();
            log.info("i:{}", i);
            if (i != 0) {
                log.error("命令执行错误:{}", process.getErrorStream());
            } else {
                log.info("命令执行成功");
            }

            return i;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return -1;
    }

    /**
     * 生成执行命令坐标以及对应文件名
     * @param suffix
     * @param destRow
     * @param destColumn
     * @return
     */
    private static List<CommandDTO> generateCoordinateAndFileName(String suffix, int destRow, int destColumn) {
        List<CommandDTO> commandDTOS = new ArrayList<>();
        // 时间字符串,作为文件名使用,可自定义,最好不重复
        String timeStr = FileUtil.generateTimeStr();
        // 裁剪的宽高
        String averageSize = "iw/" + destColumn + ":ih/" + destRow + ":";
        // 裁剪的坐标组合
        List<String> coordinates = buildCoordinates(destRow, destColumn);
        for (String c : coordinates) {
            String[] split = c.split(":");
            String x = split[0].substring(split[0].lastIndexOf("*") + 1);
            String y = split[1].substring(split[1].lastIndexOf("*") + 1);
            // 裁剪的起始坐标
            String area = x + "-" + y;
            // 裁剪后的文件名
            String fileName = timeStr + "_" + area + suffix;

            CommandDTO commandDTO = new CommandDTO(averageSize + c, area, fileName);
            commandDTOS.add(commandDTO);
        }

        return commandDTOS;
    }

    /**
     * 坐标点计算
     * @param row     行数
     * @param column  列数
     * @return
     */
    private static List<String> buildCoordinates(int row, int column) {
        if (row <= 0 || column <= 0) {
            throw new RuntimeException("裁剪的行,列必须都大于0");
        }

        List<String> commands = new ArrayList<>();
        for (int i = 0; i < column; i++) {
            for (int k = 0; k < row; k++) {
                String rowColumnCommand = "iw/" + column + "*" + i + ":ih/" + row + "*" + k;
                commands.add(rowColumnCommand);
            }
        }

        return commands;
    }

第一次研究,有问题的可以一起探讨,欢迎各位留言,提意见,觉得有用的点个赞啥的…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值