1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.opencastproject.mediapackage.track;
24
25 import org.opencastproject.mediapackage.AbstractMediaPackageElement;
26 import org.opencastproject.mediapackage.AudioStream;
27 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
28 import org.opencastproject.mediapackage.MediaPackageException;
29 import org.opencastproject.mediapackage.MediaPackageSerializer;
30 import org.opencastproject.mediapackage.Stream;
31 import org.opencastproject.mediapackage.SubtitleStream;
32 import org.opencastproject.mediapackage.Track;
33 import org.opencastproject.mediapackage.VideoStream;
34 import org.opencastproject.util.Checksum;
35 import org.opencastproject.util.MimeType;
36 import org.opencastproject.util.MimeTypes;
37 import org.opencastproject.util.UnknownFileTypeException;
38
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Node;
41
42 import java.net.URI;
43 import java.util.ArrayList;
44 import java.util.List;
45
46 import javax.xml.bind.annotation.XmlAccessType;
47 import javax.xml.bind.annotation.XmlAccessorType;
48 import javax.xml.bind.annotation.XmlAttribute;
49 import javax.xml.bind.annotation.XmlElement;
50 import javax.xml.bind.annotation.XmlRootElement;
51 import javax.xml.bind.annotation.XmlType;
52 import javax.xml.bind.annotation.adapters.XmlAdapter;
53
54
55
56
57
58 @XmlAccessorType(XmlAccessType.NONE)
59 @XmlType(name = "track", namespace = "http://mediapackage.opencastproject.org")
60 @XmlRootElement(name = "track", namespace = "http://mediapackage.opencastproject.org")
61 public class TrackImpl extends AbstractMediaPackageElement implements Track {
62
63
64 private static final long serialVersionUID = -1092781733885994038L;
65
66 public enum StreamingProtocol {
67 DOWNLOAD,HLS,DASH,HDS,SMOOTH,MMS,RTP,RTSP,RTMP,RTMPE,PNM,PNA,ICY,BITTORENTLIVE,FILE,UNKNOWN
68 }
69
70
71 @XmlElement(name = "duration")
72 protected Long duration = null;
73
74 @XmlElement(name = "audio")
75 protected List<AudioStream> audio = new ArrayList<>();
76
77 @XmlElement(name = "video")
78 protected List<VideoStream> video = new ArrayList<>();
79
80 @XmlElement(name = "subtitle")
81 protected List<SubtitleStream> subtitle = new ArrayList<>();
82
83 @XmlAttribute(name = "transport")
84 protected StreamingProtocol transport = null;
85
86 @XmlElement(name = "live")
87 protected boolean live;
88
89 @XmlElement(name = "master", required = false)
90 protected Boolean master = null;
91
92 @XmlElement(name = "logicalname", required = false)
93 protected String logicalname = null;
94
95
96 public TrackImpl() {
97 this.elementType = Track.TYPE;
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112 TrackImpl(MediaPackageElementFlavor flavor, MimeType mimeType, URI uri, long size, Checksum checksum) {
113 super(Type.Track, flavor, uri, size, checksum, mimeType);
114 }
115
116
117
118
119
120
121
122
123
124 TrackImpl(MediaPackageElementFlavor flavor, URI uri) {
125 super(Type.Track, flavor, uri);
126 if (uri != null) {
127 try {
128 this.setMimeType(MimeTypes.fromURI(uri));
129 } catch (UnknownFileTypeException e) { }
130 }
131 }
132
133
134
135
136
137
138
139
140 public static TrackImpl fromURI(URI uri) {
141 return new TrackImpl(null, uri);
142 }
143
144
145
146
147
148
149
150 public void setDuration(Long duration) {
151 this.duration = duration;
152 }
153
154
155
156
157 @Override
158 public Long getDuration() {
159 return duration;
160 }
161
162 @Override
163 public Stream[] getStreams() {
164 List<Stream> streams = new ArrayList<>(audio.size() + video.size() + subtitle.size());
165 streams.addAll(audio);
166 streams.addAll(video);
167 streams.addAll(subtitle);
168 return streams.toArray(new Stream[0]);
169 }
170
171
172
173
174 public void addStream(AbstractStreamImpl stream) {
175 if (stream instanceof AudioStreamImpl) {
176 audio.add((AudioStreamImpl) stream);
177 } else if (stream instanceof VideoStreamImpl) {
178 video.add((VideoStreamImpl) stream);
179 } else if (stream instanceof SubtitleStreamImpl) {
180 subtitle.add((SubtitleStreamImpl) stream);
181 } else {
182 throw new IllegalArgumentException("stream must be either audio or video");
183 }
184 }
185
186
187
188
189
190
191 @Override
192 public boolean hasAudio() {
193 return audio != null && audio.size() > 0;
194 }
195
196
197
198
199
200
201 @Override
202 public boolean hasVideo() {
203 return video != null && video.size() > 0;
204 }
205
206
207
208
209
210
211 @Override
212 public boolean hasSubtitle() {
213 return subtitle != null && subtitle.size() > 0;
214 }
215
216 public List<AudioStream> getAudio() {
217 return audio;
218 }
219
220 public void setAudio(List<AudioStream> audio) {
221 this.audio = audio;
222 }
223
224 public List<VideoStream> getVideo() {
225 return video;
226 }
227
228 public void setVideo(List<VideoStream> video) {
229 this.video = video;
230 }
231
232 public List<SubtitleStream> getSubtitle() {
233 return subtitle;
234 }
235
236 public void setSubtitle(List<SubtitleStream> subtitle) {
237 this.subtitle = subtitle;
238 }
239
240 public void setLive(boolean isLive) {
241 this.live = isLive;
242 }
243
244
245
246
247 @Override
248 public boolean isLive() {
249 return this.live;
250 }
251
252
253
254
255 @Override
256 public Boolean isMaster() {
257 return hasMaster() && master;
258 }
259
260 @Override
261 public void setMaster(Boolean master) {
262 this.master = master;
263 }
264
265 @Override
266 public boolean hasMaster() {
267 return master != null;
268 }
269
270
271
272
273
274 @Override
275 public Node toManifest(Document document, MediaPackageSerializer serializer) throws MediaPackageException {
276 Node node = super.toManifest(document, serializer);
277
278
279 if (duration != null && duration >= 0) {
280 Node durationNode = document.createElement("duration");
281 durationNode.appendChild(document.createTextNode(Long.toString(duration)));
282 node.appendChild(durationNode);
283 }
284
285 Node liveNode = document.createElement("live");
286 liveNode.appendChild(document.createTextNode(Boolean.toString(live)));
287 node.appendChild(liveNode);
288
289 if (hasMaster()) {
290 Node masterNode = document.createElement("master");
291 masterNode.appendChild(document.createTextNode(Boolean.toString(isMaster())));
292 node.appendChild(masterNode);
293 }
294
295 if (logicalname != null && !logicalname.isEmpty()) {
296 Node nameNode = document.createElement("logicalname");
297 liveNode.appendChild(document.createTextNode(logicalname));
298 node.appendChild(nameNode);
299 }
300
301 for (Stream s : audio)
302 node.appendChild(s.toManifest(document, serializer));
303 for (Stream s : video)
304 node.appendChild(s.toManifest(document, serializer));
305 for (Stream s : subtitle)
306 node.appendChild(s.toManifest(document, serializer));
307 return node;
308 }
309
310
311
312
313
314
315 @Override
316 public String getDescription() {
317 StringBuffer buf = new StringBuffer("");
318
319
320
321
322
323
324 return buf.toString().toLowerCase();
325 }
326
327 public void setTransport(StreamingProtocol transport) {
328 this.transport = transport;
329 }
330
331 public StreamingProtocol getTransport() {
332 if (transport == null) return autodetectTransport(getURI());
333 return transport;
334 }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354 public static class Adapter extends XmlAdapter<TrackImpl, Track> {
355 @Override
356 public TrackImpl marshal(Track mp) throws Exception {
357 return (TrackImpl) mp;
358 }
359
360 @Override
361 public Track unmarshal(TrackImpl mp) throws Exception {
362 return mp;
363 }
364 }
365
366 private StreamingProtocol autodetectTransport(URI uri) {
367 if (uri == null || uri.getScheme() == null) return null;
368 if (uri.getScheme().toLowerCase().startsWith("http")) {
369 if (uri.getFragment() == null) return StreamingProtocol.DOWNLOAD;
370 else if (uri.getFragment().toLowerCase().endsWith(".m3u8")) return StreamingProtocol.HLS;
371 else if (uri.getFragment().toLowerCase().endsWith(".mpd")) return StreamingProtocol.DASH;
372 else if (uri.getFragment().toLowerCase().endsWith(".f4m")) return StreamingProtocol.HDS;
373 else setTransport(StreamingProtocol.DOWNLOAD);
374 }
375 else if (uri.getScheme().toLowerCase().startsWith("rtmp")) return StreamingProtocol.RTMP;
376 else if (uri.getScheme().toLowerCase().startsWith("rtmpe")) return StreamingProtocol.RTMPE;
377 else if (uri.getScheme().toLowerCase().startsWith("file")) return StreamingProtocol.FILE;
378 else if (uri.getScheme().toLowerCase().startsWith("rtp")) return StreamingProtocol.RTP;
379 else if (uri.getScheme().toLowerCase().startsWith("rtsp")) return StreamingProtocol.RTSP;
380 return StreamingProtocol.UNKNOWN;
381 }
382
383 @Override
384 public String getLogicalName() {
385 if (logicalname == null)
386 return uri.getPath();
387 return logicalname;
388 }
389
390 @Override
391 public void setLogicalName(String name) {
392 logicalname = name;
393 }
394
395 }