我试图开发一个简单的java web应用程序与spring mvc访问外部HLS服务器,像代理和流不止一个直播视频。外部服务器(rtsp-simple-server,这里的github页面为那些感兴趣的人:https://github.com/aler9/mediamtx)只启用了HLS协议,每个源流都可以从url访问:https://mediaserver.url:port/stream1, https://mediaserver.url:port/stream2等…流受HTTP基本身份验证保护。我的web应用程序,部署在我的本地机器需要访问流,从后端执行身份验证,并显示在网页中检索的视频。对于流媒体部分,在前端,我使用流媒体客户端hls.js。我的网页是这样的:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#video {
width: 50%;
height: 50%;
background: black;
}
</style>
</head>
<body>
<video id="video" muted controls autoplay playsinline></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.2.9"></script>
<script>
const create = () => {
const video = document.getElementById('video');
var videoSrc = 'https://mediaserver.url:port/stream1/stream.m3u8';
// always prefer hls.js over native HLS.
// this is because some Android versions support native HLS
// but don't support fMP4s.
if (Hls.isSupported()) {
const hls = new Hls();
hls.on(Hls.Events.ERROR, (evt, data) => {
if (data.fatal) {
hls.destroy();
setTimeout(create, 2000);
}
});
hls.loadSource(videoSrc);
hls.attachMedia(video);
video.play();
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// since it's not possible to detect timeout errors in iOS,
// wait for the playlist to be available before starting the stream
fetch(videoSrc)
.then(() => {
video.src = videoSrc;
video.play();
});
}
};
window.addEventListener('DOMContentLoaded', create);
</script>
</body>
</html>
是否有一种方法在Spring注入基本的身份验证头从后端,使hls客户端只需要调用流,并在页面中显示它,使登录弹出窗口不显示,我也不需要写凭证内的流url在网页?
我尝试使用webfilter,我也尝试使用spring对象RestTemplate。最后一个,我设法检索m3u8清单,但当我用它来检索段,浏览器要求我将它们保存在本地。
提前感谢。
这里是我从服务器的视频标签 中获取的HTML代码<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#video {
width: 100%;
height: 100%;
background: black;
}
</style>
</head>
<body>
<video id="video" muted controls autoplay playsinline></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.2.9"></script>
<script>
const create = () => {
const video = document.getElementById('video');
// always prefer hls.js over native HLS.
// this is because some Android versions support native HLS
// but don't support fMP4s.
if (Hls.isSupported()) {
const hls = new Hls({
maxLiveSyncPlaybackRate: 1.5,
});
hls.on(Hls.Events.ERROR, (evt, data) => {
if (data.fatal) {
hls.destroy();
setTimeout(create, 2000);
}
});
hls.loadSource('index.m3u8');
hls.attachMedia(video);
video.play();
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// since it's not possible to detect timeout errors in iOS,
// wait for the playlist to be available before starting the stream
fetch('stream.m3u8')
.then(() => {
video.src = 'index.m3u8';
video.play();
});
}
};
window.addEventListener('DOMContentLoaded', create);
</script>
</body>
</html>
-----------------------<更新strong>--------------------------更新strong>
我用弹簧找到了解决方案。这里是我编写的将请求映射到mediasserver的控制器。
@Controller
@EnableAsync
public class RTSPSSController {
private static Logger logger = LogManager.getLogger(RTSPSSController.class);
// private static String LOCALHOST_PORT = "http://localhost:8080";
private static String HOST = "https://media.server.org/";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "cameras")
public String cameras() {
return "cameras";
}
@RequestMapping(value = "cameras/{camera}")
public String camera(@PathVariable("camera") String camera) {
return "cameras/" + camera;
}
@RequestMapping(value = "streams/{camera}")
@ResponseBody
public StreamingResponseBody getStream(@PathVariable("camera") String camera)
throws IOException {
try {
return stream (camera, "");
} catch (Exception e) {
System.err.println("Error " + e.getMessage());
// return null;
return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST).getBody();
}
}
@RequestMapping(value = "streams/{camera}/{media:.+}")
@ResponseBody
public StreamingResponseBody stream(@PathVariable("camera") String camera, @PathVariable("media") String media)
throws IOException {
try {
// Retrieve restTemplate and inject the credentials for basic auth
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null) {
interceptors = Collections.emptyList();
}
interceptors = new ArrayList<>(interceptors);
interceptors.removeIf(BasicAuthorizationInterceptor.class::isInstance);
interceptors.add(new BasicAuthorizationInterceptor("user", "password"));
restTemplate.setInterceptors(interceptors);
// restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("user",
// "password"));
ResponseEntity<Resource> responseEntity = restTemplate.exchange(
HOST+ camera + "/" + media, HttpMethod.GET, null, Resource.class);
InputStream st = responseEntity.getBody().getInputStream();
// System.out.println(st.
return (os) -> {
readAndWrite(st, os);
};
} catch (Exception e) {
System.err.println("Error " + e.getMessage());
// return null;
return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST).getBody();
}
}
private void readAndWrite(final InputStream is, OutputStream os) throws IOException {
byte[] data = new byte[1024];
int read = 0;
while ((read = is.read(data)) > 0) {
os.write(data, 0, read);
}
os.flush();
}
}
现在我可以简单地使用iframe标签从媒体服务器提供的摄像机中嵌入流,如下面的例子所示:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Cameras</title>
</head>
<body>
<iframe src="./streams/camera1/"></iframe>
<iframe src="./streams/camera2/"></iframe>
<iframe src="..."></iframe>
</body>
</html>
基本权限就是简单地在http请求中添加如下形式的标头
将此头信息添加到获取中很简单。
fetch(videoSrc,
{
headers: {
"Authorization": "Basic " + Base64encoded(username + ‘:’ + password)
})
.then(() => {
video.src = videoSrc;
video.play();
});
然而,将它添加到hls.js调用中看起来更困难。使用认证基本头,而不是id和令牌。
将base64编码(用户名+ ':' +密码)通过您在springboot中使用的Java页面渲染器(Thymeleaf, JSP, outputStream)放入页面。
向html5视频组件添加验证头曾经是可能的,但它已被阻止,因为它被视为盗版视频的一种方式。这个链接有一个工作,但它并不简单。