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 }
304 for (Stream s : video) {
305 node.appendChild(s.toManifest(document, serializer));
306 }
307 for (Stream s : subtitle) {
308 node.appendChild(s.toManifest(document, serializer));
309 }
310 return node;
311 }
312
313
314
315
316
317
318 @Override
319 public String getDescription() {
320 StringBuffer buf = new StringBuffer("");
321
322
323
324
325
326
327 return buf.toString().toLowerCase();
328 }
329
330 public void setTransport(StreamingProtocol transport) {
331 this.transport = transport;
332 }
333
334 public StreamingProtocol getTransport() {
335 if (transport == null) {
336 return autodetectTransport(getURI());
337 }
338 return transport;
339 }
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359 public static class Adapter extends XmlAdapter<TrackImpl, Track> {
360 @Override
361 public TrackImpl marshal(Track mp) throws Exception {
362 return (TrackImpl) mp;
363 }
364
365 @Override
366 public Track unmarshal(TrackImpl mp) throws Exception {
367 return mp;
368 }
369 }
370
371 private StreamingProtocol autodetectTransport(URI uri) {
372 if (uri == null || uri.getScheme() == null) {
373 return null;
374 }
375 if (uri.getScheme().toLowerCase().startsWith("http")) {
376 if (uri.getFragment() == null) {
377 return StreamingProtocol.DOWNLOAD;
378 } else if (uri.getFragment().toLowerCase().endsWith(".m3u8")) {
379 return StreamingProtocol.HLS;
380 } else if (uri.getFragment().toLowerCase().endsWith(".mpd")) {
381 return StreamingProtocol.DASH;
382 } else if (uri.getFragment().toLowerCase().endsWith(".f4m")) {
383 return StreamingProtocol.HDS;
384 } else {
385 setTransport(StreamingProtocol.DOWNLOAD);
386 }
387 } else if (uri.getScheme().toLowerCase().startsWith("rtmp")) {
388 return StreamingProtocol.RTMP;
389 } else if (uri.getScheme().toLowerCase().startsWith("rtmpe")) {
390 return StreamingProtocol.RTMPE;
391 } else if (uri.getScheme().toLowerCase().startsWith("file")) {
392 return StreamingProtocol.FILE;
393 } else if (uri.getScheme().toLowerCase().startsWith("rtp")) {
394 return StreamingProtocol.RTP;
395 } else if (uri.getScheme().toLowerCase().startsWith("rtsp")) {
396 return StreamingProtocol.RTSP;
397 }
398 return StreamingProtocol.UNKNOWN;
399 }
400
401 @Override
402 public String getLogicalName() {
403 if (logicalname == null) {
404 return uri.getPath();
405 }
406 return logicalname;
407 }
408
409 @Override
410 public void setLogicalName(String name) {
411 logicalname = name;
412 }
413
414 }