内容简介: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解析库主要有以下两个:
然而,这两个库兼容性太差,也可能是因为这个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)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Caching
Duane Wessels / O'Reilly Media, Inc. / 2001-6 / 39.95美元
On the World Wide Web, speed and efficiency are vital. Users have little patience for slow web pages, while network administrators want to make the most of their available bandwidth. A properly design......一起来看看 《Web Caching》 这本书的介绍吧!