From 038f23b8325dbef3d3cb1ab14f3fad252940568e Mon Sep 17 00:00:00 2001 From: David Thompson Date: Mon, 13 Jul 2020 11:29:22 -0700 Subject: [PATCH 1/3] Added updated IMF 2020 schema files. Generated JAXB classes for schema additions. --- .../schemas/st2067_21_2016/ObjectFactory.java | 62 +++++++ .../schemas/st2067_2_2020/CDPSequence.java | 76 ++++++++ .../schemas/st2067_2_2020/ObjectFactory.java | 170 ++++++++++++++++++ .../StereoImageTrackFileResourceType.java | 101 +++++++++++ .../schemas/st2067_2_2020/package-info.java | 9 + .../schemas/st2067_21_2016/app2e-2016.xsd | 62 +++++++ .../imf-core-constraints-2020.xsd | 108 +++++++++++ 7 files changed, 588 insertions(+) create mode 100644 generated/org/smpte_ra/schemas/st2067_21_2016/ObjectFactory.java create mode 100644 generated/org/smpte_ra/schemas/st2067_2_2020/CDPSequence.java create mode 100644 generated/org/smpte_ra/schemas/st2067_2_2020/ObjectFactory.java create mode 100644 generated/org/smpte_ra/schemas/st2067_2_2020/StereoImageTrackFileResourceType.java create mode 100644 generated/org/smpte_ra/schemas/st2067_2_2020/package-info.java create mode 100644 src/main/resources/org/smpte_ra/schemas/st2067_21_2016/app2e-2016.xsd create mode 100644 src/main/resources/org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd diff --git a/generated/org/smpte_ra/schemas/st2067_21_2016/ObjectFactory.java b/generated/org/smpte_ra/schemas/st2067_21_2016/ObjectFactory.java new file mode 100644 index 00000000..b37f02d9 --- /dev/null +++ b/generated/org/smpte_ra/schemas/st2067_21_2016/ObjectFactory.java @@ -0,0 +1,62 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2020.07.12 at 09:01:40 PM PDT +// + + +package org.smpte_ra.schemas.st2067_21_2016; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.smpte_ra.schemas.st2067_21_2016 package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _MaxCLL_QNAME = new QName("http://www.smpte-ra.org/schemas/2067-21/2016", "MaxCLL"); + private final static QName _MaxFALL_QNAME = new QName("http://www.smpte-ra.org/schemas/2067-21/2016", "MaxFALL"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.smpte_ra.schemas.st2067_21_2016 + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Integer }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/schemas/2067-21/2016", name = "MaxCLL") + public JAXBElement createMaxCLL(Integer value) { + return new JAXBElement(_MaxCLL_QNAME, Integer.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Integer }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/schemas/2067-21/2016", name = "MaxFALL") + public JAXBElement createMaxFALL(Integer value) { + return new JAXBElement(_MaxFALL_QNAME, Integer.class, null, value); + } + +} diff --git a/generated/org/smpte_ra/schemas/st2067_2_2020/CDPSequence.java b/generated/org/smpte_ra/schemas/st2067_2_2020/CDPSequence.java new file mode 100644 index 00000000..1353bbb5 --- /dev/null +++ b/generated/org/smpte_ra/schemas/st2067_2_2020/CDPSequence.java @@ -0,0 +1,76 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2020.07.12 at 07:25:26 PM PDT +// + + +package org.smpte_ra.schemas.st2067_2_2020; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import org.smpte_ra.schemas.st2067_2_2016.SequenceType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <extension base="{http://www.smpte-ra.org/schemas/2067-3/2016}SequenceType">
+ *       <sequence>
+ *         <element name="ParentTrackID" type="{http://www.smpte-ra.org/schemas/433/2008/dcmlTypes/}UUIDType"/>
+ *       </sequence>
+ *     </extension>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "parentTrackID" +}) +@XmlRootElement(name = "CDPSequence") +public class CDPSequence + extends SequenceType +{ + + @XmlElement(name = "ParentTrackID", required = true) + @XmlSchemaType(name = "anyURI") + protected String parentTrackID; + + /** + * Gets the value of the parentTrackID property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getParentTrackID() { + return parentTrackID; + } + + /** + * Sets the value of the parentTrackID property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setParentTrackID(String value) { + this.parentTrackID = value; + } + +} diff --git a/generated/org/smpte_ra/schemas/st2067_2_2020/ObjectFactory.java b/generated/org/smpte_ra/schemas/st2067_2_2020/ObjectFactory.java new file mode 100644 index 00000000..690fd858 --- /dev/null +++ b/generated/org/smpte_ra/schemas/st2067_2_2020/ObjectFactory.java @@ -0,0 +1,170 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2020.07.12 at 07:25:26 PM PDT +// + + +package org.smpte_ra.schemas.st2067_2_2020; + +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; +import org.smpte_ra.schemas.st2067_2_2016.SequenceType; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.smpte_ra.schemas.st2067_2_2020 package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _CommentarySequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "CommentarySequence"); + private final static QName _MainImageSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "MainImageSequence"); + private final static QName _KaraokeSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "KaraokeSequence"); + private final static QName _ForcedNarrativeSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "ForcedNarrativeSequence"); + private final static QName _MainAudioSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "MainAudioSequence"); + private final static QName _HearingImpairedCaptionsSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "HearingImpairedCaptionsSequence"); + private final static QName _SubtitlesSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "SubtitlesSequence"); + private final static QName _TimedTextResourceID_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "TimedTextResourceID"); + private final static QName _VisuallyImpairedTextSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "VisuallyImpairedTextSequence"); + private final static QName _AncillaryDataSequence_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "AncillaryDataSequence"); + private final static QName _ApplicationIdentification_QNAME = new QName("http://www.smpte-ra.org/ns/2067-2/2020", "ApplicationIdentification"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.smpte_ra.schemas.st2067_2_2020 + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link CDPSequence } + * + */ + public CDPSequence createCDPSequence() { + return new CDPSequence(); + } + + /** + * Create an instance of {@link StereoImageTrackFileResourceType } + * + */ + public StereoImageTrackFileResourceType createStereoImageTrackFileResourceType() { + return new StereoImageTrackFileResourceType(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "CommentarySequence") + public JAXBElement createCommentarySequence(SequenceType value) { + return new JAXBElement(_CommentarySequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "MainImageSequence") + public JAXBElement createMainImageSequence(SequenceType value) { + return new JAXBElement(_MainImageSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "KaraokeSequence") + public JAXBElement createKaraokeSequence(SequenceType value) { + return new JAXBElement(_KaraokeSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "ForcedNarrativeSequence") + public JAXBElement createForcedNarrativeSequence(SequenceType value) { + return new JAXBElement(_ForcedNarrativeSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "MainAudioSequence") + public JAXBElement createMainAudioSequence(SequenceType value) { + return new JAXBElement(_MainAudioSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "HearingImpairedCaptionsSequence") + public JAXBElement createHearingImpairedCaptionsSequence(SequenceType value) { + return new JAXBElement(_HearingImpairedCaptionsSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "SubtitlesSequence") + public JAXBElement createSubtitlesSequence(SequenceType value) { + return new JAXBElement(_SubtitlesSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "TimedTextResourceID") + public JAXBElement createTimedTextResourceID(String value) { + return new JAXBElement(_TimedTextResourceID_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "VisuallyImpairedTextSequence") + public JAXBElement createVisuallyImpairedTextSequence(SequenceType value) { + return new JAXBElement(_VisuallyImpairedTextSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SequenceType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "AncillaryDataSequence") + public JAXBElement createAncillaryDataSequence(SequenceType value) { + return new JAXBElement(_AncillaryDataSequence_QNAME, SequenceType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link List }{@code <}{@link String }{@code >}{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", name = "ApplicationIdentification") + public JAXBElement> createApplicationIdentification(List value) { + return new JAXBElement>(_ApplicationIdentification_QNAME, ((Class) List.class), null, ((List ) value)); + } + +} diff --git a/generated/org/smpte_ra/schemas/st2067_2_2020/StereoImageTrackFileResourceType.java b/generated/org/smpte_ra/schemas/st2067_2_2020/StereoImageTrackFileResourceType.java new file mode 100644 index 00000000..31523951 --- /dev/null +++ b/generated/org/smpte_ra/schemas/st2067_2_2020/StereoImageTrackFileResourceType.java @@ -0,0 +1,101 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2020.07.12 at 07:25:26 PM PDT +// + + +package org.smpte_ra.schemas.st2067_2_2020; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; +import org.smpte_ra.schemas.st2067_2_2016.BaseResourceType; +import org.smpte_ra.schemas.st2067_2_2016.TrackFileResourceType; + + +/** + *

Java class for StereoImageTrackFileResourceType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="StereoImageTrackFileResourceType">
+ *   <complexContent>
+ *     <extension base="{http://www.smpte-ra.org/schemas/2067-3/2016}BaseResourceType">
+ *       <sequence>
+ *         <element name="LeftEye" type="{http://www.smpte-ra.org/schemas/2067-3/2016}TrackFileResourceType"/>
+ *         <element name="RightEye" type="{http://www.smpte-ra.org/schemas/2067-3/2016}TrackFileResourceType"/>
+ *       </sequence>
+ *     </extension>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "StereoImageTrackFileResourceType", propOrder = { + "leftEye", + "rightEye" +}) +public class StereoImageTrackFileResourceType + extends BaseResourceType +{ + + @XmlElement(name = "LeftEye", required = true) + protected TrackFileResourceType leftEye; + @XmlElement(name = "RightEye", required = true) + protected TrackFileResourceType rightEye; + + /** + * Gets the value of the leftEye property. + * + * @return + * possible object is + * {@link TrackFileResourceType } + * + */ + public TrackFileResourceType getLeftEye() { + return leftEye; + } + + /** + * Sets the value of the leftEye property. + * + * @param value + * allowed object is + * {@link TrackFileResourceType } + * + */ + public void setLeftEye(TrackFileResourceType value) { + this.leftEye = value; + } + + /** + * Gets the value of the rightEye property. + * + * @return + * possible object is + * {@link TrackFileResourceType } + * + */ + public TrackFileResourceType getRightEye() { + return rightEye; + } + + /** + * Sets the value of the rightEye property. + * + * @param value + * allowed object is + * {@link TrackFileResourceType } + * + */ + public void setRightEye(TrackFileResourceType value) { + this.rightEye = value; + } + +} diff --git a/generated/org/smpte_ra/schemas/st2067_2_2020/package-info.java b/generated/org/smpte_ra/schemas/st2067_2_2020/package-info.java new file mode 100644 index 00000000..105736dc --- /dev/null +++ b/generated/org/smpte_ra/schemas/st2067_2_2020/package-info.java @@ -0,0 +1,9 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2020.07.12 at 07:25:26 PM PDT +// + +@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.smpte-ra.org/ns/2067-2/2020", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package org.smpte_ra.schemas.st2067_2_2020; diff --git a/src/main/resources/org/smpte_ra/schemas/st2067_21_2016/app2e-2016.xsd b/src/main/resources/org/smpte_ra/schemas/st2067_21_2016/app2e-2016.xsd new file mode 100644 index 00000000..e0965148 --- /dev/null +++ b/src/main/resources/org/smpte_ra/schemas/st2067_21_2016/app2e-2016.xsd @@ -0,0 +1,62 @@ + + + + + + + + + + + diff --git a/src/main/resources/org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd b/src/main/resources/org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd new file mode 100644 index 00000000..b97fa28f --- /dev/null +++ b/src/main/resources/org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a4efe1a4fe3bfa9d8141472f1a0dda8dc310ec7c Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 30 Jul 2020 00:22:38 -0700 Subject: [PATCH 2/3] Initial support for parsing and authoring IMF 2020 Decoupled Core Constraints version from CPL version. Deprecated use of core constraints "version" and instead added core constraints "schema" accessors. Added constants and utilities for CoreConstraints. --- .../com/netflix/imflibrary/app/IMPFixer.java | 9 +- .../AbstractApplicationComposition.java | 20 ++- .../st2067_2/Application2Composition.java | 2 + .../Application2ExtendedComposition.java | 3 + .../st2067_2/Application5Composition.java | 1 + .../st2067_2/ApplicationComposition.java | 12 +- .../ApplicationCompositionFactory.java | 1 + .../CompositionModel_st2067_2_2013.java | 19 +- .../CompositionModel_st2067_2_2016.java | 35 ++-- .../imflibrary/st2067_2/CoreConstraints.java | 88 ++++++++++ .../st2067_2/IMFCompositionPlaylistType.java | 16 +- .../st2067_2/IMFCoreConstraintsChecker.java | 6 +- .../CompositionPlaylistBuilder_2013.java | 85 +++++---- .../CompositionPlaylistBuilder_2016.java | 165 ++++++++++++------ .../imflibrary/writerTools/IMPBuilder.java | 20 ++- 15 files changed, 363 insertions(+), 119 deletions(-) create mode 100644 src/main/java/com/netflix/imflibrary/st2067_2/CoreConstraints.java diff --git a/src/main/java/com/netflix/imflibrary/app/IMPFixer.java b/src/main/java/com/netflix/imflibrary/app/IMPFixer.java index 75009cfa..1fb30fc5 100755 --- a/src/main/java/com/netflix/imflibrary/app/IMPFixer.java +++ b/src/main/java/com/netflix/imflibrary/app/IMPFixer.java @@ -17,6 +17,7 @@ import com.netflix.imflibrary.st0429_9.BasicMapProfileV2MappedFileSet; import com.netflix.imflibrary.st2067_2.ApplicationComposition; import com.netflix.imflibrary.st2067_2.ApplicationCompositionFactory; +import com.netflix.imflibrary.st2067_2.CoreConstraints; import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack; import com.netflix.imflibrary.utils.*; import com.netflix.imflibrary.writerTools.CompositionPlaylistBuilder_2016; @@ -228,16 +229,18 @@ public static List analyzePackageAndWrite(File rootFile Set trackFileIDsSet = trackFileIDToHeaderPartitionPayLoadMap.keySet(); if(versionCPLSchema.equals("")) { - if (applicationComposition.getCoreConstraintsVersion().contains("st2067_2_2013")) { + String coreConstraintsSchema = applicationComposition.getCoreConstraintsSchema(); + if (coreConstraintsSchema.equals(CoreConstraints.NAMESPACE_IMF_2013)) { versionCPLSchema = "2013"; } - else if (applicationComposition.getCoreConstraintsVersion().contains("st2067_2_2016")) { + else if (coreConstraintsSchema.equals(CoreConstraints.NAMESPACE_IMF_2016) + || coreConstraintsSchema.equals(CoreConstraints.NAMESPACE_IMF_2020)) { versionCPLSchema = "2016"; } else { imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, - String.format("Input package CoreConstraintsVersion %s not supported", applicationComposition.getCoreConstraintsVersion().toString())); + String.format("Input package CoreConstraints Schema %s not supported", applicationComposition.getCoreConstraintsSchema())); } } diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java b/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java index 68ed1913..812dee78 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java @@ -84,7 +84,7 @@ public abstract class AbstractApplicationComposition implements ApplicationCompo - private final String coreConstraintsVersion; + private final String coreConstraintsSchema; private final Map virtualTrackMap; private final IMFCompositionPlaylistType compositionPlaylistType; private final Map> essenceDescriptorDomNodeMap; @@ -140,7 +140,7 @@ public AbstractApplicationComposition(@Nonnull IMFCompositionPlaylistType imfCom this.regXMLLibDictionary = new RegXMLLibDictionary(); - this.coreConstraintsVersion = this.compositionPlaylistType.getCoreConstraintsVersion(); + this.coreConstraintsSchema = this.compositionPlaylistType.getCoreConstraintsSchema(); this.essenceDescriptorKeyIgnoreSet = Collections.unmodifiableSet(ignoreSet); @@ -375,12 +375,22 @@ private IMFCompositionPlaylistType getCompositionPlaylistType() { } /** - * Getter for the CoreConstraintsURI corresponding to this CompositionPlaylist + * Get the Java package string for the Core Constraints version + * @deprecated Instead use {@link #getCoreConstraintsSchema()} * - * @return the uri for the CoreConstraints schema for this CompositionPlaylist + * @return package containing the Core Constraints classes */ + @Deprecated public String getCoreConstraintsVersion() { - return this.coreConstraintsVersion; + return CoreConstraints.packageFromSchema(this.coreConstraintsSchema); + } + + /** + * Getter for the Core Constraints schema URI. + * @return URI for the Core Constraints schema + */ + @Nonnull public String getCoreConstraintsSchema() { + return this.coreConstraintsSchema; } /** diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/Application2Composition.java b/src/main/java/com/netflix/imflibrary/st2067_2/Application2Composition.java index 45e68894..c19b3b34 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/Application2Composition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/Application2Composition.java @@ -25,6 +25,8 @@ * A class that models Composition with Application 2 constraints from 2067-20 specification */ public class Application2Composition extends AbstractApplicationComposition { + public static final String SCHEMA_URI_APP2_2013 = "http://www.smpte-ra.org/schemas/2067-20/2013"; + public static final String SCHEMA_URI_APP2_2016 = "http://www.smpte-ra.org/schemas/2067-20/2016"; public static final Integer MAX_IMAGE_FRAME_WIDTH = 1920; public static final Integer MAX_IMAGE_FRAME_HEIGHT = 1080; public static final SetprogressiveSampleRateSupported = Collections.unmodifiableSet(new HashSet() {{ diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/Application2ExtendedComposition.java b/src/main/java/com/netflix/imflibrary/st2067_2/Application2ExtendedComposition.java index e43b9616..59438692 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/Application2ExtendedComposition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/Application2ExtendedComposition.java @@ -23,6 +23,9 @@ * A class that models Composition with Application 2Extended constraints from 2067-21 specification */ public class Application2ExtendedComposition extends AbstractApplicationComposition { + public static final String SCHEMA_URI_APP2E_2014 = "http://www.smpte-ra.org/schemas/2067-21/2014"; + public static final String SCHEMA_URI_APP2E_2016 = "http://www.smpte-ra.org/schemas/2067-21/2016"; + public static final String SCHEMA_URI_APP2E_2020 = "http://www.smpte-ra.org/ns/2067-21/2020"; public static final Integer MAX_YUV_IMAGE_FRAME_WIDTH = 3840; public static final Integer MAX_YUV_IMAGE_FRAME_HEIGHT = 2160; public static final Integer MAX_RGB_IMAGE_FRAME_WIDTH = 4096; diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/Application5Composition.java b/src/main/java/com/netflix/imflibrary/st2067_2/Application5Composition.java index 7b63c2c8..24f71648 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/Application5Composition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/Application5Composition.java @@ -44,6 +44,7 @@ * A class that models Composition with Application 5 constraints from 2067-50 specification */ public class Application5Composition extends AbstractApplicationComposition { + public static final String SCHEMA_URI_APP5_2017 = "http://www.smpte-ra.org/ns/2067-50/2017"; public static final Integer MAX_RGB_IMAGE_FRAME_WIDTH = Integer.MAX_VALUE; //TODO: 2067-50 specifies 2^32-1, would require using Long instead of Integer public static final Integer MAX_RGB_IMAGE_FRAME_HEIGHT = Integer.MAX_VALUE; //TODO: 2067-50 specifies 2^32-1, would require using Long instead of Integer public static final Map>colorToBitDepthMap = Collections.unmodifiableMap(new HashMap>() {{ diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationComposition.java b/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationComposition.java index d5c05946..0ba15c0e 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationComposition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationComposition.java @@ -106,12 +106,20 @@ public interface ApplicationComposition { public UUID getUUID(); /** - * Getter for the CoreConstraintsURI corresponding to this CompositionPlaylist + * Get the Java package string for the Core Constraints version + * @deprecated Instead use {@link #getCoreConstraintsSchema()} * - * @return the uri for the CoreConstraints schema for this CompositionPlaylist + * @return package containing the Core Constraints classes */ + @Deprecated public String getCoreConstraintsVersion(); + /** + * Getter for the Core Constraints schema URI. + * @return URI for the Core Constraints schema + */ + @Nonnull public String getCoreConstraintsSchema(); + /** * Getter for the essence VirtualTracks in this Composition * diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java b/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java index c4e28e16..b6bc84e5 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java @@ -44,6 +44,7 @@ public class ApplicationCompositionFactory { private static final Set namespacesApplication2EComposition = Collections.unmodifiableSet(new HashSet() {{ add("http://www.smpte-ra.org/schemas/2067-21/2014"); add("http://www.smpte-ra.org/schemas/2067-21/2016"); + add("http://www.smpte-ra.org/ns/2067-21/2020"); }}); private static final Set namespacesApplication5Composition = Collections.unmodifiableSet(new HashSet() {{ diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2013.java b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2013.java index 7c8b5d25..f8c4aa2e 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2013.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2013.java @@ -62,6 +62,22 @@ private CompositionModel_st2067_2_2013(){ // Parse the ApplicationIdentification values Set applicationIDs = parseApplicationIds(compositionPlaylistType, imfErrorLogger); + // Identify the Core Constraints version + String coreConstraintsSchema = CoreConstraints.fromApplicationId(applicationIDs); + if (coreConstraintsSchema == null) + { + // Get the namespaces of each Sequence being used + Set sequenceNamespaces = compositionPlaylistType.getSegmentList().getSegment().get(0) + .getSequenceList().getAny().stream().filter(JAXBElement.class::isInstance) + .map(je -> ((JAXBElement) je).getName().getNamespaceURI()).collect(Collectors.toSet()); + // Find the Core Constraints version, based on the namespaces of the Sequences + coreConstraintsSchema = CoreConstraints.fromElementNamespaces(sequenceNamespaces); + + // If all else fails, assume the minimum version applicable to this CPL version + if (coreConstraintsSchema == null) + coreConstraintsSchema = CoreConstraints.NAMESPACE_IMF_2013; + } + return new IMFCompositionPlaylistType( compositionPlaylistType.getId(), compositionPlaylistType.getEditRate(), (compositionPlaylistType.getAnnotation() == null ? null : compositionPlaylistType.getAnnotation().getValue()), @@ -71,7 +87,8 @@ private CompositionModel_st2067_2_2013(){ (compositionPlaylistType.getContentTitle() == null ? null : compositionPlaylistType.getContentTitle().getValue()), segmentList, essenceDescriptorList, - "org.smpte_ra.schemas.st2067_2_2013", applicationIDs + coreConstraintsSchema, + applicationIDs ); } diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2016.java b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2016.java index 8a871220..968e0dd5 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2016.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionModel_st2067_2_2016.java @@ -62,6 +62,22 @@ private CompositionModel_st2067_2_2016(){ // Parse the ApplicationIdentification values Set applicationIDs = parseApplicationIds(compositionPlaylistType, imfErrorLogger); + // Identify the Core Constraints version + String coreConstraintsSchema = CoreConstraints.fromApplicationId(applicationIDs); + if (coreConstraintsSchema == null) + { + // Get the namespaces of each Sequence being used + Set sequenceNamespaces = compositionPlaylistType.getSegmentList().getSegment().get(0) + .getSequenceList().getAny().stream().filter(JAXBElement.class::isInstance) + .map(je -> ((JAXBElement) je).getName().getNamespaceURI()).collect(Collectors.toSet()); + // Find the Core Constraints version, based on the namespaces of the Sequences + coreConstraintsSchema = CoreConstraints.fromElementNamespaces(sequenceNamespaces); + + // If all else fails, assume the minimum version applicable to this CPL version + if (coreConstraintsSchema == null) + coreConstraintsSchema = CoreConstraints.NAMESPACE_IMF_2016; + } + return new IMFCompositionPlaylistType( compositionPlaylistType.getId(), compositionPlaylistType.getEditRate(), (compositionPlaylistType.getAnnotation() == null ? null : compositionPlaylistType.getAnnotation().getValue()), @@ -71,7 +87,8 @@ private CompositionModel_st2067_2_2016(){ (compositionPlaylistType.getContentTitle() == null ? null : compositionPlaylistType.getContentTitle().getValue()), segmentList, essenceDescriptorList, - "org.smpte_ra.schemas.st2067_2_2016", applicationIDs + coreConstraintsSchema, + applicationIDs ); } @@ -290,7 +307,8 @@ private static JAXBContext createJAXBContext() try { return JAXBContext.newInstance( - org.smpte_ra.schemas.st2067_2_2016.ObjectFactory.class); // 2016 CPL and Core constraints + org.smpte_ra.schemas.st2067_2_2016.ObjectFactory.class, // 2016 CPL and Core constraints + org.smpte_ra.schemas.st2067_2_2020.ObjectFactory.class); // 2020 Core constraints also use 2016 CPL } catch(JAXBException e) { @@ -311,6 +329,7 @@ private static Schema createValidationSchema() InputStream xsd_dcmlTypes = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st0433_2008/dcmlTypes/dcmlTypes.xsd"); InputStream xsd_cpl_2016 = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_3_2016/imf-cpl-20160411.xsd"); InputStream xsd_core_constraints_2016 = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_2_2016/imf-core-constraints-20160411.xsd"); + InputStream xsd_core_constraints_2020 = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd") ) { // Build a schema from all of the XSD files provided @@ -320,6 +339,7 @@ private static Schema createValidationSchema() new StreamSource(xsd_dcmlTypes), new StreamSource(xsd_cpl_2016), new StreamSource(xsd_core_constraints_2016), + new StreamSource(xsd_core_constraints_2020), }); } catch(IOException | SAXException e) @@ -328,15 +348,4 @@ private static Schema createValidationSchema() } } } - - /** - * Getter for the CoreConstraintsURI corresponding to this CompositionPlaylist - * - * @return the uri for the CoreConstraints schema for this CompositionPlaylist - * @deprecated This is an instance method of a class declared final, with a private constructor. Should never be callable - */ - @Deprecated - public String getCoreConstraintsVersion() { - return "org.smpte_ra.schemas.st2067_2_2016"; - } } \ No newline at end of file diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/CoreConstraints.java b/src/main/java/com/netflix/imflibrary/st2067_2/CoreConstraints.java new file mode 100644 index 00000000..6b36d474 --- /dev/null +++ b/src/main/java/com/netflix/imflibrary/st2067_2/CoreConstraints.java @@ -0,0 +1,88 @@ +package com.netflix.imflibrary.st2067_2; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class CoreConstraints +{ + private CoreConstraints() {} // Prevent instantiation. This class is constants and utilities only + + // SMPTE ST 2067-2 version namespaces + public static final String NAMESPACE_IMF_2013 = "http://www.smpte-ra.org/schemas/2067-2/2013"; + public static final String NAMESPACE_IMF_2016 = "http://www.smpte-ra.org/schemas/2067-2/2016"; + public static final String NAMESPACE_IMF_2020 = "http://www.smpte-ra.org/ns/2067-2/2020"; + + static final List SUPPORTED_NAMESPACES = Collections.unmodifiableList(Arrays.asList( + NAMESPACE_IMF_2013, NAMESPACE_IMF_2016, NAMESPACE_IMF_2020)); + + /** + * @deprecated Remove once all deprecated, package-based, 'getCoreConstraintsVersion' methods are removed. + */ + @Deprecated + static String packageFromSchema(String coreConstraintsSchema) + { + if (coreConstraintsSchema.equals(NAMESPACE_IMF_2013)) + return "org.smpte_ra.schemas.st2067_2_2013"; + else if (coreConstraintsSchema.equals(NAMESPACE_IMF_2016)) + return "org.smpte_ra.schemas.st2067_2_2016"; + else if (coreConstraintsSchema.equals(NAMESPACE_IMF_2020)) + return "org.smpte_ra.schemas.st2067_2_2020"; + else + return coreConstraintsSchema; // No mapping, just return the schema value + } + + // Determine the highest Core Constraints version based on the ApplicationIds used + @Nullable public static String fromApplicationId(@Nonnull Collection applicationIds) + { + // NOTE- When adding new namespaces or core constraint versions, be sure that the most recent core constraints + // are checked first. That way if there are multiple ApplicationIdentifications, the newest version is returned. + if (applicationIds.contains(Application2ExtendedComposition.SCHEMA_URI_APP2E_2020)) + { + return CoreConstraints.NAMESPACE_IMF_2020; + } + else if (applicationIds.contains(Application5Composition.SCHEMA_URI_APP5_2017) + || applicationIds.contains(Application2ExtendedComposition.SCHEMA_URI_APP2E_2016) + || applicationIds.contains(Application2Composition.SCHEMA_URI_APP2_2016)) + { + return CoreConstraints.NAMESPACE_IMF_2016; + } + else if (applicationIds.contains(Application2ExtendedComposition.SCHEMA_URI_APP2E_2014) + || applicationIds.contains(Application2Composition.SCHEMA_URI_APP2_2013)) + { + return CoreConstraints.NAMESPACE_IMF_2013; + } + else + { + return null; + } + } + + // Determine the most recent core constraints version, based on a collection of element namespaces + static String fromElementNamespaces(@Nonnull Collection namespaces) + { + // NOTE- When adding new namespaces or core constraint versions, be sure that the most recent core constraints + // are checked first. That way if there are multiple different namespaces, the newest version is returned. + if (namespaces.contains(CoreConstraints.NAMESPACE_IMF_2020)) + { + return CoreConstraints.NAMESPACE_IMF_2020; + } + else if (namespaces.contains(CoreConstraints.NAMESPACE_IMF_2016)) + { + return CoreConstraints.NAMESPACE_IMF_2016; + } + else if (namespaces.contains(CoreConstraints.NAMESPACE_IMF_2013)) + { + return CoreConstraints.NAMESPACE_IMF_2013; + } + else + { + // TODO- Consider identify core constraints based on other namespaces + // Example- IABSequence "http://www.smpte-ra.org/ns/2067-201/2019" requires core constraints ST 2067-2:2016 + return null; + } + } +} diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java b/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java index b01b52d5..6ddb1579 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java @@ -60,7 +60,7 @@ final class IMFCompositionPlaylistType { private final List segmentList; private final List essenceDescriptorList; private final IMFErrorLogger imfErrorLogger; - private final String coreConstraintsVersion; + private final String coreConstraintsSchema; private final Set applicationIdSet; /** @@ -79,10 +79,10 @@ public IMFCompositionPlaylistType(String id, String contentTitle, List segmentList, List essenceDescriptorList, - String coreConstraintsVersion, + String coreConstraintsSchema, String applicationId) { - this(id, editRate, annotation, issuer, creator, contentOriginator, contentTitle, segmentList, essenceDescriptorList, coreConstraintsVersion, (applicationId == null ? new HashSet<>() : new HashSet(Arrays.asList(applicationId)))); + this(id, editRate, annotation, issuer, creator, contentOriginator, contentTitle, segmentList, essenceDescriptorList, coreConstraintsSchema, (applicationId == null ? new HashSet<>() : new HashSet(Arrays.asList(applicationId)))); } public IMFCompositionPlaylistType(String id, @@ -94,7 +94,7 @@ public IMFCompositionPlaylistType(String id, String contentTitle, List segmentList, List essenceDescriptorList, - String coreConstraintsVersion, + String coreConstraintsSchema, @Nonnull Set applicationIds) { this.id = UUIDHelper.fromUUIDAsURNStringToUUID(id); @@ -117,7 +117,7 @@ public IMFCompositionPlaylistType(String id, this.contentTitle = contentTitle; this.segmentList = Collections.unmodifiableList(segmentList); this.essenceDescriptorList = Collections.unmodifiableList(essenceDescriptorList); - this.coreConstraintsVersion = coreConstraintsVersion; + this.coreConstraintsSchema = coreConstraintsSchema; this.applicationIdSet = Collections.unmodifiableSet(applicationIds); if(imfErrorLogger.hasFatalErrors()) @@ -318,12 +318,12 @@ public List getEssenceDescriptorList(){ } /** - * Getter for the CoreConstraintsURI corresponding to this CompositionPlaylist + * Getter for the CoreConstraints URI corresponding to this CompositionPlaylist * * @return the uri for the CoreConstraints schema for this CompositionPlaylist */ - public String getCoreConstraintsVersion() { - return this.coreConstraintsVersion; + @Nonnull public String getCoreConstraintsSchema() { + return this.coreConstraintsSchema; } /** diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/IMFCoreConstraintsChecker.java b/src/main/java/com/netflix/imflibrary/st2067_2/IMFCoreConstraintsChecker.java index 66120f3a..5998f9d1 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/IMFCoreConstraintsChecker.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/IMFCoreConstraintsChecker.java @@ -274,7 +274,11 @@ else if( virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum. } //Section 6.3.2 st2067-2:2016 and Section 6.9.3 st2067-3:2016 - if(!foundMainAudioEssence){ + //Section 6.3.2 st2067-2:2020 allows CPLs without Audio Virtual Tracks + if(!foundMainAudioEssence + && (compositionPlaylistType.getCoreConstraintsSchema().equals(CoreConstraints.NAMESPACE_IMF_2013) + || compositionPlaylistType.getCoreConstraintsSchema().equals(CoreConstraints.NAMESPACE_IMF_2016))) + { imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format("The Composition represented by Id %s does not contain a single audio essence in its first segment, one or more is required", compositionPlaylistType.getId().toString())); } diff --git a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2013.java b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2013.java index 91e972a5..57e47e12 100755 --- a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2013.java +++ b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2013.java @@ -22,6 +22,7 @@ import com.netflix.imflibrary.IMFErrorLoggerImpl; import com.netflix.imflibrary.exceptions.IMFAuthoringException; import com.netflix.imflibrary.st2067_2.Composition; +import com.netflix.imflibrary.st2067_2.CoreConstraints; import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack; import com.netflix.imflibrary.st2067_2.IMFEssenceDescriptorBaseType; import com.netflix.imflibrary.st2067_2.IMFMarkerResourceType; @@ -36,7 +37,6 @@ import org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType; import org.smpte_ra.schemas.st2067_2_2013.CompositionTimecodeType; import org.smpte_ra.schemas.st2067_2_2013.ContentVersionType; -import org.smpte_ra.schemas.st2067_2_2013.SequenceType; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; @@ -63,10 +63,12 @@ import java.math.BigInteger; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -94,10 +96,9 @@ public class CompositionPlaylistBuilder_2013 { public final static String defaultHashAlgorithm = "http://www.w3.org/2000/09/xmldsig#sha1"; private final static String defaultContentKindScope = "http://www.smpte-ra.org/schemas/2067-3/XXXX#content-kind"; private final String cplFileName; - - private final String applicationId; - Map trackResourceSourceEncodingMap; - + private final String coreConstraintsSchema; + private final Set applicationIds; + private final Map trackResourceSourceEncodingMap; /** * A constructor for CompositionPlaylistBuilder class to build a CompositionPlaylist document compliant with st2067-2:2013 schema @@ -107,11 +108,12 @@ public class CompositionPlaylistBuilder_2013 { * @param creator a free form human readable text describing the tool used to create the CompositionPlaylist document * @param virtualTracks a list of VirtualTracks of the Composition * @param compositionEditRate the edit rate of the Composition - * @param applicationId ApplicationId for the composition + * @param applicationIds ApplicationIds for the composition * @param totalRunningTime a long value representing in seconds the total running time of this composition * @param trackFileInfoMap a map of the IMFTrackFile's UUID to the track file info * @param workingDirectory a folder location where the constructed CPL document can be written to * @param imfEssenceDescriptorBaseTypeList List of IMFEssenceDescriptorBaseType + * @param coreConstraintsSchema schema defining core constraints version */ public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType annotationText, @@ -119,29 +121,29 @@ public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType creator, @Nonnull List virtualTracks, @Nonnull Composition.EditRate compositionEditRate, - @Nonnull String applicationId, + @Nonnull Set applicationIds, long totalRunningTime, @Nonnull Map trackFileInfoMap, @Nonnull File workingDirectory, - @Nonnull List imfEssenceDescriptorBaseTypeList){ + @Nonnull List imfEssenceDescriptorBaseTypeList, + @Nonnull String coreConstraintsSchema){ this.uuid = uuid; this.annotationText = annotationText; this.issuer = issuer; this.creator = creator; this.issueDate = IMFUtils.createXMLGregorianCalendar(); this.virtualTracks = Collections.unmodifiableList(virtualTracks); - List editRate = new ArrayList() {{add(compositionEditRate.getNumerator()); - add(compositionEditRate.getDenominator());}}; - this.compositionEditRate = Collections.unmodifiableList(editRate); + this.compositionEditRate = Collections.unmodifiableList(Arrays.asList(compositionEditRate.getNumerator(), compositionEditRate.getDenominator())); this.totalRunningTime = totalRunningTime; this.trackFileInfoMap = Collections.unmodifiableMap(trackFileInfoMap); this.workingDirectory = workingDirectory; this.imfErrorLogger = new IMFErrorLoggerImpl(); - cplFileName = "CPL-" + this.uuid.toString() + ".xml"; - this.applicationId = applicationId; - this.trackResourceSourceEncodingMap = new HashMap<>();//Map of TrackFileId -> SourceEncodingElement of each resource of this VirtualTrack + this.cplFileName = "CPL-" + this.uuid.toString() + ".xml"; + this.applicationIds = Collections.unmodifiableSet(applicationIds); this.imfEssenceDescriptorBaseTypeList = Collections.unmodifiableList(imfEssenceDescriptorBaseTypeList); + this.coreConstraintsSchema = coreConstraintsSchema; + Map trackEncodingMap = new HashMap<>(); //Map of TrackFileId -> SourceEncodingElement of each resource of this VirtualTrack for(Composition.VirtualTrack virtualTrack : virtualTracks) { if (!(virtualTrack instanceof IMFEssenceComponentVirtualTrack)) { continue; // Skip non-essence tracks @@ -149,13 +151,42 @@ public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, IMFEssenceComponentVirtualTrack essenceTrack = (IMFEssenceComponentVirtualTrack) virtualTrack; for (IMFTrackFileResourceType trackResource : essenceTrack.getTrackFileResourceList()) { - UUID sourceEncoding = trackResourceSourceEncodingMap.get(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId())); + UUID sourceEncoding = trackEncodingMap.get(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId())); if (sourceEncoding == null) { - trackResourceSourceEncodingMap.put(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId()), UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getSourceEncoding())); + trackEncodingMap.put(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId()), UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getSourceEncoding())); } } } + this.trackResourceSourceEncodingMap = Collections.unmodifiableMap(trackEncodingMap); + } + /** + * A constructor for CompositionPlaylistBuilder class to build a CompositionPlaylist document compliant with st2067-2:2013 schema + * @param uuid identifying the CompositionPlaylist document + * @param annotationText a free form human readable text + * @param issuer a free form human readable text describing the issuer of the CompositionPlaylist document + * @param creator a free form human readable text describing the tool used to create the CompositionPlaylist document + * @param virtualTracks a list of VirtualTracks of the Composition + * @param compositionEditRate the edit rate of the Composition + * @param applicationId ApplicationId for the composition + * @param totalRunningTime a long value representing in seconds the total running time of this composition + * @param trackFileInfoMap a map of the IMFTrackFile's UUID to the track file info + * @param workingDirectory a folder location where the constructed CPL document can be written to + * @param imfEssenceDescriptorBaseTypeList List of IMFEssenceDescriptorBaseType + */ + @Deprecated + public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, + @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType annotationText, + @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType issuer, + @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType creator, + @Nonnull List virtualTracks, + @Nonnull Composition.EditRate compositionEditRate, + @Nonnull String applicationId, + long totalRunningTime, + @Nonnull Map trackFileInfoMap, + @Nonnull File workingDirectory, + @Nonnull List imfEssenceDescriptorBaseTypeList){ + this(uuid, annotationText, issuer, creator, virtualTracks, compositionEditRate, Collections.singleton(applicationId), totalRunningTime, trackFileInfoMap, workingDirectory, imfEssenceDescriptorBaseTypeList, CoreConstraints.NAMESPACE_IMF_2013); } @@ -172,6 +203,7 @@ public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, * @param trackFileInfoMap a map of the IMFTrackFile's UUID to the track file info * @param workingDirectory a folder location where the constructed CPL document can be written to */ + @Deprecated public CompositionPlaylistBuilder_2013(@Nonnull UUID uuid, @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType annotationText, @Nonnull org.smpte_ra.schemas.st2067_2_2013.UserTextType issuer, @@ -241,24 +273,15 @@ public List build() throws IOException, ParserConfigura cplRoot.setSegmentList(buildSegmentList(new ArrayList(){{add(segmentType);}})); cplRoot.setSigner(null); cplRoot.setSignature(null); - try { - String nodeString = "" + - this.applicationId + - ""; - - Element element = DocumentBuilderFactory - .newInstance() - .newDocumentBuilder() - .parse(new ByteArrayInputStream(nodeString.getBytes("UTF-8"))) - .getDocumentElement(); + if (!this.applicationIds.isEmpty()) + { + JAXBElement> appIdElement = new org.smpte_ra.schemas.st2067_2_2013.ObjectFactory() + .createApplicationIdentification(new ArrayList<>(this.applicationIds)); + org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType.ExtensionProperties extensionProperties = new org.smpte_ra.schemas.st2067_2_2013.CompositionPlaylistType.ExtensionProperties(); - extensionProperties.getAny().add(element); + extensionProperties.getAny().add(appIdElement); cplRoot.setExtensionProperties( extensionProperties); } - catch(SAXException ex) { - imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, - "Failed to create DOM node for ApplicationIdentification"); - } File outputFile = new File(this.workingDirectory + File.separator + this.cplFileName); List errors = serializeCPLToXML(cplRoot, outputFile); diff --git a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java index 9b230e83..a9eda2dd 100755 --- a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java +++ b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java @@ -21,6 +21,7 @@ import com.netflix.imflibrary.IMFErrorLoggerImpl; import com.netflix.imflibrary.exceptions.IMFAuthoringException; import com.netflix.imflibrary.st2067_2.Composition; +import com.netflix.imflibrary.st2067_2.CoreConstraints; import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack; import com.netflix.imflibrary.st2067_2.IMFEssenceDescriptorBaseType; import com.netflix.imflibrary.st2067_2.IMFMarkerResourceType; @@ -37,7 +38,7 @@ import org.smpte_ra.schemas.st2067_2_2016.ContentVersionType; import org.smpte_ra.schemas.st2067_2_2016.EssenceDescriptorBaseType; import org.smpte_ra.schemas.st2067_2_2016.SegmentType; -import org.smpte_ra.schemas.st2067_2_2016.SequenceType; +import org.smpte_ra.schemas.st2067_2_2016.UserTextType; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; @@ -64,10 +65,12 @@ import java.math.BigInteger; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -94,23 +97,25 @@ public class CompositionPlaylistBuilder_2016 { public final static String defaultHashAlgorithm = "http://www.w3.org/2000/09/xmldsig#sha1"; private final static String defaultContentKindScope = "http://www.smpte-ra.org/schemas/2067-3/XXXX#content-kind"; private final String cplFileName; - private final String applicationId; - Map trackResourceSourceEncodingMap; + private final Set applicationIds; + private final String coreConstraintsSchema; + private final Map trackResourceSourceEncodingMap; /** - * A constructor for CompositionPlaylistBuilder class to build a CompositionPlaylist document compliant with st2067-2:2013 schema + * A constructor for CompositionPlaylistBuilder class to build a CompositionPlaylist document compliant with st2067-2:2016 schema * @param uuid identifying the CompositionPlaylist document * @param annotationText a free form human readable text * @param issuer a free form human readable text describing the issuer of the CompositionPlaylist document * @param creator a free form human readable text describing the tool used to create the CompositionPlaylist document * @param virtualTracks a list of VirtualTracks of the Composition * @param compositionEditRate the edit rate of the Composition - * @param applicationId ApplicationId for the composition + * @param applicationIds ApplicationIds for the composition * @param totalRunningTime a long value representing in seconds the total running time of this composition * @param trackFileInfoMap a map of the IMFTrackFile's UUID to the track file info * @param workingDirectory a folder location where the constructed CPL document can be written to * @param imfEssenceDescriptorBaseTypeList List of IMFEssenceDescriptorBaseType + * @param coreConstraintsSchema schema defining core constraints version */ public CompositionPlaylistBuilder_2016(@Nonnull UUID uuid, @Nonnull org.smpte_ra.schemas.st2067_2_2016.UserTextType annotationText, @@ -118,29 +123,29 @@ public CompositionPlaylistBuilder_2016(@Nonnull UUID uuid, @Nonnull org.smpte_ra.schemas.st2067_2_2016.UserTextType creator, @Nonnull List virtualTracks, @Nonnull Composition.EditRate compositionEditRate, - @Nonnull String applicationId, + @Nonnull Set applicationIds, long totalRunningTime, @Nonnull Map trackFileInfoMap, @Nonnull File workingDirectory, - @Nonnull List imfEssenceDescriptorBaseTypeList){ + @Nonnull List imfEssenceDescriptorBaseTypeList, + @Nonnull String coreConstraintsSchema){ this.uuid = uuid; this.annotationText = annotationText; this.issuer = issuer; this.creator = creator; this.issueDate = IMFUtils.createXMLGregorianCalendar(); this.virtualTracks = Collections.unmodifiableList(virtualTracks); - List editRate = new ArrayList() {{add(compositionEditRate.getNumerator()); - add(compositionEditRate.getDenominator());}}; - this.compositionEditRate = Collections.unmodifiableList(editRate); + this.compositionEditRate = Collections.unmodifiableList(Arrays.asList(compositionEditRate.getNumerator(), compositionEditRate.getDenominator())); this.totalRunningTime = totalRunningTime; this.trackFileInfoMap = Collections.unmodifiableMap(trackFileInfoMap); this.workingDirectory = workingDirectory; this.imfErrorLogger = new IMFErrorLoggerImpl(); - cplFileName = "CPL-" + this.uuid.toString() + ".xml"; - this.applicationId = applicationId; - this.trackResourceSourceEncodingMap = new HashMap<>();//Map of TrackFileId -> SourceEncodingElement of each resource of this VirtualTrack + this.cplFileName = "CPL-" + this.uuid.toString() + ".xml"; + this.applicationIds = Collections.unmodifiableSet(applicationIds); this.imfEssenceDescriptorBaseTypeList = Collections.unmodifiableList(imfEssenceDescriptorBaseTypeList); + this.coreConstraintsSchema = coreConstraintsSchema; + Map trackEncodingMap = new HashMap<>(); //Map of TrackFileId -> SourceEncodingElement of each resource of this VirtualTrack for(Composition.VirtualTrack virtualTrack : virtualTracks) { if (!(virtualTrack instanceof IMFEssenceComponentVirtualTrack)) { continue; // Skip non-essence tracks @@ -148,13 +153,43 @@ public CompositionPlaylistBuilder_2016(@Nonnull UUID uuid, IMFEssenceComponentVirtualTrack essenceTrack = (IMFEssenceComponentVirtualTrack) virtualTrack; for (IMFTrackFileResourceType trackResource : essenceTrack.getTrackFileResourceList()) { - UUID sourceEncoding = trackResourceSourceEncodingMap.get(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId())); + UUID sourceEncoding = trackEncodingMap.get(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId())); if (sourceEncoding == null) { - trackResourceSourceEncodingMap.put(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId()), UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getSourceEncoding())); + trackEncodingMap.put(UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getTrackFileId()), UUIDHelper.fromUUIDAsURNStringToUUID(trackResource.getSourceEncoding())); } } } + this.trackResourceSourceEncodingMap = Collections.unmodifiableMap(trackEncodingMap); + } + /** + * @deprecated Instead use {{@link #CompositionPlaylistBuilder_2016(UUID, UserTextType, UserTextType, UserTextType, List, Composition.EditRate, Set, long, Map, File, List, String)}} + * A constructor for CompositionPlaylistBuilder class to build a CompositionPlaylist document compliant with st2067-2:2016 schema + * @param uuid identifying the CompositionPlaylist document + * @param annotationText a free form human readable text + * @param issuer a free form human readable text describing the issuer of the CompositionPlaylist document + * @param creator a free form human readable text describing the tool used to create the CompositionPlaylist document + * @param virtualTracks a list of VirtualTracks of the Composition + * @param compositionEditRate the edit rate of the Composition + * @param applicationId ApplicationId for the composition + * @param totalRunningTime a long value representing in seconds the total running time of this composition + * @param trackFileInfoMap a map of the IMFTrackFile's UUID to the track file info + * @param workingDirectory a folder location where the constructed CPL document can be written to + * @param imfEssenceDescriptorBaseTypeList List of IMFEssenceDescriptorBaseType + */ + @Deprecated + public CompositionPlaylistBuilder_2016(@Nonnull UUID uuid, + @Nonnull org.smpte_ra.schemas.st2067_2_2016.UserTextType annotationText, + @Nonnull org.smpte_ra.schemas.st2067_2_2016.UserTextType issuer, + @Nonnull org.smpte_ra.schemas.st2067_2_2016.UserTextType creator, + @Nonnull List virtualTracks, + @Nonnull Composition.EditRate compositionEditRate, + @Nonnull String applicationId, + long totalRunningTime, + @Nonnull Map trackFileInfoMap, + @Nonnull File workingDirectory, + @Nonnull List imfEssenceDescriptorBaseTypeList){ + this(uuid, annotationText, issuer, creator, virtualTracks, compositionEditRate, Collections.singleton(applicationId), totalRunningTime, trackFileInfoMap, workingDirectory, imfEssenceDescriptorBaseTypeList, CoreConstraints.NAMESPACE_IMF_2016); } /** @@ -217,24 +252,25 @@ public List build() throws IOException, ParserConfigura cplRoot.setSegmentList(buildSegmentList(new ArrayList(){{add(segmentType);}})); cplRoot.setSigner(null); cplRoot.setSignature(null); - try { - String nodeString = "" + - this.applicationId + - ""; - - Element element = DocumentBuilderFactory - .newInstance() - .newDocumentBuilder() - .parse(new ByteArrayInputStream(nodeString.getBytes("UTF-8"))) - .getDocumentElement(); + + if (!this.applicationIds.isEmpty()) + { + JAXBElement> appIdElement; + if (this.coreConstraintsSchema.equals(CoreConstraints.NAMESPACE_IMF_2020)) + { + appIdElement = new org.smpte_ra.schemas.st2067_2_2020.ObjectFactory().createApplicationIdentification(new ArrayList<>(this.applicationIds)); + } + else + { + appIdElement = new org.smpte_ra.schemas.st2067_2_2016.ObjectFactory().createApplicationIdentification(new ArrayList<>(this.applicationIds)); + } + org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType.ExtensionProperties extensionProperties = new org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType.ExtensionProperties(); - extensionProperties.getAny().add(element); + extensionProperties.getAny().add(appIdElement); cplRoot.setExtensionProperties( extensionProperties); } - catch(SAXException ex) { - imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, - "Failed to create DOM node for ApplicationIdentification"); - }File outputFile = new File(this.workingDirectory + File.separator + this.cplFileName); + + File outputFile = new File(this.workingDirectory + File.separator + this.cplFileName); serializeCPLToXML(cplRoot, outputFile); return imfErrorLogger.getErrors(); } @@ -260,7 +296,7 @@ else if (virtualTrack instanceof IMFMarkerVirtualTrack) return Collections.unmodifiableList(trackResourceList); } - private void serializeCPLToXML(org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType cplRoot, File outputFile) throws IOException, JAXBException, SAXException{ + private void serializeCPLToXML(org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType cplRoot, File outputFile) throws IOException, JAXBException, SAXException{ int numErrors = imfErrorLogger.getNumberOfErrors(); boolean formatted = true; @@ -522,29 +558,56 @@ public org.smpte_ra.schemas.st2067_2_2016.SequenceType.ResourceList buildResourc */ public void populateSequenceListForSegment(List sequenceTypeTuples, org.smpte_ra.schemas.st2067_2_2016.SegmentType segment) { - - org.smpte_ra.schemas.st2067_2_2016.ObjectFactory objectFactory = new org.smpte_ra.schemas.st2067_2_2016.ObjectFactory(); List any = segment.getSequenceList().getAny(); - - for(SequenceTypeTuple sequenceTypeTuple : sequenceTypeTuples){ - switch(sequenceTypeTuple.getSequenceType()){ - case MainImageSequence: - any.add(objectFactory.createMainImageSequence(sequenceTypeTuple.getSequence())); - break; - case MainAudioSequence: - any.add(objectFactory.createMainAudioSequence(sequenceTypeTuple.getSequence())); - break; - case IABSequence: - any.add(objectFactory.createIABSequence(sequenceTypeTuple.getSequence())); - break; - case MarkerSequence: - segment.getSequenceList().setMarkerSequence(sequenceTypeTuple.getSequence()); - break; - default: - throw new IMFAuthoringException(String.format("Currently we only support %s, %s, %s, and %s sequence types in building a Composition Playlist document, the type of sequence being requested is %s", - Composition.SequenceTypeEnum.MainAudioSequence, Composition.SequenceTypeEnum.MainImageSequence, Composition.SequenceTypeEnum.IABSequence, Composition.SequenceTypeEnum.MarkerSequence, sequenceTypeTuple.getSequenceType())); + if (this.coreConstraintsSchema.equals(CoreConstraints.NAMESPACE_IMF_2020)) + { + org.smpte_ra.schemas.st2067_2_2020.ObjectFactory objectFactory = new org.smpte_ra.schemas.st2067_2_2020.ObjectFactory(); + for(SequenceTypeTuple sequenceTypeTuple : sequenceTypeTuples){ + switch(sequenceTypeTuple.getSequenceType()){ + case MainImageSequence: + any.add(objectFactory.createMainImageSequence(sequenceTypeTuple.getSequence())); + break; + case MainAudioSequence: + any.add(objectFactory.createMainAudioSequence(sequenceTypeTuple.getSequence())); + break; + case IABSequence: + // JAXB class for IABSequence was generated in the CC 2016 package. Use that + any.add(new org.smpte_ra.schemas.st2067_2_2016.ObjectFactory() + .createIABSequence(sequenceTypeTuple.getSequence())); + break; + case MarkerSequence: + segment.getSequenceList().setMarkerSequence(sequenceTypeTuple.getSequence()); + break; + default: + throw new IMFAuthoringException(String.format("Currently we only support %s, %s, %s, and %s sequence types in building a Composition Playlist document, the type of sequence being requested is %s", + Composition.SequenceTypeEnum.MainAudioSequence, Composition.SequenceTypeEnum.MainImageSequence, Composition.SequenceTypeEnum.IABSequence, Composition.SequenceTypeEnum.MarkerSequence, sequenceTypeTuple.getSequenceType())); + } } } + else + { + org.smpte_ra.schemas.st2067_2_2016.ObjectFactory objectFactory = new org.smpte_ra.schemas.st2067_2_2016.ObjectFactory(); + for(SequenceTypeTuple sequenceTypeTuple : sequenceTypeTuples){ + switch(sequenceTypeTuple.getSequenceType()){ + case MainImageSequence: + any.add(objectFactory.createMainImageSequence(sequenceTypeTuple.getSequence())); + break; + case MainAudioSequence: + any.add(objectFactory.createMainAudioSequence(sequenceTypeTuple.getSequence())); + break; + case IABSequence: + any.add(objectFactory.createIABSequence(sequenceTypeTuple.getSequence())); + break; + case MarkerSequence: + segment.getSequenceList().setMarkerSequence(sequenceTypeTuple.getSequence()); + break; + default: + throw new IMFAuthoringException(String.format("Currently we only support %s, %s, %s, and %s sequence types in building a Composition Playlist document, the type of sequence being requested is %s", + Composition.SequenceTypeEnum.MainAudioSequence, Composition.SequenceTypeEnum.MainImageSequence, Composition.SequenceTypeEnum.IABSequence, Composition.SequenceTypeEnum.MarkerSequence, sequenceTypeTuple.getSequenceType())); + } + } + } + } /** diff --git a/src/main/java/com/netflix/imflibrary/writerTools/IMPBuilder.java b/src/main/java/com/netflix/imflibrary/writerTools/IMPBuilder.java index d1362896..fdbfbd3e 100755 --- a/src/main/java/com/netflix/imflibrary/writerTools/IMPBuilder.java +++ b/src/main/java/com/netflix/imflibrary/writerTools/IMPBuilder.java @@ -11,6 +11,7 @@ import com.netflix.imflibrary.st0377.header.InterchangeObject; import com.netflix.imflibrary.st2067_2.AbstractApplicationComposition; import com.netflix.imflibrary.st2067_2.Composition; +import com.netflix.imflibrary.st2067_2.CoreConstraints; import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack; import com.netflix.imflibrary.st2067_2.IMFEssenceDescriptorBaseType; import com.netflix.imflibrary.st2067_2.IMFTrackFileResourceType; @@ -40,6 +41,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -133,6 +135,10 @@ public static List buildIMP_2013(@Nonnull String annota IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); int numErrors = imfErrorLogger.getNumberOfErrors(); UUID cplUUID = IMFUUIDGenerator.getInstance().generateUUID(); + Set applicationIds = Collections.singleton(applicationId); + String coreConstraintsSchema = CoreConstraints.fromApplicationId(applicationIds); + if (coreConstraintsSchema == null) + coreConstraintsSchema = CoreConstraints.NAMESPACE_IMF_2013; Composition.VirtualTrack mainImageVirtualTrack = null; for(Composition.VirtualTrack virtualTrack : virtualTracks){ @@ -177,11 +183,12 @@ public static List buildIMP_2013(@Nonnull String annota CompositionPlaylistBuilder_2013.buildCPLUserTextType_2013("Photon PackingListBuilder", "en"), virtualTracks, compositionEditRate, - applicationId, + applicationIds, totalRunningTime, trackFileInfoMap, workingDirectory, - imfEssenceDescriptorBaseTypeList); + imfEssenceDescriptorBaseTypeList, + coreConstraintsSchema); imfErrorLogger.addAllErrors(compositionPlaylistBuilder_2013.build()); @@ -372,6 +379,10 @@ public static List buildIMP_2016(@Nonnull String annota IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); int numErrors = imfErrorLogger.getNumberOfErrors(); UUID cplUUID = IMFUUIDGenerator.getInstance().generateUUID(); + Set applicationIds = Collections.singleton(applicationId); + String coreConstraintsSchema = CoreConstraints.fromApplicationId(applicationIds); + if (coreConstraintsSchema == null) + coreConstraintsSchema = CoreConstraints.NAMESPACE_IMF_2016; Composition.VirtualTrack mainImageVirtualTrack = null; for(Composition.VirtualTrack virtualTrack : virtualTracks){ @@ -416,11 +427,12 @@ public static List buildIMP_2016(@Nonnull String annota CompositionPlaylistBuilder_2016.buildCPLUserTextType_2016("Photon PackingListBuilder", "en"), virtualTracks, compositionEditRate, - applicationId, + applicationIds, totalRunningTime, trackFileInfoMap, workingDirectory, - imfEssenceDescriptorBaseTypeList); + imfEssenceDescriptorBaseTypeList, + coreConstraintsSchema); imfErrorLogger.addAllErrors(compositionPlaylistBuilder_2016.build()); From 6d5cc46bf570eb2ed59794fdb785ca6333790a3d Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 6 Aug 2020 14:35:17 -0700 Subject: [PATCH 3/3] Added test cases for parsing and authoring CPLs adhering to the 2020 version of the IMF spec. Fixed a bug when serializing CPLs that referenced Core Constraints 2020 --- .../CompositionPlaylistBuilder_2016.java | 9 +- .../imflibrary/st2067_2/CompositionTest.java | 31 ++ .../writerTools/IMPBuilderFunctionalTest.java | 30 +- .../IMF-2020/CPL-2016_no-audio-track.xml | 211 +++++++++++++ .../IMF-2020/CPL-2020_no-audio-track.xml | 211 +++++++++++++ .../CPL-2020_updated-core-constraints.xml | 276 ++++++++++++++++++ 6 files changed, 762 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/TestIMP/IMF-2020/CPL-2016_no-audio-track.xml create mode 100644 src/test/resources/TestIMP/IMF-2020/CPL-2020_no-audio-track.xml create mode 100644 src/test/resources/TestIMP/IMF-2020/CPL-2020_updated-core-constraints.xml diff --git a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java index a9eda2dd..55cd48c4 100755 --- a/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java +++ b/src/main/java/com/netflix/imflibrary/writerTools/CompositionPlaylistBuilder_2016.java @@ -296,6 +296,7 @@ else if (virtualTrack instanceof IMFMarkerVirtualTrack) return Collections.unmodifiableList(trackResourceList); } + // TODO- Refactor this and consolidate all marshall/unmarshall logic into CompositionModel_st2067_2_2016.java private void serializeCPLToXML(org.smpte_ra.schemas.st2067_2_2016.CompositionPlaylistType cplRoot, File outputFile) throws IOException, JAXBException, SAXException{ int numErrors = imfErrorLogger.getNumberOfErrors(); @@ -306,18 +307,22 @@ private void serializeCPLToXML(org.smpte_ra.schemas.st2067_2_2016.CompositionPla InputStream dcmlSchemaAsAStream = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st0433_2008/dcmlTypes/dcmlTypes.xsd"); InputStream cplSchemaAsAStream = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_3_2016/imf-cpl-20160411.xsd"); InputStream coreConstraintsSchemaAsAStream = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_2_2016/imf-core-constraints-20160411.xsd"); + InputStream xsd_core_constraints_2020 = contextClassLoader.getResourceAsStream("org/smpte_ra/schemas/st2067_2_2020/imf-core-constraints-2020.xsd"); OutputStream outputStream = new FileOutputStream(outputFile) ) { SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI ); - StreamSource[] schemaSources = new StreamSource[4]; + StreamSource[] schemaSources = new StreamSource[5]; schemaSources[0] = new StreamSource(dsigSchemaAsAStream); schemaSources[1] = new StreamSource(dcmlSchemaAsAStream); schemaSources[2] = new StreamSource(cplSchemaAsAStream); schemaSources[3] = new StreamSource(coreConstraintsSchemaAsAStream); + schemaSources[4] = new StreamSource(xsd_core_constraints_2020); Schema schema = schemaFactory.newSchema(schemaSources); - JAXBContext jaxbContext = JAXBContext.newInstance("org.smpte_ra.schemas.st2067_2_2016"); + JAXBContext jaxbContext = JAXBContext.newInstance( + org.smpte_ra.schemas.st2067_2_2016.ObjectFactory.class, // 2016 CPL and Core constraints + org.smpte_ra.schemas.st2067_2_2020.ObjectFactory.class); // 2020 Core constraints also use 2016 CPL Marshaller marshaller = jaxbContext.createMarshaller(); ValidationEventHandlerImpl validationEventHandler = new ValidationEventHandlerImpl(true); marshaller.setEventHandler(validationEventHandler); diff --git a/src/test/java/com/netflix/imflibrary/st2067_2/CompositionTest.java b/src/test/java/com/netflix/imflibrary/st2067_2/CompositionTest.java index 99a0ac0d..9c8aff1b 100644 --- a/src/test/java/com/netflix/imflibrary/st2067_2/CompositionTest.java +++ b/src/test/java/com/netflix/imflibrary/st2067_2/CompositionTest.java @@ -333,4 +333,35 @@ public void compositionWithMultipleApplicationIdentificationFullyNegativeTest() ApplicationCompositionFactory.getApplicationComposition(inputFile, imfErrorLogger); Assert.assertEquals(imfErrorLogger.getErrors().size(), 2); } + + @Test + public void composition2020Test() throws IOException { + File inputFile = TestHelper.findResourceByPath + ("TestIMP/IMF-2020/CPL-2020_updated-core-constraints.xml"); + IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); + ApplicationComposition applicationComposition = ApplicationCompositionFactory.getApplicationComposition(inputFile, imfErrorLogger); + Assert.assertEquals(imfErrorLogger.getErrors().size(), 0); + Assert.assertEquals(applicationComposition.getCoreConstraintsSchema(), CoreConstraints.NAMESPACE_IMF_2020); + } + + @Test + public void composition2020WithoutAudioTrackTest() throws IOException { + File inputFile = TestHelper.findResourceByPath + ("TestIMP/IMF-2020/CPL-2020_no-audio-track.xml"); + IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); + ApplicationComposition applicationComposition = ApplicationCompositionFactory.getApplicationComposition(inputFile, imfErrorLogger); + Assert.assertEquals(imfErrorLogger.getErrors().size(), 0); + Assert.assertEquals(applicationComposition.getCoreConstraintsSchema(), CoreConstraints.NAMESPACE_IMF_2020); + } + + @Test + public void composition2016WithoutAudioTrackNegativeTest() throws IOException { + File inputFile = TestHelper.findResourceByPath + ("TestIMP/IMF-2020/CPL-2016_no-audio-track.xml"); + IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); + ApplicationComposition applicationComposition = ApplicationCompositionFactory.getApplicationComposition(inputFile, imfErrorLogger); + Assert.assertTrue(imfErrorLogger.getErrors().stream().anyMatch(e -> + e.getErrorCode() == IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR)); + Assert.assertNull(applicationComposition); + } } diff --git a/src/test/java/com/netflix/imflibrary/writerTools/IMPBuilderFunctionalTest.java b/src/test/java/com/netflix/imflibrary/writerTools/IMPBuilderFunctionalTest.java index 707e67c9..815300b0 100755 --- a/src/test/java/com/netflix/imflibrary/writerTools/IMPBuilderFunctionalTest.java +++ b/src/test/java/com/netflix/imflibrary/writerTools/IMPBuilderFunctionalTest.java @@ -27,6 +27,7 @@ import com.netflix.imflibrary.st0377.header.GenericPackage; import com.netflix.imflibrary.st0377.header.Preface; import com.netflix.imflibrary.st0377.header.SourcePackage; +import com.netflix.imflibrary.st2067_2.Application2ExtendedComposition; import com.netflix.imflibrary.st2067_2.ApplicationComposition; import com.netflix.imflibrary.st2067_2.ApplicationCompositionFactory; import com.netflix.imflibrary.utils.ByteArrayByteRangeProvider; @@ -76,6 +77,10 @@ private Object[][] getCplList() { {"TestIMP/Netflix_Sony_Plugfest_2015/CPL_BLACKL_202_HD_REC709_178_ENG_fe8cf2f4-1bcd-4145-8f72-6775af4038c4.xml", "2016", false, 0}, {"TestIMP/Netflix_Sony_Plugfest_2015/CPL_BLACKL_202_HD_REC709_178_ENG_fe8cf2f4-1bcd-4145-8f72-6775af4038c4_duplicate_source_encoding_element.xml", "2016", false, 0}, {"TestIMP/IAB/CompleteIMP/CPL_e0265fda-cb35-4e35-a4e4-4f44d82d2a52.xml", "2016", false, 0}, + {"TestIMP/IMF-2020/CPL-2020_no-audio-track.xml", "2020", false, 0}, + {"TestIMP/IMF-2020/CPL-2020_no-audio-track.xml", "2016", false, 1}, + {"TestIMP/Netflix_Sony_Plugfest_2015/CPL_BLACKL_202_HD_REC709_178_ENG_fe8cf2f4-1bcd-4145-8f72-6775af4038c4.xml", "2020", true, 1}, + {"TestIMP/Netflix_Sony_Plugfest_2015/CPL_BLACKL_202_HD_REC709_178_ENG_fe8cf2f4-1bcd-4145-8f72-6775af4038c4.xml", "2020", false, 0}, }; } @@ -105,7 +110,15 @@ private void buildIMPAndValidate(ApplicationComposition applicationComposition, "Netflix", applicationComposition.getVirtualTracks(), applicationComposition.getEditRate(), - "http://www.smpte-ra.org/schemas/2067-21/2016", + Application2ExtendedComposition.SCHEMA_URI_APP2E_2016, + buildTrackFileMetadataMap(imfErrorLogger), + tempDir); + } else if (schemaVersion.equals("2020")) { + IMPBuilder.buildIMP_2016("IMP", + "Netflix", + applicationComposition.getVirtualTracks(), + applicationComposition.getEditRate(), + Application2ExtendedComposition.SCHEMA_URI_APP2E_2020, buildTrackFileMetadataMap(imfErrorLogger), tempDir); } else if (schemaVersion.equals("2013")) { @@ -113,7 +126,7 @@ private void buildIMPAndValidate(ApplicationComposition applicationComposition, "Netflix", applicationComposition.getVirtualTracks(), applicationComposition.getEditRate(), - "http://www.smpte-ra.org/schemas/2067-21/2016", + Application2ExtendedComposition.SCHEMA_URI_APP2E_2014, buildTrackFileMetadataMap(imfErrorLogger), tempDir); } @@ -123,7 +136,16 @@ private void buildIMPAndValidate(ApplicationComposition applicationComposition, "Netflix", applicationComposition.getVirtualTracks(), applicationComposition.getEditRate(), - "http://www.smpte-ra.org/schemas/2067-21/2016", + Application2ExtendedComposition.SCHEMA_URI_APP2E_2016, + buildTrackFileInfoMap(imfErrorLogger), + tempDir, + applicationComposition.getEssenceDescriptorDomNodeMap()); + } else if (schemaVersion.equals("2020")) { + IMPBuilder.buildIMP_2016("IMP", + "Netflix", + applicationComposition.getVirtualTracks(), + applicationComposition.getEditRate(), + Application2ExtendedComposition.SCHEMA_URI_APP2E_2020, buildTrackFileInfoMap(imfErrorLogger), tempDir, applicationComposition.getEssenceDescriptorDomNodeMap()); @@ -132,7 +154,7 @@ private void buildIMPAndValidate(ApplicationComposition applicationComposition, "Netflix", applicationComposition.getVirtualTracks(), applicationComposition.getEditRate(), - "http://www.smpte-ra.org/schemas/2067-21/2016", + Application2ExtendedComposition.SCHEMA_URI_APP2E_2014, buildTrackFileInfoMap(imfErrorLogger), tempDir, applicationComposition.getEssenceDescriptorDomNodeMap()); diff --git a/src/test/resources/TestIMP/IMF-2020/CPL-2016_no-audio-track.xml b/src/test/resources/TestIMP/IMF-2020/CPL-2016_no-audio-track.xml new file mode 100644 index 00000000..11767baa --- /dev/null +++ b/src/test/resources/TestIMP/IMF-2020/CPL-2016_no-audio-track.xml @@ -0,0 +1,211 @@ + + + urn:uuid:82038b2e-8fdd-485d-884e-d71b9ba19de2 + 2020-08-06T11:00:00Z + Netflix + Manually Edited + Netflix + Photon Test- CPL 2016, No audio track. This violates st2067-2:2016 6.3.2 + + + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + + urn:uuid:8674e535-879c-4042-bf9f-1780a4c0019c + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompFill + 2 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 0 + 1023 + ScanningDirection_LeftToRightTopToBottom + 0 + 0 + + 42 + 0 + + 0 + 0 + 1080 + 1920 + 1080 + 1920 + 1080 + 1920 + FullFrame + 16/9 + 24000/1001 + urn:smpte:ul:060e2b34.0401010d.04010202.03010113 + 255 + urn:smpte:ul:060e2b34.04010106.04010101.03030000 + urn:smpte:ul:060e2b34.04010101.04010101.01020000 + urn:smpte:ul:060e2b34.04010101.04010101.02020000 + 0 + + + urn:uuid:7a8500ed-bc85-4bbd-91aa-3374ce3af3ce + 259 + 1920 + 1080 + 0 + 0 + 1920 + 1080 + 0 + 0 + 3 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 01040001010503030000778888888888 + 22871e86ea86ea86bc7f007f007ee2774c774c776460036003604567d267d26761 + + + urn:smpte:ul:060e2b34.0401010d.0d010301.020c0600 + 2 + 240 + + + + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + + urn:uuid:f3c50ea8-a293-40b7-9640-bddd78faa528 + + + urn:uuid:42546c35-ab1e-44d0-a3da-8a6e7e2a7078 + urn:smpte:ul:060e2b34.0401010d.03020221.00000000 + urn:uuid:f3ebd314-3d98-43da-8b01-92de6efb31fc + IAB + IAB + en + + + 1 + 24000/1001 + 240 + urn:smpte:ul:060e2b34.0401010d.0d010301.021d0101 + 48000/1 + False + 0 + 24 + urn:smpte:ul:060e2b34.04010105.0e090604.00000000 + + + + 24000 1001 + + http://www.smpte-ra.org/schemas/2067-21/2016 + + + + urn:uuid:fde40176-48aa-44e1-bf0c-b8c5e0f85b8f + + + urn:uuid:0ded5e28-933f-405f-9c5f-5e1683814c1f + urn:uuid:86d10356-f32f-4006-8f22-acb380222a25 + + + urn:uuid:716bc90d-df98-4331-9781-bfb8aab3dc09 + 240 + 240 + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + urn:uuid:418e4d1f-f5c7-4c71-8c62-6f796446de20 + + + + + urn:uuid:893eca6a-e09d-40b9-9958-e2d06765462e + urn:uuid:914b49d7-33d5-46fc-827e-9167bfda77a3 + + + urn:uuid:6a6294d3-f5ce-4663-9c29-2098679634f2 + 24000 1001 + 240 + 240 + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + urn:uuid:17c68559-b6de-4525-93f8-17c8ae77f599 + + + + + + + diff --git a/src/test/resources/TestIMP/IMF-2020/CPL-2020_no-audio-track.xml b/src/test/resources/TestIMP/IMF-2020/CPL-2020_no-audio-track.xml new file mode 100644 index 00000000..96a9d0c2 --- /dev/null +++ b/src/test/resources/TestIMP/IMF-2020/CPL-2020_no-audio-track.xml @@ -0,0 +1,211 @@ + + + urn:uuid:5eaad89f-79d5-4795-a712-3af4753c3d50 + 2020-08-06T11:00:00Z + Netflix + Manually Edited + Netflix + Photon Test- CPL 2020, No audio track + + + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + + urn:uuid:8674e535-879c-4042-bf9f-1780a4c0019c + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompFill + 2 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 0 + 1023 + ScanningDirection_LeftToRightTopToBottom + 0 + 0 + + 42 + 0 + + 0 + 0 + 1080 + 1920 + 1080 + 1920 + 1080 + 1920 + FullFrame + 16/9 + 24000/1001 + urn:smpte:ul:060e2b34.0401010d.04010202.03010113 + 255 + urn:smpte:ul:060e2b34.04010106.04010101.03030000 + urn:smpte:ul:060e2b34.04010101.04010101.01020000 + urn:smpte:ul:060e2b34.04010101.04010101.02020000 + 0 + + + urn:uuid:7a8500ed-bc85-4bbd-91aa-3374ce3af3ce + 259 + 1920 + 1080 + 0 + 0 + 1920 + 1080 + 0 + 0 + 3 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 01040001010503030000778888888888 + 22871e86ea86ea86bc7f007f007ee2774c774c776460036003604567d267d26761 + + + urn:smpte:ul:060e2b34.0401010d.0d010301.020c0600 + 2 + 240 + + + + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + + urn:uuid:f3c50ea8-a293-40b7-9640-bddd78faa528 + + + urn:uuid:42546c35-ab1e-44d0-a3da-8a6e7e2a7078 + urn:smpte:ul:060e2b34.0401010d.03020221.00000000 + urn:uuid:f3ebd314-3d98-43da-8b01-92de6efb31fc + IAB + IAB + en + + + 1 + 24000/1001 + 240 + urn:smpte:ul:060e2b34.0401010d.0d010301.021d0101 + 48000/1 + False + 0 + 24 + urn:smpte:ul:060e2b34.04010105.0e090604.00000000 + + + + 24000 1001 + + http://www.smpte-ra.org/ns/2067-21/2020 + + + + urn:uuid:fde40176-48aa-44e1-bf0c-b8c5e0f85b8f + + + urn:uuid:0ded5e28-933f-405f-9c5f-5e1683814c1f + urn:uuid:86d10356-f32f-4006-8f22-acb380222a25 + + + urn:uuid:716bc90d-df98-4331-9781-bfb8aab3dc09 + 240 + 240 + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + urn:uuid:418e4d1f-f5c7-4c71-8c62-6f796446de20 + + + + + urn:uuid:893eca6a-e09d-40b9-9958-e2d06765462e + urn:uuid:914b49d7-33d5-46fc-827e-9167bfda77a3 + + + urn:uuid:6a6294d3-f5ce-4663-9c29-2098679634f2 + 24000 1001 + 240 + 240 + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + urn:uuid:17c68559-b6de-4525-93f8-17c8ae77f599 + + + + + + + diff --git a/src/test/resources/TestIMP/IMF-2020/CPL-2020_updated-core-constraints.xml b/src/test/resources/TestIMP/IMF-2020/CPL-2020_updated-core-constraints.xml new file mode 100644 index 00000000..62a3387b --- /dev/null +++ b/src/test/resources/TestIMP/IMF-2020/CPL-2020_updated-core-constraints.xml @@ -0,0 +1,276 @@ + + + urn:uuid:48841f18-1998-468e-97a2-61e76c2e55dd + 2020-08-06T11:00:00Z + Netflix + Manually Edited + Netflix + Photon Test- CPL 2020 + + + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + + urn:uuid:8674e535-879c-4042-bf9f-1780a4c0019c + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompFill + 2 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 0 + 1023 + ScanningDirection_LeftToRightTopToBottom + 0 + 0 + + 42 + 0 + + 0 + 0 + 1080 + 1920 + 1080 + 1920 + 1080 + 1920 + FullFrame + 16/9 + 24000/1001 + urn:smpte:ul:060e2b34.0401010d.04010202.03010113 + 255 + urn:smpte:ul:060e2b34.04010106.04010101.03030000 + urn:smpte:ul:060e2b34.04010101.04010101.01020000 + urn:smpte:ul:060e2b34.04010101.04010101.02020000 + 0 + + + urn:uuid:7a8500ed-bc85-4bbd-91aa-3374ce3af3ce + 259 + 1920 + 1080 + 0 + 0 + 1920 + 1080 + 0 + 0 + 3 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + 9 + 1 + 1 + + + + + CompRed + 10 + + + CompGreen + 10 + + + CompBlue + 10 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + CompNull + 0 + + + 01040001010503030000778888888888 + 22871e86ea86ea86bc7f007f007ee2774c774c776460036003604567d267d26761 + + + urn:smpte:ul:060e2b34.0401010d.0d010301.020c0600 + 2 + 240 + + + + urn:uuid:9204a08f-e51a-49c5-91db-d98651aa20b8 + + urn:uuid:98990c3f-4733-4ae6-bf72-8a8d0a17fff1 + 48000/1 + 48000/1 + 2 + 24 + True + 0 + ElectroSpatialFormulation_Default + 6 + 288000 + urn:smpte:ul:060e2b34.0401010d.04020210.04010000 + + + urn:uuid:0018de35-b0d3-43fe-af1b-01492763a6ae + urn:smpte:ul:060e2b34.0401010d.03020101.00000000 + urn:uuid:09b93154-dbb9-4d36-8d35-369b97cfb772 + chL + Left + 1 + urn:uuid:8dc9dd54-bbe6-4a4e-ae07-69835da9ef5d + + + urn:uuid:4a8300e5-aaf4-41e7-8748-6653104d8587 + urn:smpte:ul:060e2b34.0401010d.03020102.00000000 + urn:uuid:1196b768-a9c3-4df7-8814-9c3f61dfa759 + chR + Right + 2 + urn:uuid:8dc9dd54-bbe6-4a4e-ae07-69835da9ef5d + + + urn:uuid:1daec10d-141a-4a00-bdc6-70ca6e3048d0 + urn:smpte:ul:060e2b34.0401010d.03020220.01000000 + urn:uuid:8dc9dd54-bbe6-4a4e-ae07-69835da9ef5d + sgST + Standard Stereo + Untitled Project + 2019-08-22T13:06:51 + Primary + PRM + en + + + urn:smpte:ul:060e2b34.04010101.0d010301.02060200 + 2 + 480480 + + + + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + + urn:uuid:f3c50ea8-a293-40b7-9640-bddd78faa528 + + + urn:uuid:42546c35-ab1e-44d0-a3da-8a6e7e2a7078 + urn:smpte:ul:060e2b34.0401010d.03020221.00000000 + urn:uuid:f3ebd314-3d98-43da-8b01-92de6efb31fc + IAB + IAB + en + + + 1 + 24000/1001 + 240 + urn:smpte:ul:060e2b34.0401010d.0d010301.021d0101 + 48000/1 + False + 0 + 24 + urn:smpte:ul:060e2b34.04010105.0e090604.00000000 + + + + 24000 1001 + + http://www.smpte-ra.org/ns/2067-21/2020 + + + + urn:uuid:fde40176-48aa-44e1-bf0c-b8c5e0f85b8f + + + urn:uuid:0ded5e28-933f-405f-9c5f-5e1683814c1f + urn:uuid:86d10356-f32f-4006-8f22-acb380222a25 + + + urn:uuid:716bc90d-df98-4331-9781-bfb8aab3dc09 + 240 + 240 + urn:uuid:ee8a1971-e46b-47e9-8687-078495f48c59 + urn:uuid:418e4d1f-f5c7-4c71-8c62-6f796446de20 + + + + + urn:uuid:77af2d39-5ed5-4f80-b157-6420b6fbc43b + urn:uuid:686dcf4b-645d-4187-8e20-c1306f22d796 + + + urn:uuid:6bc4c295-ec13-4109-9942-ad496a60d45b + 48000 1 + 480480 + 480480 + urn:uuid:9204a08f-e51a-49c5-91db-d98651aa20b8 + urn:uuid:299b877c-d40b-4f9e-aa22-4fadf7ebf2b9 + + + + + urn:uuid:893eca6a-e09d-40b9-9958-e2d06765462e + urn:uuid:914b49d7-33d5-46fc-827e-9167bfda77a3 + + + urn:uuid:6a6294d3-f5ce-4663-9c29-2098679634f2 + 24000 1001 + 240 + 240 + urn:uuid:78a82f04-9118-482c-8f2f-87e75beaaa23 + urn:uuid:17c68559-b6de-4525-93f8-17c8ae77f599 + + + + + + +