溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何在JAVA中使用ffmepg處理視頻

發(fā)布時(shí)間:2021-05-14 16:31:44 來源:億速云 閱讀:150 作者:Leah 欄目:開發(fā)技術(shù)

如何在JAVA中使用ffmepg處理視頻?很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

FFmepg安裝

路徑:
然后在使用的類中生命一個(gè)全局變量就好

 private static String ffmpegPath  = "C:\\hk\\ffmpeg\\bin\\ffmpeg.exe";  //ffmepg的絕對路徑

視頻壓縮

注意:此壓縮視頻涉及轉(zhuǎn)碼,對cpu的占用比較大(能不壓縮盡量不壓縮)

/**
     * 壓縮視頻
     * @param convertFile  待轉(zhuǎn)換的文件
     * @param targetFile  轉(zhuǎn)換后的目標(biāo)文件
     */
    public static void toCompressFile(String convertFile,String targetFile) throws IOException {
        List<String> command = new ArrayList<String>();
        /**將視頻壓縮為 每秒15幀 平均碼率600k 畫面的寬與高 為1280*720*/
        command.add(ffmpegPath);
        command.add("-i");
        command.add(convertFile);
        command.add("-r");
        command.add("15");
        command.add("-b:v");
        command.add("600k");
        command.add("-s");
        command.add("1280x720");
        command.add(targetFile);
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = null;
        try {
            process = builder.start();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 使用這種方式會(huì)在瞬間大量消耗CPU和內(nèi)存等系統(tǒng)資源,所以這里我們需要對流進(jìn)行處理

        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        String line = "";
        while ((line = br.readLine()) != null) {
        }
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }
        logger.info("-------------------壓縮完成---轉(zhuǎn)存文件--"+targetFile+"-------------");
    }

獲取視頻合并

/**
     * ffmpeg合并多個(gè)視頻文件
     *
     * @param list       需要合并的多視頻url地址以List存放
     * @param outputDir  此處是ffmpeg 配置地址,可寫死如“E:/ffmpeg/bin/ffmpeg.exe”
     * @param outputFile 合并后的視頻存放地址,如:E:/mergevideo.mp4
     * @date: 2021/4/17 9:31
     * @return: void
     */
    public static String mergeVideo(List<String> list, String outputDir, String outputFile) {

        try {
            String format1 = "%s -i %s -c copy -bsf:v h364_mp4toannexb -f mpegts %s";
            List<String> commandList = new ArrayList<>(6);
            List<String> inputList = new ArrayList<>(6);
            for (int i = 0; i < list.size(); i++) {
                String input = String.format("input%d.ts", i + 1);
                String command = String.format(format1, ffmpegPath, list.get(i), outputDir + input);
                commandList.add(command);
                inputList.add(input);
            }
            String command = getCommand(outputDir,outputFile, inputList);
            commandList.add(command);
            Boolean falg = Boolean.FALSE;
            for (int i = 0; i < commandList.size(); i++) {
                if (execCommand(commandList.get(i)) > 0) falg = true;
            }
            if (falg) {
                for (int i = 0; i < inputList.size(); i++) {
                    if (i != commandList.size() - 1) {
                        File file = new File(outputDir + inputList.get(i));
                        file.delete();
                    }
                }
//                //刪除壓縮的文件
//                for (String s:list
//                     ) {
//                    new File(s).delete();
//                }
                return outputFile;
            } else {
                return "fail";
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("-----合并失敗!!!!!!" + outputFile);
            return "fail";
        }

    }
    private static Integer execCommand(String command) {

        logger.info("execCommand.exec command={}",command);
        try {
            Process process = Runtime.getRuntime().exec(command);
            //獲取進(jìn)程的標(biāo)準(zhǔn)輸入流
            final InputStream is1 = process.getInputStream();
            //獲取進(jìn)城的錯(cuò)誤流
            final InputStream is2 = process.getErrorStream();
            //啟動(dòng)兩個(gè)線程,一個(gè)線程負(fù)責(zé)讀標(biāo)準(zhǔn)輸出流,另一個(gè)負(fù)責(zé)讀標(biāo)準(zhǔn)錯(cuò)誤流
            readInputStream(is1);
            readInputStream(is2);
            process.waitFor();
            process.destroy();
            logger.info("-----操作成功" + command + " " + sdf.format(new Date()));
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("-----操作失敗" + command);
            return -1;
        }
    }

    private static void readInputStream(InputStream inputStream) {
        new Thread(() -> {
            BufferedReader br1 = new BufferedReader(new InputStreamReader(inputStream));
            try {
                String line1;
                while ((line1 = br1.readLine()) != null) {
                    if (line1 != null) {
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

視頻分片(分割)

/**
     * 將視頻分割為小段
     *
     * @param fileName    源文件名字(帶路徑)
     * @param outputPath  輸出文件路徑,會(huì)在該路徑下根據(jù)系統(tǒng)時(shí)間創(chuàng)建目錄,并在此目錄下輸出段視頻
     * @param videoTime   總時(shí)間,單位 分鐘
     * @param periodTime  小段視頻時(shí)長 單位 分鐘
     * @param merge       true合并,false單獨(dú)分割 說明:是否將整個(gè)視頻結(jié)尾部分不足一次分割時(shí)間的部分,合并到最后一次分割的視頻中,即false會(huì)比true多生成一段視頻
     */
    public static List<Map<String,Object>>  splitVideoFile(String fileName, String outputPath, float videoTime, int periodTime,  boolean merge) {
        final String TAG = "----------------------------";

        // 在outputPath路徑下根據(jù)系統(tǒng)時(shí)間創(chuàng)建目錄
        File file = createFileBySysTime(outputPath);
        if (file == null) {
            System.out.println("分割視頻失敗,創(chuàng)建目錄失敗");
            return null;
        }
        outputPath = file.getPath() + File.separator; // 更新視頻輸出目錄


        // 計(jì)算視頻分割的個(gè)數(shù)
        int count;// 分割為幾段
        float remain = 0; // 不足一次剪輯的剩余時(shí)間
        if (merge) {
            count = (int) (videoTime / periodTime);
            remain = videoTime % periodTime; // 不足一次剪輯的剩余時(shí)間
        } else {
            count = (int) (videoTime / periodTime) + 1;
        }
        System.out.println("將視頻分割為" + count + "段,每段約" + periodTime + "分鐘");

        String indexName; // 第 i 個(gè)視頻,打印日志用
        final String FFMPEG = ffmpegPath;
        String startTime; // 每段視頻的開始時(shí)間
        String periodVideoName; // 每段視頻的名字,名字規(guī)則:視頻i_時(shí)間段xx_yy
        float duration; // 每次分割的時(shí)長
        String command;// 執(zhí)行的命令
        // 得到視頻后綴 如.mp4
        String videoSuffix = fileName.substring(fileName.lastIndexOf("."));//得到點(diǎn)后面的后綴,包括點(diǎn)
        Runtime runtime = Runtime.getRuntime(); // 執(zhí)行命令者

        List<Map<String,Object>> list =new ArrayList<>();

        // 將視頻分割為count段
        for (int i = 0; i < count; i++) {
            Map<String,Object> map =new HashMap<>();
            indexName = "第" + (i+1) + "個(gè)視頻";

            // 決定是否將整個(gè)視頻結(jié)尾部分不足一次的時(shí)間,合并到最后一次分割的視頻中
            if (merge) {
                if (i == count - 1) {
                    duration = periodTime * 60 + remain * 60;// 將整個(gè)視頻不足一次剪輯的時(shí)間,拼接在最后一次剪裁中
                    if(periodTime * i /60 >= 1){
                        startTime = "0"+periodTime * i /60+ ":00:00";
                    }else{
                        startTime = periodTime * i + ":00";
                    }
                    periodVideoName = "video" + (i+1) + "_" + periodTime * i + "_end" + videoSuffix;
                } else {
                    duration = periodTime * 60;
                    if(periodTime * i /60 >= 1){
                        startTime = "0"+periodTime * i /60+ ":00:00";
                    }else{
                        startTime = periodTime * i + ":00";
                    }
                    periodVideoName = "video" +(i+1) + "_" + periodTime * i + "_" + periodTime * (i + 1) + videoSuffix;
                }
            } else {
                duration = periodTime * 60;
                if(periodTime * i /60 >= 1){
                    startTime = "0"+periodTime * i /60+ ":00:00";
                }else{
                    startTime = periodTime * i + ":00";
                }
                periodVideoName = "video" + (i+1) + "_" + periodTime * i + "_" + periodTime * (i + 1) + videoSuffix;
            }

            // 執(zhí)行分割命令
            try {
                // 創(chuàng)建命令
                command = FFMPEG + " -ss " + startTime +" -accurate_seek "+ " -i " + fileName + " -c copy -t " + duration + " " + outputPath + periodVideoName;

                System.out.println(TAG);
                System.out.println(indexName);
                System.out.println("執(zhí)行命令:" + command);
                runtime.exec(command);
                System.out.println(indexName + "分割成功");

                map.put("videoPath",(outputPath + periodVideoName).replace("\\","/"));
                map.put("count",i);
                list.add(map);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(indexName + "分割失敗!!!!!!");
            }
        }
        //刪除原來的大視頻
        new File(fileName).delete();

        return list;
    }
/**
     * 在指定目錄下根據(jù)系統(tǒng)時(shí)間創(chuàng)建文件夾
     * 文件名字eg:2019-07-02-23-56-31
     *
     * @param path 路徑:eg: "/Users/amarao/業(yè)余/剪輯/output/";
     *             結(jié)果:創(chuàng)建成功/Users/amarao/業(yè)余/剪輯/output/2019-07-03-10-28-05
     *             <p>
     *             步驟:
     *             1. 讀取系統(tǒng)時(shí)間
     *             2. 格式化系統(tǒng)時(shí)間
     *             3. 創(chuàng)建文件夾
     *             <p>
     *             參考:http://www.bubuko.com/infodetail-1685972.html
     */
    public static File createFileBySysTime(String path) {

        // 1. 讀取系統(tǒng)時(shí)間
        Calendar calendar = Calendar.getInstance();
        Date time = calendar.getTime();

        // 2. 格式化系統(tǒng)時(shí)間
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String fileName = format.format(time); //獲取系統(tǒng)當(dāng)前時(shí)間并將其轉(zhuǎn)換為string類型,fileName即文件名

        // 3. 創(chuàng)建文件夾
        String newPath = path + fileName;
        File file = new File(newPath);
        //如果文件目錄不存在則創(chuàng)建目錄
        if (!file.exists()) {
            if (!file.mkdir()) {
                System.out.println("當(dāng)前路徑不存在,創(chuàng)建失敗");
                return null;
            }
        }
        System.out.println("創(chuàng)建成功" + newPath);
        return file;
    }

獲取視頻的時(shí)長

/**
     * 獲取視頻時(shí)長 單位/秒
     * @param video
     * @return
     */
    public static long getVideoDuration(File video) {
        long duration = 0L;
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
        try {
            ff.start();
            duration = ff.getLengthInTime() / (1000 * 1000 * 60);
            ff.stop();
        } catch (FrameGrabber.Exception e) {
            e.printStackTrace();
        }
        return duration;
    }

視頻剪切

 /**
     *
     *剪切視頻
     videoInputPath 需要處理的視頻路徑
     startTime: 截取的開始時(shí)間 格式為 00:00:00(時(shí)分秒)
     endTime: 截取的結(jié)束時(shí)間 格式為00:03:00(時(shí)分秒) 
     devIp: 通道號(hào)  業(yè)務(wù)存在 ,可自行刪除
          * */

    public static String videoClip(String videoInputPath,String startTime,String endTime,String devIp) throws IOException {


        SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat dtf=new SimpleDateFormat("yyyyMMddHHmmss");

        //判斷轉(zhuǎn)碼文件是否存在
        if(!new File(videoInputPath).exists()){
            System.out.println("需要處理的視頻不存在");
            return null;
        }
        StringBuffer videoOutPath = new StringBuffer();
        videoOutPath.append("C:/video/playBack/"+devIp+"/"+sdf1.format(new Date())+"/clip/");
        File file = new File(videoOutPath.toString());
        if (!file.exists()){
            file.mkdirs();
        }
        videoOutPath.append(dtf.format(new Date())+".mp4");
        List<String> command = new ArrayList<String>();
        command.add(ffmpegPath);
        command.add("-ss");
        command.add(startTime);
        command.add("-t");
        command.add(endTime);
        command.add("-i");
        command.add(videoInputPath);
        command.add("-c");
        command.add("copy");
        command.add(videoOutPath.toString());
        command.add("-y");
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = null;
        try {
            process = builder.start();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        String line = "";
        while ((line = br.readLine()) != null) {
        }
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }

        return videoOutPath.toString();
    }

視頻轉(zhuǎn)GIF(MP4)

/*
     * 視頻轉(zhuǎn)gif
     *inputVideoPath   需要轉(zhuǎn)換的視頻
     * createGif  gif保存的位置
     * */
    public static String mp4ToGif(String inputVideoPath,String createGif) {
        String name = UUID.randomUUID().toString().replaceAll("-", "");
        String paletteFile = createGif +name + ".png";
        String gifFile = createGif+name + ".gif";
        boolean isComplete = false;
        //操作ffmpeg生成gif圖片
        for (int i = 0; i < 5; i++) {
            try {
                //生成調(diào)色板
                Process p = new ProcessBuilder()
                        .command(ffmpegPath,
                                "-v", "warning",
                                "-ss", "2",
                                "-t", "10",
                                "-i", inputVideoPath,
                                "-vf", "fps=5,scale=400:-1:flags=lanczos,palettegen",
                                "-y", paletteFile, "-vn")
                        .redirectError(new File("stderr.txt"))
                        .start();
                isComplete = p.waitFor(10, TimeUnit.SECONDS);

                if (!isComplete) {
                    System.out.println("生成調(diào)色板出錯(cuò)");
                } else {
                    List<String> command = new ArrayList<String>();
                    /**將視頻壓縮為 每秒15幀 平均碼率600k 畫面的寬與高 為1280*720*/
                    command.add(ffmpegPath);
                    command.add("-v");
                    command.add("warning");
                    command.add("-ss");
                    command.add("2");
                    command.add("-t");
                    command.add("10");
                    command.add("-i");
                    command.add(inputVideoPath);
                    command.add("-i");
                    command.add(paletteFile);
                    command.add("-lavfi");
                    command.add("fps=5,scale=400:-1:flags=lanczos [x]; [x][1:v] paletteuse");
                    command.add("-y");
                    command.add(gifFile);
                    command.add("-vn");
                    ProcessBuilder builder = new ProcessBuilder(command);
                    Process process = null;
                    try {
                        process = builder.start();
                        isComplete = process.waitFor(10, TimeUnit.SECONDS);
                        if (isComplete) {
                            new File(paletteFile).delete();
                            System.out.println("生成gif成功");
                            break;
                        } else {
                            System.out.println("生成gif出錯(cuò)");
                        }
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
//                process = new ProcessBuilder()
//                            .command(ffmpegPath,
//                                    "-v", "warning",
//                                    "-ss", "2",
//                                    "-t", "10",
//                                    "-i", "E:\\Video_2021-05-14_113013.mp4",
//                                    "-i", paletteFile,
//                                    "-lavfi", "fps=5,scale=400:-1:flags=lanczos [x]; [x][1:v] paletteuse",
//                                    "-y", gifFile, "-vn")
//                            .redirectError(new File("stderr.txt"))
//                            .start();
//                    isComplete = process.waitFor(10, TimeUnit.SECONDS);
//                    if (isComplete) {
//                        System.out.println("生成gif成功");
//                        break;
//                    } else {
//                        System.out.println("生成gif出錯(cuò)");
//                    }
                }
            } catch (Exception e) {
                System.out.println("生成gif出錯(cuò)");
            }
        }

        return gifFile;
    }

java基本數(shù)據(jù)類型有哪些

Java的基本數(shù)據(jù)類型分為:1、整數(shù)類型,用來表示整數(shù)的數(shù)據(jù)類型。2、浮點(diǎn)類型,用來表示小數(shù)的數(shù)據(jù)類型。3、字符類型,字符類型的關(guān)鍵字是“char”。4、布爾類型,是表示邏輯值的基本數(shù)據(jù)類型。

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI