Skip to content

Commit

Permalink
Merge pull request #279 from Netflix/feature/compositionParseRefactor
Browse files Browse the repository at this point in the history
Refactoring and code cleanup related to parsing CompositionPlaylistType.
  • Loading branch information
davidt-netflix authored Jul 31, 2020
2 parents 1e18d4d + ce5b2fa commit 93cd7cc
Show file tree
Hide file tree
Showing 7 changed files with 613 additions and 501 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public IMFException(String s, @Nonnull IMFErrorLogger errorLogger)
this.errorLogger = errorLogger;
}

public IMFException(String s, Throwable t, @Nonnull IMFErrorLogger errorLogger)
{
super(s, t);
this.errorLogger = errorLogger;
}

public List<ErrorLogger.ErrorObject> getErrors()
{
List errorList = new ArrayList<ErrorLogger.ErrorObject>();
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,25 @@
import com.netflix.imflibrary.utils.ResourceByteRangeProvider;
import com.netflix.imflibrary.utils.UUIDHelper;
import com.netflix.imflibrary.utils.Utilities;
import com.netflix.imflibrary.writerTools.utils.ValidationEventHandlerImpl;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
* A class that models an IMF Composition Playlist structure.
Expand Down Expand Up @@ -120,48 +115,23 @@ public IMFCompositionPlaylistType(String id,
this.creator = creator;
this.contentOriginator = contentOriginator;
this.contentTitle = contentTitle;
this.segmentList = segmentList;
this.essenceDescriptorList = essenceDescriptorList;
this.segmentList = Collections.unmodifiableList(segmentList);
this.essenceDescriptorList = Collections.unmodifiableList(essenceDescriptorList);
this.coreConstraintsVersion = coreConstraintsVersion;
this.applicationIdSet = applicationIds;
this.applicationIdSet = Collections.unmodifiableSet(applicationIds);

if(imfErrorLogger.hasFatalErrors())
{
throw new IMFException("Failed to create IMFBaseResourceType", imfErrorLogger);
}
}

private static class CoreConstraintsSchemas {
private final String coreConstraintsSchemaPath;
private final String coreConstraintsContext;

private CoreConstraintsSchemas(String coreConstraintsSchemaPath, String coreConstraintsContext) {
this.coreConstraintsSchemaPath = coreConstraintsSchemaPath;
this.coreConstraintsContext = coreConstraintsContext;
}

private String getCoreConstraintsSchemaPath() {
return this.coreConstraintsSchemaPath;
}

private String getCoreConstraintsContext() {
return this.coreConstraintsContext;
}
}
private static final List<CoreConstraintsSchemas> supportedIMFCoreConstraintsSchemas = Collections.unmodifiableList
(new ArrayList<CoreConstraintsSchemas>() {{
add(new CoreConstraintsSchemas("org/smpte_ra/schemas/st2067_2_2013/imf-core-constraints-20130620-pal.xsd", "org.smpte_ra.schemas.st2067_2_2013"));
add(new CoreConstraintsSchemas("org/smpte_ra/schemas/st2067_2_2016/imf-core-constraints-20160411.xsd", "org.smpte_ra.schemas.st2067_2_2016"));
}});

private static final String dcmlTypes_schema_path = "org/smpte_ra/schemas/st0433_2008/dcmlTypes/dcmlTypes.xsd";
private static final String xmldsig_core_schema_path = "org/w3/_2000_09/xmldsig/xmldsig-core-schema.xsd";
private static final Set<String> supportedCPLSchemaURIs = Collections.unmodifiableSet(new HashSet<String>() {{
add("http://www.smpte-ra.org/schemas/2067-3/2013");
add("http://www.smpte-ra.org/schemas/2067-3/2016");
}});

@Nullable
@Nonnull
private static final String getCompositionNamespaceURI(ResourceByteRangeProvider resourceByteRangeProvider, @Nonnull IMFErrorLogger imfErrorLogger) throws IOException {

String result = "";
Expand Down Expand Up @@ -215,41 +185,6 @@ public void fatalError(SAXParseException exception) throws SAXException {
return result;
}

private static final String getCPLNamespaceVersion(String namespaceURI) {
String[] uriComponents = namespaceURI.split("/");
String namespaceVersion = uriComponents[uriComponents.length - 1];
return namespaceVersion;
}

private static final String serializeIMFCoreConstaintsSchemasToString(List<CoreConstraintsSchemas> coreConstraintsSchemas) {
StringBuilder stringBuilder = new StringBuilder();
for (CoreConstraintsSchemas coreConstraintsSchema : coreConstraintsSchemas) {
stringBuilder.append(String.format("%n"));
stringBuilder.append(coreConstraintsSchema.getCoreConstraintsContext());
}
return stringBuilder.toString();
}

private static final String getIMFCPLSchemaPath(String namespaceVersion, @Nonnull IMFErrorLogger imfErrorLogger) {
String imf_cpl_schema_path;
switch (namespaceVersion) {
case "2013":
imf_cpl_schema_path = "org/smpte_ra/schemas/st2067_3_2013/imf-cpl.xsd";
break;
case "2016":
imf_cpl_schema_path = "org/smpte_ra/schemas/st2067_3_2016/imf-cpl-20160411.xsd";
break;
default:
String message = String.format("Please check the CPL document and namespace URI, currently we " +
"only support the following schema URIs %s", Utilities.serializeObjectCollectionToString
(supportedCPLSchemaURIs));
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors
.ErrorLevels.FATAL,
message);
throw new IMFException(message, imfErrorLogger);
}
return imf_cpl_schema_path;
}

/**
* A method that confirms if the inputStream corresponds to a Composition document instance.
Expand Down Expand Up @@ -281,99 +216,33 @@ public static boolean isCompositionPlaylist(ResourceByteRangeProvider resourceBy
return false;
}

public static IMFCompositionPlaylistType getCompositionPlayListType(ResourceByteRangeProvider resourceByteRangeProvider, IMFErrorLogger imfErrorLogger) throws IOException {
String imf_cpl_schema_path = "";
try {
String cplNameSpaceURI = getCompositionNamespaceURI(resourceByteRangeProvider, imfErrorLogger);
public static IMFCompositionPlaylistType getCompositionPlayListType(ResourceByteRangeProvider resourceByteRangeProvider, IMFErrorLogger imfErrorLogger) throws IOException
{
// Determine which version of the CPL namespace is being used
String cplNamespace = getCompositionNamespaceURI(resourceByteRangeProvider, imfErrorLogger);

String namespaceVersion = getCPLNamespaceVersion(cplNameSpaceURI);
imf_cpl_schema_path = getIMFCPLSchemaPath(namespaceVersion, imfErrorLogger);
}
catch(IMFException e)
if (cplNamespace.equals("http://www.smpte-ra.org/schemas/2067-3/2013"))
{
imfErrorLogger.addAllErrors(e.getErrors());
throw new IMFException("Composition creation failed", imfErrorLogger);
}
org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType jaxbCpl
= CompositionModel_st2067_2_2013.unmarshallCpl(resourceByteRangeProvider, imfErrorLogger);

CoreConstraintsSchemas coreConstraintsSchema = supportedIMFCoreConstraintsSchemas.get(0);
JAXBElement jaxbElement = null;

for (int i = 0; i < supportedIMFCoreConstraintsSchemas.size(); i++) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try (InputStream inputStream = resourceByteRangeProvider.getByteRangeAsStream(0, resourceByteRangeProvider.getResourceSize() - 1);
InputStream xmldsig_core_is = contextClassLoader.getResourceAsStream(xmldsig_core_schema_path);
InputStream dcmlTypes_is = contextClassLoader.getResourceAsStream(dcmlTypes_schema_path);
InputStream imf_cpl_is = contextClassLoader.getResourceAsStream(imf_cpl_schema_path);
InputStream imf_core_constraints_is = contextClassLoader.getResourceAsStream(supportedIMFCoreConstraintsSchemas.get(i).coreConstraintsSchemaPath);) {
StreamSource[] streamSources = new StreamSource[4];
streamSources[0] = new StreamSource(xmldsig_core_is);
streamSources[1] = new StreamSource(dcmlTypes_is);
streamSources[2] = new StreamSource(imf_cpl_is);
streamSources[3] = new StreamSource(imf_core_constraints_is);

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(streamSources);

ValidationEventHandlerImpl validationEventHandlerImpl = new ValidationEventHandlerImpl(true);
JAXBContext jaxbContext = JAXBContext.newInstance(supportedIMFCoreConstraintsSchemas.get(i).coreConstraintsContext);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setEventHandler(validationEventHandlerImpl);
unmarshaller.setSchema(schema);

jaxbElement = (JAXBElement) unmarshaller.unmarshal(inputStream);
coreConstraintsSchema = supportedIMFCoreConstraintsSchemas.get(i);

if (validationEventHandlerImpl.hasErrors()) {
validationEventHandlerImpl.getErrors().stream()
.map(e -> new ErrorLogger.ErrorObject(
IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR,
e.getValidationEventSeverity(),
"Line Number : " + e.getLineNumber().toString() + " - " + e.getErrorMessage())
)
.forEach(imfErrorLogger::addError);

throw new IMFException(validationEventHandlerImpl.toString(), imfErrorLogger);
}
break; //No errors so we can break out without trying other Core constraints schema namespaces.
} catch (SAXException | JAXBException e) {
if (i == supportedIMFCoreConstraintsSchemas.size() - 1) {
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger
.IMFErrors.ErrorLevels.FATAL,
e.getMessage());
throw new IMFException(e.getMessage(), imfErrorLogger);
}
}
return CompositionModel_st2067_2_2013.getCompositionPlaylist(jaxbCpl, imfErrorLogger);
}
else if (cplNamespace.equals("http://www.smpte-ra.org/schemas/2067-3/2016"))
{
org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType jaxbCpl
= CompositionModel_st2067_2_2016.unmarshallCpl(resourceByteRangeProvider, imfErrorLogger);

String coreConstraintsVersion = coreConstraintsSchema.getCoreConstraintsContext();
IMFCompositionPlaylistType compositionPlaylistType = null;

switch (coreConstraintsVersion) {
case "org.smpte_ra.schemas.st2067_2_2013": {
org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType compositionPlaylistTypeJaxb =
(org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType) jaxbElement.getValue();

compositionPlaylistType = CompositionModel_st2067_2_2013.getCompositionPlaylist(compositionPlaylistTypeJaxb,
imfErrorLogger);
}
break;
case "org.smpte_ra.schemas.st2067_2_2016": {
org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType compositionPlaylistTypeJaxb = (org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType) jaxbElement.getValue();

compositionPlaylistType = CompositionModel_st2067_2_2016.getCompositionPlaylist(compositionPlaylistTypeJaxb, imfErrorLogger);
}
break;
default:
String message = String.format("Please check the CPL document, currently we only support the " +
"following CoreConstraints schema URIs %s", serializeIMFCoreConstaintsSchemasToString
(supportedIMFCoreConstraintsSchemas));
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger
.IMFErrors.ErrorLevels.FATAL, message);
throw new IMFException(message, imfErrorLogger);

return CompositionModel_st2067_2_2016.getCompositionPlaylist(jaxbCpl, imfErrorLogger);
}
else
{
String message = String.format("Please check the CPL document and namespace URI, currently we " +
"only support the following schema URIs %s", supportedCPLSchemaURIs);
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger
.IMFErrors.ErrorLevels.FATAL, message);
throw new IMFException(message, imfErrorLogger);
}

return compositionPlaylistType;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import javax.annotation.concurrent.Immutable;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

Expand All @@ -39,14 +40,9 @@ public IMFMarkerResourceType(String id,
List<IMFMarkerType> markerList )
{
super(id, editRate, intrinsicDuration, entryPoint, sourceDuration, repeatCount);
markerList.sort(new Comparator<IMFMarkerType>() {
@Override
public int compare(IMFMarkerType o1, IMFMarkerType o2) {
return o1.getOffset().compareTo(o2.getOffset());
}
});
markerList.sort(Comparator.comparing(IMFMarkerType::getOffset));

this.markerList = markerList;
this.markerList = Collections.unmodifiableList(markerList);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.netflix.imflibrary.st2067_2;

import javax.annotation.concurrent.Immutable;
import java.util.Collections;
import java.util.List;

/**
Expand All @@ -32,7 +33,7 @@ final class IMFSegmentType {
public IMFSegmentType(String id,
List<IMFSequenceType> sequenceList){
this.id = id;
this.sequenceList = sequenceList;
this.sequenceList = Collections.unmodifiableList(sequenceList);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.netflix.imflibrary.st2067_2;

import javax.annotation.concurrent.Immutable;
import java.util.Collections;
import java.util.List;

/**
Expand All @@ -38,7 +39,7 @@ public IMFSequenceType(String id,
{
this.id = id;
this.trackId = trackId;
this.resourceList = (List<IMFBaseResourceType>)resourceList;
this.resourceList = Collections.unmodifiableList(resourceList);
this.type = type;
}

Expand Down

0 comments on commit 93cd7cc

Please sign in to comment.