Javacv图片合成视频

项目场景:

公司最近的设备视频查看一直频繁报问题,看不到监控视频,设备安卓端多次查看无果后决定改变视频录制的方式,变成由安卓端截屏处理成jpg图片后面再由云端合成视频;


云端图片合成视频

选择依赖

        <!--opencv 处理图片-->
        <!-- https://mvnrepository.com/artifact/org.bytedeco/javacv -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!--解压tar.gz文件-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.20</version>
        </dependency>
        <dependency>
        <groupId>org.apache.ant</groupId>
        <artifactId>ant</artifactId>
        <version>1.8.1</version>
        </dependency>

代码测试:

	 @Test //测试方法
    void javacv(){

        String pathSuffix="C:\\data\\spring_logs\\";
        String pathSuffixTar="C:\\data\\spring_logs\\sdcard\\DCIM\\";
        String fileName="131081f8-4390-897b758e7c6c-test";
        String filePathPrefixMP4=".mp4";
        String filePathPrefixZIP=".zip";
        String filePathPrefixTarGZ=".tar.gz";


        File fileMP4 = new File(pathSuffix + fileName + filePathPrefixMP4);
        if (fileMP4.exists()){
            System.out.println(fileMP4.getName()+" mp4文件已存在");
            return;
        }
		
		//本来只要处理上传上来的zip文件就好了,但安卓端非要处理成tar.gz,难受我又搞不定解压的路径
		//tar.gz解压后会根据你解压文件的打包路径重新解压;就是会多出一个或多个文件包裹你解压的文件,我只能把解压好的文件移出到传入的路径下,并且把解压目录给删除掉;
        if (new File(pathSuffix + fileName + filePathPrefixZIP).exists()){
//                && !new File(pathSuffix + fileName).exists()){
            //解压文件zip
            try {
                zipUncompressed(pathSuffix + fileName + filePathPrefixZIP,pathSuffix);
            } catch (Exception e) {
                System.out.println(pathSuffix + fileName + " zip文件解压异常");
                if (new File(pathSuffix+fileName).exists()){
                    System.out.println("删除解压的异常文件夹:"+pathSuffix+fileName);
                    deleteDir(pathSuffix+fileName);
                }
                System.out.println("再次尝试解压:");
                try {
                    zipUncompressed(pathSuffix + fileName + filePathPrefixZIP,pathSuffix);
                } catch (Exception exception) {
                    System.out.println(pathSuffix + fileName + " zip文件解压再次异常");
                    return;
                }
            }
        } else if (new File(pathSuffix + fileName + filePathPrefixTarGZ).exists()){
//                &&!new File(pathSuffix+fileName).exists()){
            try {
                //解压文件tar.gz
//                unTarGz(pathSuffix+fileName+filePathPrefixTarGZ,pathSuffix);
                deCompressGZipFile(pathSuffix+fileName+filePathPrefixTarGZ,pathSuffix);
                //CompressUtil.decompressTarGz(new File(pathSuffix+fileName+filePathPrefixTarGZ),pathSuffix);
            } catch (Exception e) {
                System.out.println(pathSuffix + fileName + " tar文件解压异常");
                if (new File(pathSuffix+fileName).exists()){
                    System.out.println("删除解压的异常文件夹:"+pathSuffix+fileName);
                    deleteDir(pathSuffix+fileName);
                }
                System.out.println("再次尝试解压:");
                try {
//                    unTarGz(pathSuffix+fileName+filePathPrefixTarGZ,pathSuffix);
                    deCompressGZipFile(pathSuffix+fileName+filePathPrefixTarGZ,pathSuffix);
                    //CompressUtil.decompressTarGz(new File(pathSuffix+fileName+filePathPrefixTarGZ),pathSuffix);
                } catch (Exception exception) {
                    System.out.println(pathSuffix + fileName + " tar文件解压再次异常");
                    return;
                }
            }
        }

        //合成视频
            File fileJPG = new File(pathSuffixTar + fileName);
            if (fileJPG.isDirectory()&&fileJPG.exists()){
                System.out.println("开始合成视频");
                File[] files = fileJPG.listFiles();
                HashMap<Integer, File> imgMap = new HashMap<>();
                int width = 1600;
                int height = 900;
                for (File imgFile : files) {
                    String substring = imgFile.getName().substring(0, imgFile.getName().lastIndexOf(".")).trim();
                    int i =(int)Long.valueOf(substring).intValue();
                    imgMap.put(i, imgFile);
                }
                Object[] objects = imgMap.keySet().toArray();
                Arrays.sort(objects);
//                HashMap<Integer, File> imgMapSort = new HashMap<>();
//                for (Object object : objects) {
//                    imgMapSort.put((int)object,imgMap.get((int)object));
//                }
//                System.out.println("imgMapSort" + imgMapSort.toString());
                try {
                    createMp4(pathSuffixTar + fileName + filePathPrefixMP4, imgMap,objects, width, height);
                } catch (FrameRecorder.Exception e) {
                    System.out.println("合成失败!");
                }
            }

        File file = new File(pathSuffixTar+fileName+filePathPrefixMP4);
        if (file.isFile()){
            File toFile=new File(pathSuffix+fileName+filePathPrefixMP4);
            if (toFile.exists()){
                System.out.println("文件已存在");
            }
            else{
                file.renameTo(toFile);
                System.out.println("移动文件成功");
            }
        }
        System.out.println("删除文件");
        deleteDir(pathSuffix+"sdcard");
    }

    /**
     * zip文件解压
     *
     * @param inputFile   待解压文件夹/文件
     * @param destDirPath 解压路径
     */
    public static void zipUncompressed(String inputFile, String destDirPath) throws Exception {
        File srcFile = new File(inputFile);//获取当前压缩文件
        // 判断源文件是否存在
//        if (!srcFile.exists()) {
//            throw new Exception(srcFile.getPath() + "所指文件不存在");
//        }
        //开始解压
        //构建解压输入流
        ZipInputStream zIn = new ZipInputStream(new FileInputStream(srcFile));
        ZipEntry entry = null;
        File file = null;
        while ((entry = zIn.getNextEntry()) != null) {
            if (!entry.isDirectory()) {
                file = new File(destDirPath, entry.getName());
                if (!file.exists()) {
                    new File(file.getParent()).mkdirs();//创建此文件的上级目录
                }
                OutputStream out = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(out);
                int len = -1;
                byte[] buf = new byte[1024];
                while ((len = zIn.read(buf)) != -1) {
                    bos.write(buf, 0, len);
                }
                // 关流顺序,先打开的后关闭
                bos.close();
                out.close();
            }
        }
    }
    /**
     * Tar文件解压方法
     *
     * @param tarGzFile 要解压的压缩文件名称(绝对路径名称)
     * @param destDir   解压后文件放置的路径名(绝对路径名称)当路径不存在,会自动创建
     * @return 解压出的文件列表
     */
    public static void deCompressGZipFile(String tarGzFile, String destDir) throws Exception {
        File sourceFile = new File(tarGzFile);
        // decompressing *.tar.gz files to tar
        TarArchiveInputStream fin = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(sourceFile)));
        File extraceFolder = new File(destDir);
        TarArchiveEntry entry;
        // 将 tar 文件解压到 extractPath 目录下
        while ((entry = fin.getNextTarEntry()) != null) {
            if (entry.isDirectory()) {
                continue;
            }
            File curfile = new File(extraceFolder,entry.getName());
//            System.out.println("path : "+entry.getFile().getName());
            File parent = curfile.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }
            // 将文件写出到解压的目录
            IOUtils.copy(fin, new FileOutputStream(curfile));
        }
    }

太长了,我就再起一个,这里主要是视频合成和文件删除

	//文件删除
    public static boolean deleteDir(String path){
        File file = new File(path);
        if(!file.exists()){//判断是否待删除目录是否存在
            System.err.println("The dir are not exists!");
            return false;
        }

        String[] content = file.list();//取得当前目录下所有文件和文件夹
        for(String name : content){
            File temp = new File(path, name);
            if(temp.isDirectory()){//判断是否是目录
                deleteDir(temp.getAbsolutePath());//递归调用,删除目录里的内容
                temp.delete();//删除空目录
            }else{
                if(!temp.delete()){//直接删除文件
                    System.err.println("Failed to delete " + name);
                }
            }
        }
        file.delete();
        return true;
    }
	//视频合成
    private static void createMp4(String mp4SavePath, Map<Integer, File> imgMap, Object[] objects, int width, int height) throws FrameRecorder.Exception {
        //视频宽高最好是按照常见的视频的宽高  16:9  或者 9:16
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);
        //设置视频编码层模式
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        //设置视频为25帧每秒
        recorder.setFrameRate(25);
        //设置视频图像数据格式
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setFormat("mp4");
      //  recorder.setAudioBitrate(1300);
        try {
            recorder.start();
            Java2DFrameConverter converter = new Java2DFrameConverter();
            //根据文件数量定义视频时长
            for (Object object : objects) {
                File file = imgMap.get(object);
                BufferedImage read = ImageIO.read(file);
                //6:控制的是快进的速率/倍数
                for (int i = 0; i < 6; i++) {
                    recorder.record(converter.getFrame(read));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //最后一定要结束并释放资源
            recorder.stop();
            recorder.release();
        }
    }

虽然代码写的稀碎但可以正常跑,合成的图片不是太清晰,但能看;

后续更新

太坑了整了个这项目ja包由原先的40多M飙升到了800多M这哪是导依赖呀!这是注水呀!注的还不是一点半点,只能简化下导入的依赖了,简化后还又140M呐,先跑着吧!

<!--   测试减缩jar     window&&Linux-->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.1.0-1.5.1</version>
            <classifier>linux-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.1.0-1.5.1</version>
            <classifier>windows-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.6-1.5.1</version>
            <classifier>linux-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.6-1.5.1</version>
            <classifier>windows-x86_64</classifier>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>4.2.2-1.5.3</version>
            <classifier>linux-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>4.2.2-1.5.3</version>
            <classifier>windows-x86_64</classifier>
        </dependency>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值