乐趣区

关于java:傻瓜式JAVA-MP4加密转码M3u8-并获取视频大小长度

======= 结尾重要绿色链接,不看悔恨,和上面关联 ==========
JAVA 无需本地下载 Ffmpeg,实现 FfmpegCMD

本文不须要本地下载 Ffmpeg,只需引入 maven 依赖即可

1、增加 maven 依赖

<!--m3u8 视频转码须要 FFmpeg 依赖 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-all-deps</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win64</artifactId>
            <version>3.3.1</version>
        </dependency>

2、编写代码

package com.gxw.util;

import com.alibaba.fastjson.JSON;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description:(MP4 转码 HLS m3u8 AES128 加密)
 * @author: GuoXiangWen
 * @date:   2022 年 04 月 16 日 上午 08:08:08
 */
public class EncodeVideoUtils {
    private static String PREIX = "gxwAwdlpgyn";
    // 加密文件门路,如:enc.key、enc.keyinfo、m3u8 文件、ts 文件等
    private static String ENC_DIRECTORY = "D:/test-ffmpeg/"+PREIX;

    //enc.key 地址
    private static String ENC_KEY_FILE_PATH = null;

    //enc.keyinfo 地址
    private static String ENC_KEY_INFO_FILE_PATH = null;

    // 密钥 http 地址
    private static String ENC_HTTP_PATH = "http://127.0.0.1:8848/Student";

    // 执行胜利 0, 失败 1
    private static int CODE_SUCCESS = 0;
    private static int CODE_FAIL = 1;

    //$encInfoPath、$encPath 是须要替换的 ENC_DIRECTORY 文件门路
    private static String cmd_enc = "-y -i {videoPath} -hls_time 12 -hls_key_info_file $encInfoPath -hls_playlist_type vod -hls_segment_filename $encPath\\{tsName}_%3d.ts $encPath\\{saveM3u8Name}";

    public static void main(String[] args) {
        // 视频门路
        String videoPath = "D:/test-ffmpeg/test.mp4";
        System.out.println(JSON.toJSONString(encodeVideo(videoPath, "test", "1")));
    }

    /**
     * 第一步:创立 enc.keyinfo 文件
     *      * 第二步:HLS m3u8 AES128 加密
     * @param videoPath 视频门路
     * @param videoType 视频所属类型,* @param index 视频角标,传来递增的
     * @return
     */
    public synchronized static Map<String, String> encodeVideo(String videoPath, String videoType, String index) {
        String videoTypeBP = videoType;
        String mp4Name = videoPath.substring(videoPath.lastIndexOf('/') + 1, videoPath.lastIndexOf(".mp4"));
        String saveM3u8Name = PREIX + RandomUtils.generateRandomString(32);

        // 设置 tsName
        cmd_enc = cmd_enc.replace("{tsName}", saveM3u8Name);

        // 设置视频地址
        cmd_enc = cmd_enc.replace("{videoPath}", videoPath);

        // 设置视频保留名称地址

        cmd_enc = cmd_enc.replace("{saveM3u8Name}", saveM3u8Name + ".m3u8");
        if(videoType.equals("") || videoType == null){videoType = "";}else {videoType = "/" + Md5Utils.md5Password(videoType, 8);
        }
        ENC_DIRECTORY = ENC_DIRECTORY + videoType + "/" + index;

        ENC_KEY_FILE_PATH = ENC_DIRECTORY + "/" + saveM3u8Name + ".key";
        ENC_KEY_INFO_FILE_PATH = ENC_DIRECTORY + "/" + saveM3u8Name + ".keyinfo";

        ENC_HTTP_PATH = ENC_HTTP_PATH + "/" + PREIX + videoType + "/" + index + "/" + saveM3u8Name + ".key";

        // 先创立两个流动线程的线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);

        // 异步执行
        // 第一步:创立 enc.keyinfo 文件等
        CompletableFuture<String> completableFutureTask = CompletableFuture.supplyAsync(() ->{

            // 保留加密视频目录 enc
            File encFilePathDir = new File(ENC_DIRECTORY);

            if (!encFilePathDir.exists()) {// 判断目录是否存在
                encFilePathDir.mkdirs();}

            // 写入文件内容 enc.key
            BufferedWriter bwkey = null;
            // 写入文件内容 enc.keyinfo
            BufferedWriter bwkeyInfo = null;

            try{// 文件
                File fileKey = new File(ENC_KEY_FILE_PATH);
                File fileKeyInfo = new File(ENC_KEY_INFO_FILE_PATH);

                // 初始化存在删除
                if(fileKey.exists()) {fileKey.delete();
                }
                if(fileKeyInfo.exists()) {fileKeyInfo.delete();
                }
                bwkey = new BufferedWriter(new FileWriter(fileKey));
                bwkeyInfo = new BufferedWriter(new FileWriter(fileKeyInfo));

                // 写入 key-- 自定义的 AES128 加密的密匙
                bwkey.write("******** 换成本人加密钥匙即可 *******");
                // 写入 keyInfo
                // 密匙 URL 地址,能够对该 URL 鉴权
                bwkeyInfo.write(ENC_HTTP_PATH);
                bwkeyInfo.newLine();
                // 全门路,绝对路径
                bwkeyInfo.write(ENC_KEY_FILE_PATH);

                bwkey.flush();
                bwkeyInfo.flush();}catch(IOException e){e.printStackTrace();
                // 复原默认
                ENC_KEY_INFO_FILE_PATH = null;
            } finally{
                try {
                    // 肯定要敞开文件
                    bwkey.close();
                    bwkeyInfo.close();} catch (IOException e) {e.printStackTrace();
                }
            }
            return ENC_KEY_INFO_FILE_PATH;
        }, executor).exceptionally(e -> {System.out.println(e);
            return "false";
        });
        // 异步执行
        // 第二步:HLS m3u8 AES128 加密
        CompletableFuture<Integer> completableFutureTaskHls = completableFutureTask.thenApplyAsync((String encKeyInfoFilePath)->{if(encKeyInfoFilePath == null || encKeyInfoFilePath.length() == 0) {return CODE_FAIL;}
            Integer codeTmp =  cmdExecut(cmd_enc.replace("$encInfoPath", encKeyInfoFilePath).replace("$encPath", ENC_DIRECTORY));
            if(CODE_SUCCESS != codeTmp) {return CODE_FAIL;}
            System.out.println("类型:"+videoTypeBP+"视频:" + mp4Name + "加密, 胜利!");
            return codeTmp;
        }, executor).exceptionally(e -> {System.out.println(e);
            return null;
        });

        // 获取执行后果
        //code= 0 示意失常
        try {System.out.println(String.format("获取最终执行后果:%s", completableFutureTaskHls.get() == CODE_SUCCESS ? "胜利!" : "失败!"));
        } catch (InterruptedException e) {Thread.currentThread().interrupt();
            e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();
        }
        // 返回后果
        Map<String, String> resultMap = new HashMap<>();
        // 视频名称
        resultMap.put("videoName", mp4Name);
        // 视频大小
        resultMap.put("videoSize", getVideoSize(new File(videoPath)));
        // 视频时长
        resultMap.put("videoTime", getVideoTime(videoPath));
        // 保留 m3u8 加密地址
        resultMap.put("saveM3u8Path", ENC_DIRECTORY + "/" + saveM3u8Name + ".m3u8");
        return resultMap;
    }

    /**
     *
     * @Description: (执行 ffmpeg 自定义命令)
     * @param: @param cmdStr
     * @param: @return
     * @return: Integer
     * @throws
     */
    public static Integer cmdExecut(String cmdStr) {
        //code= 0 示意失常
        Integer code  = null;
        FfmpegCmd ffmpegCmd = new FfmpegCmd();
        /**
         * 谬误流
         */
        InputStream errorStream = null;
        try {ffmpegCmd.execute(false, true, cmdStr);
            errorStream = ffmpegCmd.getErrorStream();

            // 打印过程
            int len = 0;
            while ((len=errorStream.read())!=-1){System.out.print((char)len);
            }

            //code= 0 示意失常
            code = ffmpegCmd.getProcessExitCode();} catch (IOException e) {e.printStackTrace();
        } finally {
            // 敞开资源
            ffmpegCmd.close();}
        // 返回
        return code;
    }

    /**
     * 获取视频时长
     *
     * @param FileUrl
     * @return
     */
    public static String getVideoTime(String FileUrl) {File source = new File(FileUrl);
        String length = "";
        try {MultimediaObject instance = new MultimediaObject(source);
            MultimediaInfo result = instance.getInfo();
            long ls = result.getDuration() / 1000;
            Integer hour = (int) (ls / 3600);
            Integer minute = (int) (ls % 3600) / 60;
            Integer second = (int) (ls - hour * 3600 - minute * 60);
            String hr = hour.toString();
            String mi = minute.toString();
            String se = second.toString();
            if (hr.length() < 2) {hr = "0" + hr;}
            if (mi.length() < 2) {mi = "0" + mi;}
            if (se.length() < 2) {se = "0" + se;}
            length = hr + ":" + mi + ":" + se;
        } catch (Exception e) {e.printStackTrace();
        }
        return length;
    }


    /**
     * 获取视频大小
     *
     * @param source
     * @return
     */
    public static String getVideoSize(File source) {
        FileChannel fc = null;
        String size = "";
        try {FileInputStream fis = new FileInputStream(source);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            size = fileSize.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_UP) + "MB";
        } catch (FileNotFoundException e) {e.printStackTrace();
        } catch (IOException e) {e.printStackTrace();
        } finally {if (null != fc) {
                try {fc.close();
                } catch (IOException e) {e.printStackTrace();
                }
            }
        }
        return size;
    }


}

3、运行测试

取得以下后果,即胜利:

退出移动版