public void resolveParagraphTimeOverlaps() { new TtmlParagraphResolver(mergedItt).resolveTimeOverlaps(); }
private TtEltype fixStyleRefs(TtEltype itt, String oldId, String newId) { Transformer transformer = TtmlUtils.createTtmlTransformer(REPLACE_STYLE_ID_TRANSFORMATION); transformer.setParameter(OLD_STYLE_ID_PARAMETER, oldId); transformer.setParameter(NEW_STYLE_ID_PARAMETER, newId); return TtmlUtils.transformTtmlDocument(itt, transformer); } }
/** * Gets correct value of ending point of a temporal interval. * <p><p/> * Note: if both end and dur attributes are specified in the element then ending point is equal to the lesser of the * value of the dur attribute and the difference between the value of the end attribute and the element's begin * time. * * @param begin begin point of a temporal interval * @param end ending point of a temporal interval * @param dur duration of a temporal interval * @return correct value of ending point of a temporal interval */ private static long getEnd(TtmlTimeConverter ttConverter, String begin, String end, String dur) { long b = (begin == null || begin.isEmpty()) ? 0 : ttConverter.parseTimeExpression(begin); long e = (end == null || end.isEmpty()) ? Long.MAX_VALUE : ttConverter.parseTimeExpression(end); long d = (dur == null || dur.isEmpty()) ? Long.MAX_VALUE - b : ttConverter.parseTimeExpression(dur); return Math.min(b + d, e); }
@Test public void offsetTimeMsParsedCorrecly() { /* PREPARATION */ TtmlTimeConverter converter = new TtmlTimeConverter(new TtEltype()); int expectedMs = 10; String timeExpression = String.format("%dms", expectedMs); /* EXECUTION */ long ms = converter.parseTimeExpression(timeExpression); /* VALIDATION */ assertEquals(expectedMs, ms); } }
TtmlTimeConverter ttConverter = new TtmlTimeConverter(tt); long totalBegin = ttConverter.parseTimeExpression(tt.getBody().getBegin()); DivEltype div = divIt.next(); totalBegin += ttConverter.parseTimeExpression(div.getBegin()); long pBegin = totalBegin + ttConverter.parseTimeExpression(p.getBegin()); long pEnd = totalBegin + getEnd(ttConverter, p.getBegin(), p.getEnd(), p.getDur()); if (pEnd < startMS || pBegin > endMS) { // remove not matched blockIt.remove(); frameRate == null ? ttConverter.getUnitsInSec() : frameRate)); p.setEnd(ConversionHelper.msToSmpteTimecode(offsetMS + pEnd - startMS, frameRate == null ? ttConverter.getUnitsInSec() : frameRate)); p.setDur(null);
/** * Resolve time overlaps. * Method uses {@link SplitUtils} for split initial <p>s on slices. */ public void resolveTimeOverlaps() { DivEltype div = tt.getBody().getDiv().stream() .findFirst() .orElseThrow(() -> new ConvertException("At least one <div> must be defined.")); List<PEltype> sliced = split(pStream(div.getBlockClass())) .map(this::merge) .collect(Collectors.toList()); div.getBlockClass().clear(); div.getBlockClass().addAll(sliced); }
private PEltype merge(Slice<PEltype> slice) { PEltype p = copy(slice.getContents().stream() .findFirst() .orElseThrow(() -> new ConvertException("At least one <p> must exist for content."))); p.setBegin(ConversionHelper.msToSmpteTimecode(slice.getBegin(), frameRate)); p.setEnd(ConversionHelper.msToSmpteTimecode(slice.getEnd(), frameRate)); p.getContent().clear(); p.getContent().addAll(mergeContent(slice.getContents())); return p; }
/** * Writes resulting iTT document to output file. * * @throws JAXBException */ public void writeToFile() throws JAXBException { if (mergedItt == null) { System.out.println("Resulting iTT is empty. No data to write."); return; } Marshaller jaxbMarshaller = TtmlUtils.createTtmlJaxbContext().createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.marshal(new ObjectFactory().createTt(mergedItt), outputFile); }
private PEltype copy(PEltype p) { List<Object> styles = new ArrayList<>(p.getStyle()); Object region = p.getRegion(); p.getStyle().clear(); p.setRegion(null); PEltype targetP = deepCopy(p, PEltype.class, TTML_PACKAGES); p.getStyle().addAll(styles); p.setRegion(region); targetP.getStyle().addAll(styles); targetP.setRegion(region); return targetP; }
/** * Moves all style refs from boby and div to corresponding p element for easiest merging. * * @param tt root timed object */ public static void moveStyleRefToP(TtEltype tt) { Set<Object> styles = new HashSet<>(tt.getBody().getStyle()); // set style to null, workaround for JAXB objects else style list will be just empty (invalid ttml) BodyEltype body = tt.getBody(); body.getStyle().clear(); setStyleListToNull(body); tt.getBody().getDiv().stream() .peek((div) -> { styles.addAll(div.getStyle()); div.getStyle().clear(); setStyleListToNull(div); }) .flatMap((div) -> div.getBlockClass().stream()) .filter((o) -> o instanceof PEltype) .map((o) -> (PEltype) o) .forEachOrdered((p) -> { Set<Object> pStyles = new HashSet<>(styles); pStyles.addAll(p.getStyle()); p.getStyle().clear(); p.getStyle().addAll(pStyles); }); }
@Test public void testParallelParagraphs() { PEltype p1 = TtmlTestUtils.createP("00:00:01:00", "00:00:04:00", "p1"); PEltype p2 = TtmlTestUtils.createP("00:00:01:00", "00:00:04:00", "p2"); TtEltype tt = TtmlTestUtils.wrapPs(p1, p2); new TtmlParagraphResolver(tt).resolveTimeOverlaps(); Iterator<Object> iterator; iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:01:00", TtmlTestUtils.getPBegin(iterator.next())); assertFalse(iterator.hasNext()); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:04:00", TtmlTestUtils.getPEnd(iterator.next())); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertArrayEquals(new Serializable[]{"p1", "p2"}, TtmlTestUtils.getPContent(iterator.next())); }
@Test public void clockTimeWithoutFractionsAndFramesParsedCorrecly() { /* PREPARATION */ TtmlTimeConverter converter = new TtmlTimeConverter(new TtEltype()); int h = 1; int m = 2; int s = 3; String timeExpression = String.format("%02d:%02d:%02d", h, m, s); long expectedMs = (h * 3600 + m * 60 + s) * 1000; /* EXECUTION */ long ms = converter.parseTimeExpression(timeExpression); /* VALIDATION */ assertEquals(expectedMs, ms); }
/** * Converts all TTML input documents to corresponding iTT. * * @throws TransformerConfigurationException */ public void convertInputsToItt() throws TransformerConfigurationException { Transformer transformer = TtmlUtils.createTtmlTransformer(TTML_TO_ITT_TRANSFORMATION); convertedItts = ttmlTts.stream() .map((tt) -> TtmlUtils.transformTtmlDocument(tt, transformer)) .collect(Collectors.toCollection(ArrayList::new)); mergedItt = convertedItts.get(0); }
/** * Does TTML document transformation to another TTML document. * * @param tt source TTML document root element * @param transformer transformer * @return TTML document after transformation */ public static TtEltype transformTtmlDocument(TtEltype tt, Transformer transformer) { JAXBElement<TtEltype> ttJaxb = new ObjectFactory().createTt(tt); try { JAXBContext jaxbc = createTtmlJaxbContext(); JAXBSource source = new JAXBSource(jaxbc, ttJaxb); JAXBResult result = new JAXBResult(jaxbc); // transform transformer.transform(source, result); return (TtEltype) ((JAXBElement<TtEltype>) result.getResult()).getValue(); } catch (JAXBException | TransformerException e) { throw new ConvertException(e); } }
@Test public void testGapBetweenParagraphs() { PEltype p1 = TtmlTestUtils.createP("00:00:01:00", "00:00:04:00", "p1"); PEltype p2 = TtmlTestUtils.createP("00:00:10:00", "00:00:14:00", "p2"); TtEltype tt = TtmlTestUtils.wrapPs(p1, p2); new TtmlParagraphResolver(tt).resolveTimeOverlaps(); Iterator<Object> iterator; iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:01:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:10:00", TtmlTestUtils.getPBegin(iterator.next())); assertFalse(iterator.hasNext()); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:04:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:14:00", TtmlTestUtils.getPEnd(iterator.next())); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertArrayEquals(new Serializable[]{"p1"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p2"}, TtmlTestUtils.getPContent(iterator.next())); }
@Test public void clockTimeWithFramesNonDropParsedCorrecly() { /* PREPARATION */ TtmlTimeConverter converter = new TtmlTimeConverter(new TtEltype()); // default frame rate 30 int h = 1; int m = 2; int s = 3; int f = 10; String timeExpression = String.format("%02d:%02d:%02d:%02d", h, m, s, f); long expectedMs = (h * 3600 + m * 60 + s) * 1000 + (int)((float) f * 1000 / 30); /* EXECUTION */ long ms = converter.parseTimeExpression(timeExpression); /* VALIDATION */ assertEquals(expectedMs, ms); }
@Test public void testStuckedParagraphs() { PEltype p1 = TtmlTestUtils.createP("00:00:01:00", "00:00:02:00", "p1"); PEltype p2 = TtmlTestUtils.createP("00:00:02:00", "00:00:03:00", "p2"); PEltype p3 = TtmlTestUtils.createP("00:00:03:00", "00:00:04:00", "p3"); TtEltype tt = TtmlTestUtils.wrapPs(p1, p2, p3); new TtmlParagraphResolver(tt).resolveTimeOverlaps(); Iterator<Object> iterator; iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:01:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:02:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:03:00", TtmlTestUtils.getPBegin(iterator.next())); assertFalse(iterator.hasNext()); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:02:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:03:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:04:00", TtmlTestUtils.getPEnd(iterator.next())); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertArrayEquals(new Serializable[]{"p1"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p2"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p3"}, TtmlTestUtils.getPContent(iterator.next())); }
@Test public void testOverlappedParagraphs() { PEltype p1 = TtmlTestUtils.createP("00:00:01:00", "00:00:03:00", "p1"); PEltype p2 = TtmlTestUtils.createP("00:00:02:00", "00:00:04:00", "p2"); PEltype p3 = TtmlTestUtils.createP("00:00:03:00", "00:00:05:00", "p3"); TtEltype tt = TtmlTestUtils.wrapPs(p1, p2, p3); new TtmlParagraphResolver(tt).resolveTimeOverlaps(); Iterator<Object> iterator; iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:01:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:02:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:03:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:04:00", TtmlTestUtils.getPBegin(iterator.next())); assertFalse(iterator.hasNext()); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:02:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:03:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:04:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:05:00", TtmlTestUtils.getPEnd(iterator.next())); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertArrayEquals(new Serializable[]{"p1"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p1", "p2"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p2", "p3"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p3"}, TtmlTestUtils.getPContent(iterator.next())); }
@Test public void testNestedParagraphs() { PEltype p1 = TtmlTestUtils.createP("00:00:01:00", "00:00:10:00", "p1"); PEltype p2 = TtmlTestUtils.createP("00:00:03:00", "00:00:08:00", "p2"); PEltype p3 = TtmlTestUtils.createP("00:00:05:00", "00:00:06:00", "p3"); TtEltype tt = TtmlTestUtils.wrapPs(p1, p2, p3); new TtmlParagraphResolver(tt).resolveTimeOverlaps(); Iterator<Object> iterator; iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:01:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:03:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:05:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:06:00", TtmlTestUtils.getPBegin(iterator.next())); assertEquals("00:00:08:00", TtmlTestUtils.getPBegin(iterator.next())); assertFalse(iterator.hasNext()); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertEquals("00:00:03:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:05:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:06:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:08:00", TtmlTestUtils.getPEnd(iterator.next())); assertEquals("00:00:10:00", TtmlTestUtils.getPEnd(iterator.next())); iterator = tt.getBody().getDiv().get(0).getBlockClass().iterator(); assertArrayEquals(new Serializable[]{"p1"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p1", "p2"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p1", "p2", "p3"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p1", "p2"}, TtmlTestUtils.getPContent(iterator.next())); assertArrayEquals(new Serializable[]{"p1"}, TtmlTestUtils.getPContent(iterator.next())); }
TtEltype tt = TtmlTestUtils.wrapPs(p1, p2, p3); new TtmlParagraphResolver(tt).resolveTimeOverlaps();