1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.media.video;
20
21 import java.io.IOException;
22 import java.net.URISyntaxException;
23 import java.nio.file.Files;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Optional;
28 import java.util.function.Supplier;
29
30 import org.apache.logging.log4j.LogManager;
31 import org.mycore.common.MCRSessionMgr;
32 import org.mycore.common.config.MCRConfiguration2;
33 import org.mycore.datamodel.metadata.MCRObjectID;
34 import org.mycore.datamodel.niofs.MCRAbstractFileStore;
35 import org.mycore.datamodel.niofs.MCRPath;
36 import org.mycore.frontend.filter.MCRSecureTokenV2FilterConfig;
37 import org.mycore.frontend.support.MCRSecureTokenV2;
38 import org.mycore.media.MCRMediaSourceType;
39
40 public class MCRMediaSourceProvider {
41 private static Optional<String> wowzaBaseURL = MCRConfiguration2.getString("MCR.Media.Wowza.BaseURL");
42
43 private static Optional<String> wowzaRTMPBaseURL = MCRConfiguration2
44 .getString("MCR.Media.Wowza.RTMPBaseURL");
45
46 private static String wowzaHashParameter = MCRConfiguration2.getString("MCR.Media.Wowza.HashParameter")
47 .orElse("wowzatokenhash");
48
49 static {
50 MCRConfiguration2.addPropertyChangeEventLister(p -> p.startsWith("MCR.Media.Wowza"),
51 MCRMediaSourceProvider::updateWowzaSettings);
52 }
53
54 private Optional<MCRSecureTokenV2> wowzaToken;
55
56 private ArrayList<MCRMediaSource> sources;
57
58 public MCRMediaSourceProvider(String derivateId, String path, Optional<String> userAgent,
59 Supplier<String[]> parameterSupplier) throws IOException, URISyntaxException {
60 try {
61 wowzaToken = wowzaBaseURL.map(
62 (w) -> new MCRSecureTokenV2(
63 MCRConfiguration2.getStringOrThrow("MCR.Media.Wowza.ContentPathPrefix")
64 + getContentPath(derivateId, path),
65 MCRSessionMgr.getCurrentSession().getCurrentIP(),
66 MCRConfiguration2.getStringOrThrow("MCR.Media.Wowza.SharedSecred"),
67 parameterSupplier.get()));
68 } catch (RuntimeException e) {
69 Throwable cause = e.getCause();
70 if (cause != null) {
71 if (cause instanceof IOException) {
72 throw (IOException) cause;
73 }
74 if (cause instanceof URISyntaxException) {
75 throw (URISyntaxException) cause;
76 }
77 }
78 throw e;
79 }
80 ArrayList<MCRMediaSource> mediaSources = new ArrayList<>(4);
81 getDashStream()
82 .map(s -> new MCRMediaSource(s, MCRMediaSourceType.dash_stream))
83 .ifPresent(mediaSources::add);
84 userAgent.filter(MCRMediaSourceProvider::mayContainHLSStream).ifPresent(f -> getHLSStream()
85 .map(s -> new MCRMediaSource(s, MCRMediaSourceType.hls_stream))
86 .ifPresent(mediaSources::add));
87 getRTMPStream()
88 .map(s -> new MCRMediaSource(s, MCRMediaSourceType.rtmp_stream))
89 .ifPresent(mediaSources::add);
90 mediaSources.add(
91 new MCRMediaSource(getPseudoStream(MCRObjectID.getInstance(derivateId), path), MCRMediaSourceType.mp4));
92 this.sources = mediaSources;
93 }
94
95 public static void updateWowzaSettings(String propertyName, Optional<String> oldValue,
96 Optional<String> newValue) {
97 switch (propertyName) {
98 case "MCR.Media.Wowza.BaseURL":
99 wowzaBaseURL = newValue;
100 break;
101 case "MCR.Media.Wowza.RTMPBaseURL":
102 wowzaRTMPBaseURL = newValue;
103 break;
104 case "MCR.Media.Wowza.HashParameter":
105 wowzaHashParameter = newValue.orElse("wowzatokenhash");
106 break;
107 default:
108 break;
109 }
110 }
111
112 public List<MCRMediaSource> getSources() {
113 return Collections.unmodifiableList(sources);
114 }
115
116 private Optional<String> toURL(MCRSecureTokenV2 token, Optional<String> baseURL, String suffix,
117 String hashParameterName) {
118 return baseURL.map(b -> {
119 try {
120 return token.toURI(b, suffix, hashParameterName).toString();
121 } catch (URISyntaxException e) {
122 throw new RuntimeException(e);
123 }
124 });
125 }
126
127 private Optional<String> getDashStream() {
128 return wowzaToken.flatMap(w -> toURL(w, wowzaBaseURL, "/manifest.mpd", wowzaHashParameter));
129 }
130
131 private Optional<String> getHLSStream() {
132 return wowzaToken.flatMap(w -> toURL(w, wowzaBaseURL, "/playlist.m3u8", wowzaHashParameter));
133 }
134
135 private Optional<String> getRTMPStream() {
136 return wowzaToken.flatMap(w -> toURL(w, wowzaRTMPBaseURL, "", wowzaHashParameter));
137 }
138
139 private String getPseudoStream(MCRObjectID derivate, String path) {
140 return MCRSecureTokenV2FilterConfig.getFileNodeServletSecured(derivate, path);
141 }
142
143 private static String getContentPath(String derivateId, String path) {
144 try {
145 MCRPath mcrPath = MCRPath.getPath(derivateId, path);
146 MCRAbstractFileStore fileStore = (MCRAbstractFileStore) Files.getFileStore(mcrPath);
147 java.nio.file.Path absolutePath = fileStore.getPhysicalPath(mcrPath);
148 java.nio.file.Path relativePath = fileStore.getBaseDirectory().relativize(absolutePath);
149 LogManager.getLogger().info("{} -> {} -> {}", mcrPath, absolutePath, relativePath);
150 return relativePath.toString();
151 } catch (IOException e) {
152 throw new RuntimeException(e);
153 }
154 }
155
156 private static boolean mayContainHLSStream(String userAgent) {
157
158 return !(userAgent.contains("Android") && userAgent.contains("Gecko"));
159 }
160 }