1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.smil.api.util;
22
23 import org.opencastproject.mediapackage.Catalog;
24 import org.opencastproject.mediapackage.MediaPackage;
25 import org.opencastproject.mediapackage.MediaPackageElement;
26 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
27 import org.opencastproject.mediapackage.selector.AbstractMediaPackageElementSelector;
28 import org.opencastproject.mediapackage.selector.CatalogSelector;
29 import org.opencastproject.util.NotFoundException;
30 import org.opencastproject.util.XmlUtil;
31 import org.opencastproject.util.data.Either;
32 import org.opencastproject.util.data.functions.Misc;
33 import org.opencastproject.workspace.api.Workspace;
34
35 import com.android.mms.dom.smil.parser.SmilXmlParser;
36
37 import org.apache.commons.httpclient.util.URIUtil;
38 import org.apache.commons.io.IOUtils;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.smil.SMILDocument;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.SAXException;
47
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.net.URI;
53 import java.util.Collection;
54
55
56
57
58 public final class SmilUtil {
59
60 private static final Logger logger = LoggerFactory.getLogger(SmilUtil.class);
61
62 public static final String SMIL_NODE_NAME = "smil";
63 public static final String SMIL_NS_URI = "http://www.w3.org/ns/SMIL";
64
65 public enum TrackType {
66 PRESENTER, PRESENTATION
67 }
68
69 private SmilUtil() {
70 }
71
72
73
74
75
76
77
78 public static Document loadSmilDocument(InputStream in, MediaPackageElement mpe) {
79 try {
80 Either<Exception, org.w3c.dom.Document> eitherDocument = XmlUtil.parseNs(new InputSource(in));
81 if (eitherDocument.isRight()) {
82 return eitherDocument.right().value();
83 }
84
85 throw eitherDocument.left().value();
86 } catch (Exception e) {
87 logger.warn("Unable to load smil document from catalog '{}'", mpe, e);
88 return Misc.chuck(e);
89 }
90 }
91
92
93
94
95
96
97 public static Document createSmil() {
98 Document smilDocument = XmlUtil.newDocument();
99 smilDocument.setXmlVersion("1.1");
100 Element smil = smilDocument.createElementNS(SMIL_NS_URI, SMIL_NODE_NAME);
101 smil.setAttribute("version", "3.0");
102 smilDocument.appendChild(smil);
103 Node head = smilDocument.createElement("head");
104 smil.appendChild(head);
105 Node body = smilDocument.createElement("body");
106 smil.appendChild(body);
107 Element parallel = smilDocument.createElement("par");
108 parallel.setAttribute("dur", "0ms");
109 body.appendChild(parallel);
110 return smilDocument;
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public static Document addTrack(Document smilDocument, TrackType trackType, boolean hasVideo, long startTime,
133 long duration, URI uri, String trackId) {
134 Element parallel = (Element) smilDocument.getElementsByTagName("par").item(0);
135 if (parallel.getChildNodes().getLength() == 0) {
136 Node presenterSeq = smilDocument.createElement("seq");
137 parallel.appendChild(presenterSeq);
138 Node presentationSeq = smilDocument.createElement("seq");
139 parallel.appendChild(presentationSeq);
140 }
141
142 String trackDurationString = parallel.getAttribute("dur");
143 Long oldTrackDuration = Long.parseLong(trackDurationString.substring(0, trackDurationString.indexOf("ms")));
144 Long newTrackDuration = startTime + duration;
145 if (newTrackDuration > oldTrackDuration) {
146 parallel.setAttribute("dur", newTrackDuration + "ms");
147 }
148
149 Node sequence;
150 switch (trackType) {
151 case PRESENTER:
152 sequence = parallel.getChildNodes().item(0);
153 break;
154 case PRESENTATION:
155 sequence = parallel.getChildNodes().item(1);
156 break;
157 default:
158 throw new IllegalStateException("Unknown track type " + trackType.toString());
159 }
160
161 Element element = smilDocument.createElement(hasVideo ? "video" : "audio");
162 element.setAttribute("begin", Long.toString(startTime) + "ms");
163 element.setAttribute("dur", Long.toString(duration) + "ms");
164 element.setAttribute("src", URIUtil.getPath(uri.toString()));
165 if (trackId != null) {
166 element.setAttribute("xml:id", trackId);
167 }
168 sequence.appendChild(element);
169 return smilDocument;
170 }
171
172
173 public static SMILDocument getSmilDocumentFromMediaPackage(MediaPackage mp, MediaPackageElementFlavor smilFlavor,
174 Workspace workspace)
175 throws IOException, SAXException, NotFoundException {
176 final AbstractMediaPackageElementSelector<Catalog> smilSelector = new CatalogSelector();
177 smilSelector.addFlavor(smilFlavor);
178 final Collection<Catalog> smilCatalog = smilSelector.select(mp, false);
179 if (smilCatalog.size() == 1) {
180 return getSmilDocument(smilCatalog.iterator().next(), workspace);
181 } else {
182 logger.error("More or less than one smil catalog found: {}", smilCatalog);
183 throw new IllegalStateException("More or less than one smil catalog found!");
184 }
185 }
186
187
188 private static SMILDocument getSmilDocument(final Catalog smilCatalog, Workspace workspace) throws NotFoundException,
189 IOException, SAXException {
190 FileInputStream in = null;
191 try {
192 File smilXmlFile = workspace.get(smilCatalog.getURI());
193 SmilXmlParser smilParser = new SmilXmlParser();
194 in = new FileInputStream(smilXmlFile);
195 return smilParser.parse(in);
196 } finally {
197 IOUtils.closeQuietly(in);
198 }
199 }
200
201 }