com.googlecode.mp4parser.authoring.tracks.ttml.TtmlHelpers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of isoparser Show documentation
Show all versions of isoparser Show documentation
A generic parser and writer for all ISO 14496 based files (MP4, Quicktime, DCF, PDCF, ...)
The newest version!
package com.googlecode.mp4parser.authoring.tracks.ttml;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TtmlHelpers {
static byte[] namespacesStyleSheet1 = ("\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" ").getBytes();
public static void main(String[] args) throws URISyntaxException, ParserConfigurationException, IOException, SAXException, XPathExpressionException, TransformerException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse("C:\\dev\\mp4parser\\a.xml");
List split = TtmlSegmenter.split(doc, 60);
/* for (Document document : split) {
pretty(document, System.out, 4);
}*/
Track t = new TtmlTrackImpl("a.xml", split);
Movie m = new Movie();
m.addTrack(t);
Container c = new DefaultMp4Builder().build(m);
c.writeContainer(new FileOutputStream("output.mp4").getChannel());
}
public static String[] getAllNamespaces(Document doc) {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer;
try {
transformer = tf.newTransformer(new StreamSource(new ByteArrayInputStream(namespacesStyleSheet1)));
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(sw));
List r = new ArrayList(new LinkedHashSet(Arrays.asList(sw.getBuffer().toString().split("\n"))));
return r.toArray(new String[r.size()]);
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
public static String toTimeExpression(long ms) {
return toTimeExpression(ms, -1);
}
public static String toTimeExpression(long ms, int frames) {
String minus = ms >= 0 ? "" : "-";
ms = Math.abs(ms);
long hours = ms / 1000 / 60 / 60;
ms -= hours * 1000 * 60 * 60;
long minutes = ms / 1000 / 60;
ms -= minutes * 1000 * 60;
long seconds = ms / 1000;
ms -= seconds * 1000;
if (frames >= 0) {
return String.format("%s%02d:%02d:%02d:%d", minus, hours, minutes, seconds, frames);
} else {
return String.format("%s%02d:%02d:%02d.%03d", minus, hours, minutes, seconds, ms);
}
}
public static long toTime(String expr) {
Pattern p = Pattern.compile("(-?)([0-9][0-9]):([0-9][0-9]):([0-9][0-9])([\\.:][0-9][0-9]?[0-9]?)?");
Matcher m = p.matcher(expr);
if (m.matches()) {
String minus = m.group(1);
String hours = m.group(2);
String minutes = m.group(3);
String seconds = m.group(4);
String fraction = m.group(5);
if (fraction == null) {
fraction = ".000";
}
fraction = fraction.replace(":", ".");
long ms = Long.parseLong(hours) * 60 * 60 * 1000;
ms += Long.parseLong(minutes) * 60 * 1000;
ms += Long.parseLong(seconds) * 1000;
if (fraction.contains(":")) {
ms += Double.parseDouble("0" + fraction.replace(":", ".")) * 40 * 1000; // 40ms == 25fps - simplifying assumption should be ok for here
} else {
ms += Double.parseDouble("0" + fraction) * 1000;
}
return ms * ("-".equals(minus) ? -1 : 1);
} else {
throw new RuntimeException("Cannot match '" + expr + "' to time expression");
}
}
public static final String SMPTE_TT_NAMESPACE = "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt";
public static final String TTML_NAMESPACE = "http://www.w3.org/ns/ttml";
public static final NamespaceContext NAMESPACE_CONTEXT = new TextTrackNamespaceContext();
private static class TextTrackNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if (prefix.equals("ttml")) {
return TTML_NAMESPACE;
}
if (prefix.equals("smpte")) {
return SMPTE_TT_NAMESPACE;
}
return null;
}
public Iterator getPrefixes(String val) {
return Arrays.asList("ttml", "smpte").iterator();
}
public String getPrefix(String uri) {
if (uri.equals(TTML_NAMESPACE)) {
return "ttml";
}
if (uri.equals(SMPTE_TT_NAMESPACE)) {
return "smpte";
}
return null;
}
}
public static void pretty(Document document, OutputStream outputStream, int indent) throws IOException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = transformerFactory.newTransformer();
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
}
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
if (indent > 0) {
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(indent));
}
Result result = new StreamResult(outputStream);
Source source = new DOMSource(document);
try {
transformer.transform(source, result);
} catch (TransformerException e) {
throw new IOException(e);
}
}
public static long getStartTime(Node p) {
long time = 0;
Node current = p;
while ((current = current.getParentNode()) != null) {
if (current.getAttributes() != null && current.getAttributes().getNamedItem("begin") != null) {
time += toTime(current.getAttributes().getNamedItem("begin").getNodeValue());
}
}
if (p.getAttributes() != null && p.getAttributes().getNamedItem("begin") != null) {
return time + toTime(p.getAttributes().getNamedItem("begin").getNodeValue());
}
return time;
}
public static long getEndTime(Node p) {
long time = 0;
Node current = p;
while ((current = current.getParentNode()) != null) {
if (current.getAttributes() != null && current.getAttributes().getNamedItem("begin") != null) {
time += toTime(current.getAttributes().getNamedItem("begin").getNodeValue());
}
}
if (p.getAttributes() != null && p.getAttributes().getNamedItem("end") != null) {
return time + toTime(p.getAttributes().getNamedItem("end").getNodeValue());
}
return time;
}
public static void deepCopyDocument(Document ttml, File target) throws IOException {
try {
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//*/@backgroundImage");
NodeList nl = (NodeList) expr.evaluate(ttml, XPathConstants.NODESET);
for (int i = 0; i < nl.getLength(); i++) {
Node backgroundImage = nl.item(i);
URI backgroundImageUri = URI.create(backgroundImage.getNodeValue());
if (!backgroundImageUri.isAbsolute()) {
copyLarge(new URI(ttml.getDocumentURI()).resolve(backgroundImageUri).toURL().openStream(), new File(target.toURI().resolve(backgroundImageUri).toURL().getFile()));
}
}
copyLarge(new URI(ttml.getDocumentURI()).toURL().openStream(), target);
} catch (XPathExpressionException e) {
throw new IOException(e);
} catch (URISyntaxException e) {
throw new IOException(e);
}
}
private static long copyLarge(InputStream input, File outputFile)
throws IOException {
byte[] buffer = new byte[16384];
long count = 0;
int n = 0;
outputFile.getParentFile().mkdirs();
FileOutputStream output = new FileOutputStream(outputFile);
try {
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
} finally {
output.close();
}
return count;
}
}