HLS大杂烩

栏目: Java · 发布时间: 7年前

内容简介:HLS(HTTP Live Streaming)是苹果公司主导的基于HTTP的流媒体协议。它将视频切分为数秒一个的TS文件,客户端需要轮询视频文件的索引地址,将一小段一小段的视频组装起来HLS地址:某一时段的响应:

HLS(HTTP Live Streaming)是苹果公司主导的基于HTTP的流媒体协议。它将视频切分为数秒一个的TS文件,客户端需要轮询视频文件的索引地址,将一小段一小段的视频组装起来

HLS索引文件示例

HLS地址: http://hls.open.ys7.com/openlive/f01018a141094b7fa138b9d0b856507b.hd.m3u8

某一时段的响应:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:15
#EXT-TS-OFFSET-BEGIN:45
#EXTINF:3.997,
http://hzhls01.ys7.com:7888/openlivedata/203751922_1_1/8883db61d09440008b06f7062cdadb31-15.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
#EXT-TS-OFFSET-BEGIN:48
#EXTINF:3.997,
http://hzhls01.ys7.com:7888/openlivedata/203751922_1_1/8883db61d09440008b06f7062cdadb31-16.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
#EXT-TS-OFFSET-BEGIN:51
#EXTINF:3.998,
http://hzhls01.ys7.com:7888/openlivedata/203751922_1_1/8883db61d09440008b06f7062cdadb31-17.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a

部分字段解释:

#EXT-X-TARGETDURATION:4
#EXT-TS-OFFSET-BEGIN:45
#EXTINF:3.997
http://...

注意,萤石云的HLS协议不够规范,比如视频时长用浮点数,两个视频片段的索引间隔跟单个视频时长不一致,不知是何用意。真实时长以下载到视频文件的时长为准。

Java 解析HLS

Java的HLS解析库主要有以下两个:

  1. hlsparserj
  2. open-m3u8

然而,这两个库兼容性太差,也可能是因为这个HLS报文不够标准吧,总之两个库没有一个库能解析出来。所以只能手动解析了。

示例代码

package bj;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.Context;
import io.vavr.control.Try;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by BaiJiFeiLong@gmail.com at 2018/12/1 上午10:17
 */
@Slf4j
public class HlsTest {

    private RestTemplate restTemplate = new RestTemplate();

    /**
     * 从HLS链接下载一个20秒的视频片段
     *
     * @throws IOException .
     */
    @Test
    public void testAlpha() throws IOException {
        // Logback配置日志级别为INFO
        ((Logger) (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME))).setLevel(Level.INFO);
        // 自定义控制台日志格式
        // noinspection unchecked
        ((ConsoleAppender) ((Logger) (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME))).getAppender("console")).setEncoder(new PatternLayoutEncoder() {
            {
                setContext((Context) LoggerFactory.getILoggerFactory());
                setPattern("[%date] %highlight([%level]) [%logger{10} %file:%line] [%thread] %msg%n");
                start();
            }
        });

        // 视频分片列表
        List<Triple<Integer, Integer, String>> videos = new ArrayList<>();
        // HLS索引URL
        String playlistUrl = "http://hls.open.ys7.com/openlive/f01018a141094b7fa138b9d0b856507b.hd.m3u8";

        // 当前下载的秒数
        int seconds = 0;
        // 总共下载的秒数
        int needSeconds = 20;

        loop:
        while (true) {
            log.info("Requesting playlist: {}", playlistUrl);
            // 下载索引文件
            String text = restTemplate.getForObject(playlistUrl, String.class);
            assert text != null;

            /// 获取视频片段时长
            Matcher targetDurationMatcher = Pattern.compile("#EXT-X-TARGETDURATION:(\\d+)", Pattern.DOTALL).matcher(text);
            assert targetDurationMatcher.find();
            int targetDuration = Integer.parseInt(targetDurationMatcher.group(1));

            /// 获取视频片段
            Pattern pattern = Pattern.compile("#EXT-TS-OFFSET-BEGIN:(?<offset>\\d+)[\r\n]+#EXTINF:(?<duration>[\\d.]+).+?(?<url>http://\\S+)", Pattern.DOTALL);
            Matcher matcher = pattern.matcher(text);

            while (matcher.find()) {
                // 视频片段Offset
                int offset = Integer.parseInt(matcher.group("offset"));
                // 视频片段Duration
                int duration = Math.round(Float.parseFloat(matcher.group("duration")));
                // 视频片段URL
                String url = matcher.group("url");

                Triple<Integer, Integer, String> triple = Triple.of(offset, duration, url);

                // 不下载重复文件
                if (!videos.contains(triple)) {
                    log.info("Downloading: {}", url);

                    // 下载视频片段
                    FileUtils.copyURLToFile(new URL(url), new File((seconds / targetDuration) + ".ts"));

                    videos.add(triple);

                    // 更新下载进度
                    seconds += targetDuration;

                    // 下载完毕,退出循环
                    if (seconds >= needSeconds) {
                        break loop;
                    }
                } else {
                    log.info("Ignored: {}", url);
                }
            }

            // 等待一个视频片段的时长,等待索引文件更新
            log.info("Sleeping...");
            Try.run(() -> Thread.sleep(targetDuration * 1000)).get();
        }
        videos.forEach(System.out::println);
    }
}

控制台输出

[2018-12-04 16:56:48,894] [INFO] [bj.HlsTest HlsTest.java:58] [main] Requesting playlist: http://hls.open.ys7.com/openlive/f01018a141094b7fa138b9d0b856507b.hd.m3u8
[2018-12-04 16:56:49,387] [INFO] [bj.HlsTest HlsTest.java:84] [main] Downloading: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1953.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
[2018-12-04 16:56:49,821] [INFO] [bj.HlsTest HlsTest.java:84] [main] Downloading: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1954.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
[2018-12-04 16:56:50,249] [INFO] [bj.HlsTest HlsTest.java:84] [main] Downloading: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1955.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
[2018-12-04 16:56:50,451] [INFO] [bj.HlsTest HlsTest.java:104] [main] Sleeping...
[2018-12-04 16:56:54,524] [INFO] [bj.HlsTest HlsTest.java:58] [main] Requesting playlist: http://hls.open.ys7.com/openlive/f01018a141094b7fa138b9d0b856507b.hd.m3u8
[2018-12-04 16:56:54,631] [INFO] [bj.HlsTest HlsTest.java:99] [main] Ignored: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1955.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
[2018-12-04 16:56:54,632] [INFO] [bj.HlsTest HlsTest.java:84] [main] Downloading: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1956.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
[2018-12-04 16:56:54,978] [INFO] [bj.HlsTest HlsTest.java:84] [main] Downloading: http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1957.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a
(5859,4,http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1953.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a)
(5862,4,http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1954.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a)
(5865,4,http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1955.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a)
(5868,4,http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1956.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a)
(5871,4,http://hzhls05.ys7.com:7894/openlivedata/203751922_1_1/dc6267715c6243d4a224da0903be9408-1957.ts?Usr=c1cbc1d4e86d49a0981f54beea95280a)

文章首发: https://baijifeilong.github.io


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Art of Computer Programming, Volume 3

The Art of Computer Programming, Volume 3

Donald E. Knuth / Addison-Wesley Professional / 1998-05-04 / USD 74.99

Finally, after a wait of more than thirty-five years, the first part of Volume 4 is at last ready for publication. Check out the boxed set that brings together Volumes 1 - 4A in one elegant case, and ......一起来看看 《The Art of Computer Programming, Volume 3》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具