diff --git a/src/main/java/com/netflix/imflibrary/Colorimetry.java b/src/main/java/com/netflix/imflibrary/Colorimetry.java index 1be3992b..69c74e57 100644 --- a/src/main/java/com/netflix/imflibrary/Colorimetry.java +++ b/src/main/java/com/netflix/imflibrary/Colorimetry.java @@ -22,6 +22,7 @@ public enum Colorimetry { Color6(ColorPrimaries.P3D65, TransferCharacteristic.SMPTEST2084, CodingEquation.None), Color7(ColorPrimaries.ITU2020, TransferCharacteristic.SMPTEST2084, CodingEquation.ITU2020NCL), Color_App5_AP0(ColorPrimaries.ACES, TransferCharacteristic.Linear, CodingEquation.None), + Color8(ColorPrimaries.ITU2020, TransferCharacteristic.ITU2020HLG, CodingEquation.ITU2020NCL), Unknown(ColorPrimaries.Unknown, TransferCharacteristic.Unknown, CodingEquation.Unknown); @@ -87,6 +88,7 @@ public static enum TransferCharacteristic { ITU2020(UL.fromULAsURNStringToUL("urn:smpte:ul:06.0E.2B.34.04.01.01.0E.04.01.01.01.01.09.00.00")), SMPTEST2084(UL.fromULAsURNStringToUL("urn:smpte:ul:06.0E.2B.34.04.01.01.0D.04.01.01.01.01.0A.00.00")), Linear(UL.fromULAsURNStringToUL("urn:smpte:ul:06.0e.2b.34.04.01.01.06.04.01.01.01.01.06.00.00")), + ITU2020HLG(UL.fromULAsURNStringToUL("urn:smpte:ul:06.0E.2B.34.04.01.01.0D.04.01.01.01.01.0B.00.00")), Unknown(null); private final UL transferCharacteristicUL; 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..eb7e185c 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/AbstractApplicationComposition.java @@ -646,7 +646,7 @@ Map getEssenceDescriptorListMap(Set ignoreSet) * * @return a HashMap mapping the UUID to its corresponding EssenceDescriptor in the Composition */ - Map getEssenceDescriptorListMap() { + protected Map getEssenceDescriptorListMap() { return getEssenceDescriptorListMap(new HashSet<>()); } 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..95711617 100755 --- a/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/ApplicationCompositionFactory.java @@ -20,10 +20,10 @@ import com.netflix.imflibrary.IMFErrorLogger; import com.netflix.imflibrary.exceptions.IMFException; +import com.netflix.imflibrary.tsp_2121.ApplicationTsp2121Composition; import com.netflix.imflibrary.utils.FileByteRangeProvider; import com.netflix.imflibrary.utils.ResourceByteRangeProvider; -import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; @@ -50,10 +50,15 @@ public class ApplicationCompositionFactory { add("http://www.smpte-ra.org/ns/2067-50/2017"); }}); + private static final Set namespacesApplicationTsp2121Composition = Collections.unmodifiableSet(new HashSet() {{ + add("http://www.digitalproductionpartnership.co.uk/schema/imf/TSP2121-1/2018"); + }}); + public enum ApplicationCompositionType { APPLICATION_2_COMPOSITION_TYPE(Application2Composition.class, namespacesApplication2Composition), APPLICATION_2E_COMPOSITION_TYPE(Application2ExtendedComposition.class, namespacesApplication2EComposition), APPLICATION_5_COMPOSITION_TYPE(Application5Composition.class, namespacesApplication5Composition), + APPLICATION_TSP_2121_COMPOSITION_TYPE(ApplicationTsp2121Composition.class, namespacesApplicationTsp2121Composition), APPLICATION_UNSUPPORTED_COMPOSITION_TYPE(ApplicationUnsupportedComposition.class, Collections.unmodifiableSet(new HashSet<>())); private Set nameSpaceSet; private Class clazz; diff --git a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionImageEssenceDescriptorModel.java b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionImageEssenceDescriptorModel.java index 18f86ef4..f971a016 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/CompositionImageEssenceDescriptorModel.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/CompositionImageEssenceDescriptorModel.java @@ -29,7 +29,7 @@ /** * Created by svenkatrav on 11/2/16. */ -public final class CompositionImageEssenceDescriptorModel { +public class CompositionImageEssenceDescriptorModel { private final UUID imageEssencedescriptorID; private final DOMNodeObjectModel imageEssencedescriptorDOMNode; private final RegXMLLibDictionary regXMLLibDictionary; @@ -199,6 +199,18 @@ public CompositionImageEssenceDescriptorModel(@Nonnull UUID imageEssencedescript return imageEssencedescriptorID; } + protected @Nonnull IMFErrorLogger getImfErrorLogger() { + return imfErrorLogger; + } + + protected @Nonnull DOMNodeObjectModel getImageEssenceDescriptorDOMNode() { + return imageEssencedescriptorDOMNode; + } + + protected @Nonnull RegXMLLibDictionary getRegXMLLibDictionary() { + return regXMLLibDictionary; + } + public @Nonnull FrameLayoutType getFrameLayoutType() { return frameLayoutType; } @@ -380,7 +392,7 @@ public TransferCharacteristic getTransferCharacteristic() { return paletteLayout; } - private @Nonnull Integer parsePixelBitDepth(@Nonnull ColorModel colorModel) { + protected @Nonnull Integer parsePixelBitDepth(@Nonnull ColorModel colorModel) { Integer refPixelBitDepth = null; DOMNodeObjectModel subDescriptors = imageEssencedescriptorDOMNode.getDOMNode(regXMLLibDictionary.getSymbolNameFromURN(subdescriptorsUL)); if (subDescriptors == 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 b477f493..6cbe1d30 100644 --- a/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java +++ b/src/main/java/com/netflix/imflibrary/st2067_2/IMFCompositionPlaylistType.java @@ -54,7 +54,7 @@ * A class that models an IMF Composition Playlist structure. */ @Immutable -final class IMFCompositionPlaylistType { +public final class IMFCompositionPlaylistType { private final UUID id; private final Composition.EditRate editRate; private final String annotation; diff --git a/src/main/java/com/netflix/imflibrary/tsp_2121/ApplicationTsp2121Composition.java b/src/main/java/com/netflix/imflibrary/tsp_2121/ApplicationTsp2121Composition.java new file mode 100644 index 00000000..fb168319 --- /dev/null +++ b/src/main/java/com/netflix/imflibrary/tsp_2121/ApplicationTsp2121Composition.java @@ -0,0 +1,289 @@ +/* + * + * Copyright 2015 Media-IO, France. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.netflix.imflibrary.tsp_2121; + +import com.netflix.imflibrary.Colorimetry; +import com.netflix.imflibrary.IMFErrorLogger; +import com.netflix.imflibrary.st0377.header.UL; +import com.netflix.imflibrary.st2067_2.AbstractApplicationComposition; +import com.netflix.imflibrary.st2067_2.ApplicationCompositionFactory.ApplicationCompositionType; +import com.netflix.imflibrary.st2067_2.CompositionImageEssenceDescriptorModel; +import com.netflix.imflibrary.st2067_2.IMFCompositionPlaylistType; +import com.netflix.imflibrary.utils.DOMNodeObjectModel; +import com.netflix.imflibrary.utils.Fraction; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import static com.netflix.imflibrary.Colorimetry.*; +import static com.netflix.imflibrary.st0377.header.GenericPictureEssenceDescriptor.FrameLayoutType; + +/** + * A class that models Composition with Application TSP 2121 constraints from TSP 2121-1 specification + */ +public class ApplicationTsp2121Composition extends AbstractApplicationComposition { + + public static final Set resolutionsSupported = Collections.unmodifiableSet(new HashSet() {{ + add(new Fraction(1920, 1080)); + add(new Fraction(3840, 2160)); + }}); + + public static final Set sampleRateSupported = Collections.unmodifiableSet(new HashSet() {{ + add(new Fraction(24)); + add(new Fraction(25)); + add(new Fraction(30)); + add(new Fraction(50)); + add(new Fraction(60)); + add(new Fraction(24000, 1001)); + add(new Fraction(30000, 1001)); + add(new Fraction(60000, 1001)); + }}); + + public static final Set bitDepthsSupported = Collections.unmodifiableSet(new HashSet() {{ + add(10); + add(12); + }}); + + private static final Set ignoreSet = Collections.unmodifiableSet(new HashSet() {{ + add("SignalStandard"); + add("ActiveFormatDescriptor"); + add("VideoLineMap"); + add("AlphaTransparency"); + add("PixelLayout"); + add("ActiveHeight"); + add("ActiveWidth"); + add("ActiveXOffset"); + add("ActiveYOffset"); + }}); + + private CompositionImageEssenceTsp2121DescriptorModel imageEssenceDescriptorModel; + + public ApplicationTsp2121Composition(@Nonnull IMFCompositionPlaylistType imfCompositionPlaylistType) { + this(imfCompositionPlaylistType, new HashSet<>()); + } + + public ApplicationTsp2121Composition(@Nonnull IMFCompositionPlaylistType imfCompositionPlaylistType, Set homogeneitySelectionSet) { + super(imfCompositionPlaylistType, ignoreSet, homogeneitySelectionSet); + + try { + this.imageEssenceDescriptorModel = (CompositionImageEssenceTsp2121DescriptorModel) getCompositionImageEssenceDescriptorModel(); + + if (imageEssenceDescriptorModel != null) { + imfErrorLogger.addAllErrors(imageEssenceDescriptorModel.getErrors()); + validateGenericPictureEssenceDescriptor(imfErrorLogger); + validateImageCharacteristics(imfErrorLogger); + } + } catch (Exception e) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("Exception in validating EssenceDescriptors in APPLICATION_TSP_2121_COMPOSITION_TYPE: %s ", e.getMessage())); + } + } + + @Override + public @Nullable CompositionImageEssenceDescriptorModel getCompositionImageEssenceDescriptorModel() { + CompositionImageEssenceTsp2121DescriptorModel imageEssenceDescriptorModel = null; + final DOMNodeObjectModel imageEssencedescriptorDOMNode = this.getEssenceDescriptor( + this.getVideoVirtualTrack().getTrackResourceIds().iterator().next()); + + if (imageEssencedescriptorDOMNode != null) { + final UUID imageEssenceDescriptorID = getEssenceDescriptorListMap().entrySet().stream() + .filter(e -> e.getValue().equals(imageEssencedescriptorDOMNode)) + .map(e -> e.getKey()) + .findFirst().get(); + imageEssenceDescriptorModel = + new CompositionImageEssenceTsp2121DescriptorModel(imageEssenceDescriptorID, imageEssencedescriptorDOMNode, regXMLLibDictionary); + } + + return imageEssenceDescriptorModel; + } + + private void validateGenericPictureEssenceDescriptor(IMFErrorLogger imfErrorLogger) { + UUID imageEssenceDescriptorID = imageEssenceDescriptorModel.getImageEssencedescriptorID(); + ColorModel colorModel = imageEssenceDescriptorModel.getColorModel(); + if (colorModel.equals(ColorModel.Unknown)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has Invalid color components as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + return; + } + + Integer storedWidth = imageEssenceDescriptorModel.getStoredWidth(); + Integer storedHeight = imageEssenceDescriptorModel.getStoredHeight(); + if ((storedWidth <= 0) || (storedHeight <= 0)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid storedWidth(%d) or storedHeight(%d) as per %s", + imageEssenceDescriptorID.toString(), storedWidth, storedHeight, getApplicationCompositionType().toString())); + } + + Integer sampleWidth = imageEssenceDescriptorModel.getSampleWidth(); + Integer sampleHeight = imageEssenceDescriptorModel.getSampleHeight(); + if ((sampleWidth != null && !sampleWidth.equals(storedWidth)) || + (sampleHeight != null && !sampleHeight.equals(storedHeight))) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid sampleWidth(%d) or sampleHeight(%d) as per %s", + imageEssenceDescriptorID.toString(), sampleWidth != null ? sampleWidth : 0, sampleHeight != null ? sampleHeight : 0, + getApplicationCompositionType().toString())); + } + + if (imageEssenceDescriptorModel.getStoredOffset() != null) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s invalid StoredOffset as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + } + + ColorPrimaries colorPrimaries = imageEssenceDescriptorModel.getColorPrimaries(); + if (colorPrimaries.equals(ColorPrimaries.Unknown)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid ColorPrimaries as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + } + + TransferCharacteristic transferCharacteristic = imageEssenceDescriptorModel.getTransferCharacteristic(); + if (transferCharacteristic.equals(TransferCharacteristic.Unknown)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid TransferCharacteristic as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + } + + CodingEquation codingEquation = imageEssenceDescriptorModel.getCodingEquation(); + if (codingEquation.equals(CodingEquation.Unknown)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid CodingEquation as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + } + + Colorimetry color = imageEssenceDescriptorModel.getColor(); + if (color.equals(Colorimetry.Unknown)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid ColorPrimaries(%s)-TransferCharacteristic(%s)-CodingEquation(%s) combination as per %s", + imageEssenceDescriptorID.toString(), colorPrimaries.name(), transferCharacteristic.name(), codingEquation.name(), getApplicationCompositionType().toString())); + } + + UL essenceContainerFormatUL = imageEssenceDescriptorModel.getEssenceContainerFormatUL(); + if (essenceContainerFormatUL != null) { + UL proresUL = UL.fromULAsURNStringToUL("urn:smpte:ul:06.0E.2B.34.04.01.01.0D.0D.01.03.01.02.1C.01.00"); + if (!essenceContainerFormatUL.equals(proresUL)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid UL %s indicated by the ContainerFormat as per %s", + imageEssenceDescriptorID.toString(), essenceContainerFormatUL.toString(), getApplicationCompositionType().toString())); + } + } + } + + private void validateImageCharacteristics(IMFErrorLogger imfErrorLogger) { + UUID imageEssenceDescriptorID = imageEssenceDescriptorModel.getImageEssencedescriptorID(); + + ColorModel colorModel = imageEssenceDescriptorModel.getColorModel(); + if (!colorModel.equals(ColorModel.RGB) && !colorModel.equals(ColorModel.YUV)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has Invalid color components as per %s", + imageEssenceDescriptorID.toString(), getApplicationCompositionType().toString())); + return; + } + + //storedWidth + Integer storedWidth = imageEssenceDescriptorModel.getStoredWidth(); + //storedHeight + Integer storedHeight = imageEssenceDescriptorModel.getStoredHeight(); + if (!resolutionsSupported.contains(new Fraction(storedWidth, storedHeight))) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid storedHeight(%d) as per %s", + imageEssenceDescriptorID.toString(), storedHeight, getApplicationCompositionType().toString())); + } + + //ComponentDepth + Integer componentDepth = imageEssenceDescriptorModel.getComponentDepth(); + if (!bitDepthsSupported.contains(componentDepth)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s invalid ComponentDepth(%d) as per %s", + imageEssenceDescriptorID.toString(), componentDepth, getApplicationCompositionType().toString())); + } + + //PixelBitDepth + Integer pixelBitDepth = imageEssenceDescriptorModel.getPixelBitDepth(); + if (!bitDepthsSupported.contains(pixelBitDepth)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s invalid PixelBitDepth(%d) as per %s", + imageEssenceDescriptorID.toString(), pixelBitDepth, getApplicationCompositionType().toString())); + } + + //FrameLayout + FrameLayoutType frameLayoutType = imageEssenceDescriptorModel.getFrameLayoutType(); + if (!frameLayoutType.equals(FrameLayoutType.FullFrame) && !frameLayoutType.equals(FrameLayoutType.SeparateFields)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid FrameLayout(%s) as per %s", + imageEssenceDescriptorID.toString(), frameLayoutType.name(), getApplicationCompositionType().toString())); + } else { + //SampleRate + Fraction sampleRate = imageEssenceDescriptorModel.getSampleRate(); + if (!sampleRateSupported.contains(sampleRate)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has Invalid SampleRate(%s) for frame structure %s as per %s", + imageEssenceDescriptorID.toString(), sampleRate.toString(), frameLayoutType.name(), getApplicationCompositionType().toString())); + } + } + + //Sampling + Sampling sampling = imageEssenceDescriptorModel.getSampling(); + if (frameLayoutType.equals(FrameLayoutType.SeparateFields) && !sampling.equals(Sampling.Sampling422)) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid combination of FrameLayOut(%s) for Sampling(%s) as per %s", + imageEssenceDescriptorID.toString(), frameLayoutType.name(), sampling.name(), getApplicationCompositionType().toString())); + } + + //Quantization + Quantization quantization = imageEssenceDescriptorModel.getQuantization(); + Colorimetry color = imageEssenceDescriptorModel.getColor(); + if ((sampling.equals(Sampling.Sampling422) && + !(quantization.equals(Quantization.QE1) && colorModel.equals(ColorModel.YUV))) || + (quantization.equals(Quantization.QE2) && + !(colorModel.equals(ColorModel.RGB) && color.equals(Colorimetry.Color3)))) { + imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has invalid combination of quantization(%s)-Sampling(%s)-colorModel(%s)-color(%s) as per %s", + imageEssenceDescriptorID.toString(), quantization.name(), sampling.name(), colorModel.name(), color.name(), getApplicationCompositionType().toString())); + } + } + + public ApplicationCompositionType getApplicationCompositionType() { + return ApplicationCompositionType.APPLICATION_TSP_2121_COMPOSITION_TYPE; + } + +} diff --git a/src/main/java/com/netflix/imflibrary/tsp_2121/CompositionImageEssenceTsp2121DescriptorModel.java b/src/main/java/com/netflix/imflibrary/tsp_2121/CompositionImageEssenceTsp2121DescriptorModel.java new file mode 100644 index 00000000..cf1dd028 --- /dev/null +++ b/src/main/java/com/netflix/imflibrary/tsp_2121/CompositionImageEssenceTsp2121DescriptorModel.java @@ -0,0 +1,128 @@ +package com.netflix.imflibrary.tsp_2121; + +import com.netflix.imflibrary.Colorimetry; +import com.netflix.imflibrary.IMFErrorLogger; +import com.netflix.imflibrary.st2067_2.CompositionImageEssenceDescriptorModel; +import com.netflix.imflibrary.utils.DOMNodeObjectModel; +import com.netflix.imflibrary.utils.RegXMLLibDictionary; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.netflix.imflibrary.st0377.header.GenericPictureEssenceDescriptor.*; +import static com.netflix.imflibrary.st0377.header.GenericPictureEssenceDescriptor.RGBAComponentType.Null; + + +public final class CompositionImageEssenceTsp2121DescriptorModel extends CompositionImageEssenceDescriptorModel { + + public CompositionImageEssenceTsp2121DescriptorModel(@Nonnull UUID imageEssenceDescriptorID, + @Nonnull DOMNodeObjectModel imageEssenceDescriptorDOMNode, + @Nonnull RegXMLLibDictionary regXMLLibDictionary) { + super(imageEssenceDescriptorID, imageEssenceDescriptorDOMNode, regXMLLibDictionary); + } + + @Override + protected @Nonnull Integer parsePixelBitDepth(@Nonnull Colorimetry.ColorModel colorModel) { + Integer refPixelBitDepth = null; + DOMNodeObjectModel subDescriptors = getImageEssenceDescriptorDOMNode().getDOMNode(getRegXMLLibDictionary().getSymbolNameFromURN(subdescriptorsUL)); + if (subDescriptors == null) { + Integer componentDepth = getComponentDepth(); + if (componentDepth == null || componentDepth == 0) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s is missing SubDescriptors and missing Component Depth", getImageEssencedescriptorID().toString())); + } + refPixelBitDepth = componentDepth; + } else { + DOMNodeObjectModel jpeg2000SubdescriptorDOMNode = subDescriptors.getDOMNode(getRegXMLLibDictionary().getSymbolNameFromURN(jpeg2000SubDescriptorUL)); + + if (jpeg2000SubdescriptorDOMNode == null) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s is missing JPEG2000SubDescriptor in SubDescriptors", + getImageEssencedescriptorID().toString())); + } else { + DOMNodeObjectModel j2cLayout = jpeg2000SubdescriptorDOMNode.getDOMNode(getRegXMLLibDictionary().getSymbolNameFromURN(j2cLayoutUL)); + if (j2cLayout == null) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s is missing J2CLayout in JPEG2000SubDescriptor", + getImageEssencedescriptorID().toString())); + } else { + List rgbaComponents = j2cLayout.getDOMNodes(getRegXMLLibDictionary().getSymbolNameFromURN(rgbaComponentUL)); + if (rgbaComponents.size() == 0) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s is missing RGBAComponent in J2CLayout", + getImageEssencedescriptorID().toString())); + } else { + Map componentMap = new HashMap<>(); + for (DOMNodeObjectModel domNodeObjectModel : rgbaComponents) { + String code = domNodeObjectModel.getFieldAsString(getRegXMLLibDictionary().getTypeFieldNameFromURN(rgbaComponentUL, codeUL)); + if (code == null) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has an RGBAComponent with missing Code", + getImageEssencedescriptorID().toString())); + } else { + RGBAComponentType codeValue = RGBAComponentType.valueOf(getRegXMLLibDictionary().getEnumerationValueFromName(rgbaComponentKindUL, code)); + Integer pixelBitDepth = domNodeObjectModel.getFieldAsInteger(getRegXMLLibDictionary().getTypeFieldNameFromURN(rgbaComponentUL, componentSizeUL)); + if (pixelBitDepth == null) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has an RGBAComponent %s with missing ComponentSize", + getImageEssencedescriptorID().toString(), code)); + } else { + if (refPixelBitDepth == null) { + refPixelBitDepth = pixelBitDepth; + } + if ((codeValue.equals(Null) && pixelBitDepth != 0) || + (!codeValue.equals(Null) && (!pixelBitDepth.equals(refPixelBitDepth)))) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s has an RGBAComponent %s with invalid ComponentSize %d", + getImageEssencedescriptorID().toString(), code, pixelBitDepth)); + } + } + if (componentMap.containsKey(codeValue)) { + Integer count = componentMap.get(codeValue); + componentMap.put(codeValue, count + 1); + } else { + componentMap.put(codeValue, 1); + } + + } + } + componentMap.entrySet().stream().forEach( + e -> { + if (e.getKey().equals(RGBAComponentType.Null)) { + if (!e.getValue().equals(5)) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s and ColorModel %s has invalid number of RGBAComponent %s in J2CLayout", + getImageEssencedescriptorID().toString(), colorModel.name(), e.getKey())); + } + } else if (!colorModel.getComponentTypeSet().contains(e.getKey())) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s and ColorModel %s has invalid RGBAComponent %s in J2CLayout", + getImageEssencedescriptorID().toString(), colorModel.name(), e.getKey())); + } else if (!e.getValue().equals(1)) { + getImfErrorLogger().addError(IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR, + IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, + String.format("EssenceDescriptor with ID %s and ColorModel %s has more than one RGBAComponent %s in J2CLayout", + getImageEssencedescriptorID().toString(), colorModel.name(), e.getKey())); + } + + } + ); + } + } + } + } + return refPixelBitDepth != null ? refPixelBitDepth : 0; + } +}