java m3u8格式视频文件解析(获取部分信息)

 参考:  https://github.com/qq494257084/m3u8Download

           https://blog.csdn.net/qq494257084/article/details/103551293

1.导入jar包

        <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk16 -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk16</artifactId>
            <version>1.46</version>
        </dependency>

2.工具类



import com.eebbk.notifyservice.exception.M3u8Exception;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * @Description  m3u8解析
 * @project
 * @author:hf
 * @date:
 */
@Slf4j
public class M3u8DownloadFactory {


    //重试次数
    private static int retryCount = 30;

    //链接连接超时时间(单位:毫秒)
    private static long timeoutMillisecond = 1000L;

    //密钥字节
    private static byte[] keyBytes = new byte[16];

    //自定义请求头
    private static Map<String, Object> requestHeaderMap = new HashMap<>();


    /**
     * 获取所有的ts片段下载链接
     *
     * @return 链接是否被加密,null为非加密
     */
    private static  Map getTsUrl(String url) {
        StringBuilder content = getUrlContent(url, false);
        //判断是否是m3u8链接
        if (!content.toString().contains("#EXTM3U")){
            throw new M3u8Exception(url + "不是m3u8链接!");
        }
        String[] split = content.toString().split("\\n");
        String keyUrl = "";
        boolean isKey = false;
        for (String s : split) {
            //如果含有此字段,则说明只有一层m3u8链接
            if (s.contains("#EXT-X-KEY") || s.contains("#EXTINF")) {
                isKey = true;
                keyUrl = url;
                break;
            }
        }
        if (StringUtils.isEmpty(keyUrl)){
            throw new M3u8Exception("未发现key链接!");
        }
        //获取密钥
        Map key = isKey ? getKey(keyUrl, content) : getKey(keyUrl, null);

        return key;
    }

    /**
     * @Description: 判断是否是为url链接
     * @Author: hf
     * @Time: 2021/7/13 16:44
     */
    public static boolean isUrl(String str) {
        if (StringUtils.isEmpty(str)){
            return false;
        }
        str = str.trim();
        return str.matches("^(http|https)://.+");
    }
    /**
     * @Description:
     * @Return:
     * @Author: hf
     * @Time: 2021/7/13 16:44
     */
    private static String mergeUrl(String start, String end) {
        if (end.startsWith("/")){
            end = end.replaceFirst("/", "");
        }
        int position = 0;
        String subEnd, tempEnd = end;
        while ((position = end.indexOf("/", position)) != -1) {
            subEnd = end.substring(0, position + 1);
            if (start.endsWith(subEnd)) {
                tempEnd = end.replaceFirst(subEnd, "");
                break;
            }
            ++position;
        }
        return start + tempEnd;
    }
  /**
   * @Description:
   * @param url     密钥链接,如果无密钥的m3u8,则此字段可为空
   * @param content 内容,如果有密钥,则此字段可以为空
   * @Return:
   * @Author: hf
   * @Time: 2021/7/13 16:44
   */
    private static Map getKey(String url, StringBuilder content) {
        Map<String,String> map=new HashMap<String,String>();
        StringBuilder urlContent;
        if (content == null || StringUtils.isEmpty(content.toString())){
            urlContent = getUrlContent(url, false);
        }else{
            urlContent = content;
        }
        if (!urlContent.toString().contains("#EXTM3U")){
            throw new M3u8Exception(url + "不是m3u8链接!");
        }
        String method=null;
        String keyUrl=null;
        String iv=null;
        String[] split = urlContent.toString().split("\\n");
        for (String s : split) {
            //如果含有此字段,则获取加密算法以及获取密钥的链接
            if (s.contains("EXT-X-KEY")) {
                String[] split1 = s.split(",");
                for (String s1 : split1) {
                    if (s1.contains("METHOD")) {
                        method = s1.split("=", 2)[1];
                        map.put("method",method);
                        continue;
                    }
                    if (s1.contains("URI")) {
                        keyUrl = s1.split("=", 2)[1];
                        continue;
                    }
                    if (s1.contains("IV")){
                        iv = s1.split("=", 2)[1];
                        map.put("iv",iv);
                    }
                }
                //只需要 key和vi 直接结束循环
                break;
            }
        }
        String aesKey=null;
        String relativeUrl = url.substring(0, url.lastIndexOf("/") + 1);
        if (!StringUtils.isEmpty(keyUrl)) {
            keyUrl = keyUrl.replace("\"", "");
            aesKey= getUrlContent(isUrl(keyUrl) ? keyUrl : mergeUrl(relativeUrl, keyUrl), true).toString().replaceAll("\\s+", "");
            map.put("aesKey",aesKey);
        }
        return map;
    }
  /**
   * @Description:
   * @param urls  http链接
   * @param isKey 这个url链接是否用于获取key
   * @Return:
   * @Author: hf
   * @Time: 2021/7/13 16:43
   */
    private static StringBuilder getUrlContent(String urls, boolean isKey) {
        int count = 1;
        HttpURLConnection httpURLConnection = null;
        StringBuilder content = new StringBuilder();
        while (count <= retryCount) {
            try {
                URL url = new URL(urls);
                httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setConnectTimeout((int) timeoutMillisecond);
                httpURLConnection.setReadTimeout((int) timeoutMillisecond);
                httpURLConnection.setUseCaches(false);
                httpURLConnection.setDoInput(true);
                for (Map.Entry<String, Object> entry : requestHeaderMap.entrySet()){
                    httpURLConnection.addRequestProperty(entry.getKey(), entry.getValue().toString());
                }
                String line;
                InputStream inputStream = httpURLConnection.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                //这个url链接是否用于获取key
                if (isKey) {
                    byte[] bytes = new byte[128];
                    int len;
                    len = inputStream.read(bytes);
                    if (len == 1 << 4) {
                        keyBytes = Arrays.copyOf(bytes, 16);
                        String aesKey= bytesTohex(keyBytes);
                        content.append(aesKey);
                    } else{
                        content.append(new String(Arrays.copyOf(bytes, len)));
                    }
                    return content;
                }
                while ((line = bufferedReader.readLine()) != null){
                    content.append(line).append("\n");
                }
                bufferedReader.close();
                inputStream.close();
                //log.info(content.toString());
                break;
            } catch (Exception e) {
                log.error("第" + count + "获取链接重试!\t" + urls+";"+e.getMessage());
                count++;
            } finally {
                if (httpURLConnection != null) {
                    httpURLConnection.disconnect();
                }
            }
        }
        if (count > retryCount){
            throw new M3u8Exception("连接超时!");
        }
        return content;
     }

    /**
     * @Description: 转16进制
     * @Author: hf
     * @Time: 2021/7/13 16:35
     */
    public static  String bytesTohex(byte[] bytes) {
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            boolean flag = false;
            if(b < 0){
                flag = true;
            }
            int absB = Math.abs(b);
            if(flag){
                absB = absB | 0x80;
            }
           // System.out.println(absB & 0xFF);
            String tmp = Integer.toHexString(absB & 0xFF);
            //转化的十六进制不足两位,需要补0
            if (tmp.length() == 1) {
                hex.append("0");
            }
            hex.append(tmp.toUpperCase());
            if(i!=bytes.length-1){
                hex.append(" ");
            }
        }
        return hex.toString();
    }

    public static void main(String[] args) {
        String url="xxxx.m3u8";
        System.out.println("getTsUrl:"+getTsUrl(url));
    }

    /**
     * @Description: 获取16进制随机数
     * @Author: hf
     * @Time: 2021/7/13 10:49
     */
    public static String randomHexString(int len) {
        try {
            StringBuffer result = new StringBuffer();
            for (int i = 1; i <= len; i++) {
                result.append(Integer.toHexString(new Random().nextInt(16)));
                if(i%2==0 && i!=len){
                    result.append(" ");
                }
            }
            return result.toString().toUpperCase();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }
}

3.输出加密方式,秘钥,iv

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值