org.mp4parser.muxer.tracks.ttml.TtmlHelpers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of muxer Show documentation
Show all versions of muxer Show documentation
This package has a focus on file operation. It can read A/V data from Random Access Datasources
package org.mp4parser.muxer.tracks.ttml;
import org.mp4parser.Container;
import org.mp4parser.muxer.Movie;
import org.mp4parser.muxer.Track;
import org.mp4parser.muxer.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.*;
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.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TtmlHelpers {
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();
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 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;
}
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;
}
}
}