欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java下载m3u8视频

程序员文章站 2022-05-08 09:14:29
@tocjava下载m3u8视频m3u8视频主要由 m3u8文件、key、ts文件 三部分组成1.创建工具类@Slf4jpublic class M3U8Factory { private RestTemplate restTemplate = new RestTemplate(); //外部输入参数 private String title;//视频名字 private String dir;//本地文件夹 private String uri;/...

java下载m3u8视频

m3u8视频主要
由 m3u8文件、key、ts文件 三部分组成

1.创建工具类

@Slf4j
public class M3U8Factory {

    private RestTemplate restTemplate = new RestTemplate();

    //外部输入参数
    private String title;//视频名字
    private String dir;//本地文件夹

    private String uri;//host地址
    private String m3u8;//m3u8原文件名+后缀
    private String m3u8Path;//m3u8本地保存路径
    
    public void setM3u8(String m3u8Url, String title, String outPath) {
        this.uri = m3u8Url.substring(0, m3u8Url.lastIndexOf("/") + 1);
        this.m3u8 = m3u8Url.substring(m3u8Url.lastIndexOf("/") + 1);
        this.title = title;
        this.dir = outPath;
    }

	public void build(){
        init();
        downloadM3U8(); //下载m3u8文件
        downloadKey(); //下载key
        updateM3U8File(); // 修改m3u8文件
        //downloadTs(); //下载ts文件
        merge(); //合成
    }

    private void init() {
        File directory = new File(dir);
        if (directory.exists() == true) {
            log.info("目录已存在");
        } else {
            directory.mkdirs();
            log.info("目录创建完成");
        }
    }
}

2.下载m3u8文件

    /**
     * 下载m3u8文件,包含key的下载地址(如果有) 和 ts文件名
     */
    private String downloadM3U8() {

        ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(uri + m3u8, byte[].class);
        //System.out.println(new String(forEntity.getBody()));

        if (title == null) {
            m3u8Path = dir + "\\\\" + m3u8;
        } else {
            m3u8Path = dir + "\\\\" + title + ".m3u8";
        }
        File file = new File(m3u8Path);

        try {
            Files.write(file.toPath(), Objects.requireNonNull(forEntity.getBody(), "未获取到文件"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(forEntity.getBody());
    }

3.下载key

    /**
     * 下载key,分两种
     * 一种不加密的,直接把key放出来的,复制黏贴就行,此处代码无视
     * 一种加密的,需要发起请求,下载key
     * @throws Exception
     */
    public String downloadKey() throws Exception {
        String uri = "填入key的下载链接";
        String key = null;

        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

        // params 如果有的话
        LinkedHashMap<String, String> param = new LinkedHashMap<>();        
        //param.put...

        // herders 如果有的话
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("user-agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Mobile Safari/537.36");
        httpHeaders.add("cookie", "填入cookie,如果有的话");
        HttpEntity<Object> request = new HttpEntity<>(httpHeaders);

        try {
        
            ResponseEntity<byte[]> entity = restTemplate.exchange(uri, HttpMethod.GET, request, byte[].class, param);
            key = new String(entity.getBody());
            log.info("key = {}", key);
            
            File file = new File(dir + "//key.key");
            Files.write(file.toPath(), Objects.requireNonNull(entity.getBody(), "未获取到文件"));
            
        } catch (HttpClientErrorException e) {
            log.info(e);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return key;
    }

4.关于ts文件

两种处理方式

  • 下载到本地合成
    将 m3u8文件中,每个ts文件加上 本地路径
  • 在线合成
    将 m3u8文件中,每个ts文件加上 uri
    //修改m3u8文件
    private void updateM3U8File() {
        String keyPath = dir + "\\\\key.key";
        log.info("-------------------------------------");
        log.info("keyPath = {}", keyPath);
        log.info("修改m3u8文件");

        File file = new File(m3u8Path);
        String line = null;
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new FileReader(file));
            while ((line = br.readLine()) != null) {
                if (line.startsWith("#EXT-X-KEY")) {
                    line = "#EXT-X-KEY:METHOD=AES-128,URI=\"" + keyPath + "\"";
                }
                if (line.endsWith(".ts")) {//修改ts文件地址
                    //line = uri + line;  //在线
                    line = dir + "\\\\" + line; //本地
                }
                stringBuffer.append(line + "\n");
            }
            //System.out.println("stringBuffer: " + stringBuffer);
            bw = new BufferedWriter(new FileWriter(file));
            bw.write(stringBuffer.toString());
            bw.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //获取ts文件名
    public List<String> getTsList(String m3u8Content) {
        List<String> tsList = new ArrayList<>();
        BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(m3u8Content.getBytes(Charset.forName("utf8"))), Charset.forName("utf8")));
        String line;
        try {
            while ((line = br.readLine()) != null) {
                if (line.endsWith("ts")) {
                    tsList.add(line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        tsList.forEach(System.out::println);
        return tsList;
    }

    //下载ts文件
    public void downloadTs(String m3u8Content) {
        List<String> tsList = getTsList(m3u8Content);
        long startTime = System.currentTimeMillis();//开始时间

        for (String name : tsList) {
            ResponseEntity<byte[]> forEntity = restTemplate.getForEntity((uri + name), byte[].class);
            //System.out.println("结果 = " + forObject);
            File file = new File((dir + "//" + name));
            try {
                Files.write(file.toPath(), Objects.requireNonNull(forEntity.getBody(), "未获取到文件"));
                log.info("写入文件 = {}", file.toPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        long endTime = System.currentTimeMillis();//结束时间
        log.info("ts文件下载耗时:" + (endTime - startTime) + "ms");
    }

5.合成视频

工具类在下面

    public void merge() {
        //例子:FfmpegUtils.mergeVedio("D:\\下载m3u8\\index.m3u8","D:\\下载m3u8\\new.flv");
        log.info("-------------------------------------");
        log.info("m3u8文件地址 = {}", m3u8Path);
        log.info("输出地址 = {}", dir + "\\\\" + title + ".mp4");
        log.info("开始合成");
        FfmpegUtils.mergeVedio(m3u8Path, dir + "\\\\" + title + ".mp4");
    }

二、关于ffmpeg

1.简单用法

cmd 运行

  • 本地合成
    ffmpeg -y -allowed_extensions ALL -i D:\下载m3u8\index.m3u8 -c copy D:\下载m3u8\new.MP4
  • 在线合成
    ffmpeg -y -allowed_extensions ALL -protocol_whitelist "file,http,https,crypto,tcp,tls" -i D:\下载m3u8\index.m3u8 -c copy D:\下载m3u8\new.MP4

2.创建工具类

@Slf4j
public class FfmpegUtils {

    private String inputPath;
    private String outputPath;
    private static String ffmpegPath = "D:\\Program Files\\ffmpeg-4.3.1\\bin\\ffmpeg.exe"; // 填你本地的ffmpeg.exe路径


    public static void mergeVedio(String inputPath, String outputPath) {

        StringBuffer command = new StringBuffer("");
        command.append(ffmpegPath);
        command.append(" -y");
        command.append(" -allowed_extensions");
        command.append(" ALL");
        //command.append(" -protocol_whitelist");
        //command.append(" file,http,https,crypto,tcp,tls");
        command.append(" -i");
        command.append(" " + inputPath);
        command.append(" -c");
        command.append(" copy");
        command.append(" " + outputPath);

        try {
            final Process process = Runtime.getRuntime().exec(command.toString());

            log.info("start run cmd {}", command);
            //⚠️此处代码是因为如果合并大视频文件会产生大量的日志缓存导致线程阻塞,最终合并失败,所以加两个线程处理日志的缓存,之后再调用waitFor方法,等待执行结果。
            //打印输出信息
            new Thread(() -> {
                BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line = null;
                try {
                    while ((line = in.readLine()) != null) {
                        System.out.println("output:" + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            //打印错误信息
            new Thread(() -> {
                BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                String line = null;
                try {
                    while ((line = err.readLine()) != null) {
                        System.out.println("err:" + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        err.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            // 等待命令子线程执行完成
            process.waitFor();
            process.destroy();

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

    }

}

本文地址:https://blog.csdn.net/zyd573803837/article/details/109576612