/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.smil.api.util; import static org.opencastproject.util.IoSupport.withResource; import org.opencastproject.mediapackage.MediaPackageElement; import org.opencastproject.util.XmlUtil; import org.opencastproject.util.data.Either; import org.opencastproject.util.data.functions.Misc; import com.android.mms.dom.smil.parser.SmilXmlParser; import com.entwinemedia.fn.Fn; import com.entwinemedia.fn.FnX; import org.apache.commons.httpclient.util.URIUtil; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.smil.SMILDocument; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; import java.net.URI; /** * General purpose utility functions for dealing with SMIL. */ public final class SmilUtil { private static final Logger logger = LoggerFactory.getLogger(SmilUtil.class); public static final String SMIL_NODE_NAME = "smil"; public static final String SMIL_NS_URI = "http://www.w3.org/ns/SMIL"; public enum TrackType { PRESENTER, PRESENTATION } private SmilUtil() { } /** Parse a SMIL document from an input stream. */ public static final Fn<InputStream, SMILDocument> parseSmilFn = new FnX<InputStream, SMILDocument>() { @Override public SMILDocument applyX(InputStream in) throws SAXException, IOException { return new SmilXmlParser().parse(in); } }; /** * Read a SMIL document from a string. * * @throws java.io.IOException * in case of any IO error * @throws org.xml.sax.SAXException * in case of a SAX related error */ public static SMILDocument readSmil(String smil) throws IOException, SAXException { return withResource(IOUtils.toInputStream(smil, "UTF-8"), parseSmilFn); } /** * Load the SMIL document identified by <code>mpe</code>. Throws an exception if it does not exist or cannot be loaded * by any reason. * * @return the document */ public static Document loadSmilDocument(InputStream in, MediaPackageElement mpe) { try { Either<Exception, org.w3c.dom.Document> eitherDocument = XmlUtil.parseNs(new InputSource(in)); if (eitherDocument.isRight()) return eitherDocument.right().value(); throw eitherDocument.left().value(); } catch (Exception e) { logger.warn("Unable to load smil document from catalog '{}': {}", mpe, ExceptionUtils.getStackTrace(e)); return Misc.chuck(e); } } /** * Creates a skeleton SMIL document * * @return the SMIL document */ public static Document createSmil() { Document smilDocument = XmlUtil.newDocument(); smilDocument.setXmlVersion("1.1"); Element smil = smilDocument.createElementNS(SMIL_NS_URI, SMIL_NODE_NAME); smil.setAttribute("version", "3.0"); smilDocument.appendChild(smil); Node head = smilDocument.createElement("head"); smil.appendChild(head); Node body = smilDocument.createElement("body"); smil.appendChild(body); Element parallel = smilDocument.createElement("par"); parallel.setAttribute("dur", "0ms"); body.appendChild(parallel); return smilDocument; } /** * Adds a track to the SMIL document. * * @param smilDocument * the SMIL document * @param trackType * the track type * @param hasVideo * whether the track has a video stream * @param startTime * the start time * @param duration * the duration * @param uri * the track URI * @return the augmented SMIL document */ public static Document addTrack(Document smilDocument, TrackType trackType, boolean hasVideo, long startTime, long duration, URI uri) { return addTrack(smilDocument, trackType, hasVideo, startTime,duration, uri, null); } /** * Adds a track to the SMIL document. * * @param smilDocument * the SMIL document * @param trackType * the track type * @param hasVideo * whether the track has a video stream * @param startTime * the start time * @param duration * the duration * @param uri * the track URI * @param trackId * the Id of the track * @return the augmented SMIL document */ public static Document addTrack(Document smilDocument, TrackType trackType, boolean hasVideo, long startTime, long duration, URI uri, String trackId) { Element parallel = (Element) smilDocument.getElementsByTagName("par").item(0); if (parallel.getChildNodes().getLength() == 0) { Node presenterSeq = smilDocument.createElement("seq"); parallel.appendChild(presenterSeq); Node presentationSeq = smilDocument.createElement("seq"); parallel.appendChild(presentationSeq); } String trackDurationString = parallel.getAttribute("dur"); Long oldTrackDuration = Long.parseLong(trackDurationString.substring(0, trackDurationString.indexOf("ms"))); Long newTrackDuration = startTime + duration; if (newTrackDuration > oldTrackDuration) { parallel.setAttribute("dur", newTrackDuration + "ms"); } Node sequence; switch (trackType) { case PRESENTER: sequence = parallel.getChildNodes().item(0); break; case PRESENTATION: sequence = parallel.getChildNodes().item(1); break; default: throw new IllegalStateException("Unknown track type " + trackType.toString()); } Element element = smilDocument.createElement(hasVideo ? "video" : "audio"); element.setAttribute("begin", Long.toString(startTime) + "ms"); element.setAttribute("dur", Long.toString(duration) + "ms"); element.setAttribute("src", URIUtil.getPath(uri.toString())); if (trackId != null) { element.setAttribute("xml:id", trackId); } sequence.appendChild(element); return smilDocument; } }