in src/main/java/com/netflix/imflibrary/st2067_2/IMFCoreConstraintsChecker.java [101:296]
public static List<ErrorLogger.ErrorObject> checkVirtualTracks(IMFCompositionPlaylistType compositionPlaylistType,
Map<UUID, ? extends Composition.VirtualTrack> virtualTrackMap,
Map<UUID, DOMNodeObjectModel> essenceDescriptorListMap,
RegXMLLibDictionary regXMLLibDictionary,
Set<String> homogeneitySelectionSet){
boolean foundMainImageEssence = false;
int numberOfMainImageEssences = 0;
boolean foundMainAudioEssence = false;
IMFErrorLogger imfErrorLogger =new IMFErrorLoggerImpl();
Iterator iterator = virtualTrackMap.entrySet().iterator();
while(iterator.hasNext()) {
Composition.VirtualTrack virtualTrack = ((Map.Entry<UUID, ? extends Composition.VirtualTrack>) iterator.next()).getValue();
List<? extends IMFBaseResourceType> virtualTrackResourceList = virtualTrack.getResourceList();
imfErrorLogger.addAllErrors(checkVirtualTrackResourceList(virtualTrack.getTrackID(), virtualTrackResourceList));
if (!(virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainAudioSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MarkerSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.SubtitlesSequence)
|| (virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.ForcedNarrativeSequence)
&& compositionPlaylistType.getCoreConstraintsSchema().equals(CoreConstraints.NAMESPACE_IMF_2020))
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.IABSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MGASADMSignalSequence))) {
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.WARNING,
String.format("CPL has a Sequence of type %s which is not fully supported sequence type in Photon, NS: %s",
virtualTrack.getSequenceTypeEnum().toString(), compositionPlaylistType.getCoreConstraintsSchema()));
continue;
}
if (virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)) {
foundMainImageEssence = true;
numberOfMainImageEssences++;
Composition.EditRate compositionEditRate = compositionPlaylistType.getEditRate();
for (IMFBaseResourceType baseResourceType : virtualTrackResourceList) {
Composition.EditRate trackResourceEditRate = baseResourceType.getEditRate();
//Section 6.4 st2067-2:2016
if (trackResourceEditRate != null
&& !trackResourceEditRate.equals(compositionEditRate)) {
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format("This Composition is invalid since the CompositionEditRate %s is not the same as atleast one of the MainImageSequence's Resource EditRate %s. Please refer to st2067-2:2013 Section 6.4", compositionEditRate.toString(), trackResourceEditRate.toString()));
}
}
}
else if(virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainAudioSequence)){
foundMainAudioEssence = true;
}
if((virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainAudioSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.SubtitlesSequence)
|| (virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.ForcedNarrativeSequence)
&& compositionPlaylistType.getCoreConstraintsSchema().equals(CoreConstraints.NAMESPACE_IMF_2020))
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.IABSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MGASADMSignalSequence))
&& compositionPlaylistType.getEssenceDescriptorList() != null
&& compositionPlaylistType.getEssenceDescriptorList().size() > 0)
{
List<DOMNodeObjectModel> virtualTrackEssenceDescriptors = new ArrayList<>();
String refSourceEncodingElement = "";
String essenceDescriptorField = "";
String otherEssenceDescriptorField = "";
Composition.EditRate essenceEditRate = null;
for(IMFBaseResourceType imfBaseResourceType : virtualTrackResourceList){
IMFTrackFileResourceType imfTrackFileResourceType = IMFTrackFileResourceType.class.cast(imfBaseResourceType);
DOMNodeObjectModel domNodeObjectModel = essenceDescriptorListMap.get(UUIDHelper.fromUUIDAsURNStringToUUID(imfTrackFileResourceType.getSourceEncoding()));
//Section 6.8 st2067-2:2016
if(domNodeObjectModel == null){
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, String.format("EssenceDescriptor ID %s referenced by a " +
"VirtualTrack Resource does not have a corresponding EssenceDescriptor in the EssenceDescriptorList in the CPL",
imfTrackFileResourceType.getSourceEncoding()));
}
else {
if (!refSourceEncodingElement.equals(imfTrackFileResourceType.getSourceEncoding())) {
refSourceEncodingElement = imfTrackFileResourceType.getSourceEncoding();
//Section 6.3.1 and 6.3.2 st2067-2:2016 Edit Rate check
if (virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence) ||
virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.SubtitlesSequence) ||
(virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.ForcedNarrativeSequence)
&& compositionPlaylistType.getCoreConstraintsSchema().equals(CoreConstraints.NAMESPACE_IMF_2020))) {
essenceDescriptorField = "SampleRate";
} else if (virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainAudioSequence) ||
virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.IABSequence) ||
virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MGASADMSignalSequence)){
essenceDescriptorField = "SampleRate";
otherEssenceDescriptorField = "AudioSampleRate";
}
String sampleRate = domNodeObjectModel.getFieldAsString(essenceDescriptorField);
if(sampleRate == null && !otherEssenceDescriptorField.isEmpty()) {
sampleRate = domNodeObjectModel.getFieldAsString(otherEssenceDescriptorField);
}
if (sampleRate != null) {
Long numerator = 0L;
Long denominator = 0L;
String[] sampleRateElements = (sampleRate.contains(" ")) ? sampleRate.split(" ") : sampleRate.contains("/") ? sampleRate.split("/") : new String[2];
if (sampleRateElements.length == 2) {
numerator = Long.valueOf(sampleRateElements[0]);
denominator = Long.valueOf(sampleRateElements[1]);
} else if (sampleRateElements.length == 1) {
numerator = Long.valueOf(sampleRateElements[0]);
denominator = 1L;
}
List<Long> editRate = new ArrayList<>();
Integer sampleRateToEditRateScale = 1;
if(virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)) {
CompositionImageEssenceDescriptorModel imageEssenceDescriptorModel = new CompositionImageEssenceDescriptorModel(UUIDHelper.fromUUIDAsURNStringToUUID
(imfTrackFileResourceType.getSourceEncoding()),
domNodeObjectModel,
regXMLLibDictionary);
sampleRateToEditRateScale = imageEssenceDescriptorModel.getFrameLayoutType().equals(GenericPictureEssenceDescriptor.FrameLayoutType.SeparateFields) ? 2 : 1;
}
editRate.add(numerator / sampleRateToEditRateScale);
editRate.add(denominator);
essenceEditRate = new Composition.EditRate(editRate);
}
}
if (essenceEditRate == null) {
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
String.format("This Composition represented by the ID %s is invalid since the VirtualTrack represented by ID %s has a Resource represented by ID %s that seems to refer to a EssenceDescriptor in the CPL's EssenceDescriptorList represented by the ID %s " +
"which does not have a value set for the field %s, however the Resource Edit Rate is %s"
, compositionPlaylistType.getId().toString(), virtualTrack.getTrackID().toString(), imfBaseResourceType.getId(), imfTrackFileResourceType.getSourceEncoding(), essenceDescriptorField, imfBaseResourceType.getEditRate().toString()));
} else if (!essenceEditRate.equals(imfBaseResourceType.getEditRate())) {
//Section 6.3.1 and 6.3.2 st2067-2:2016
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
String.format("This Composition represented by the ID %s is invalid since the VirtualTrack represented by ID %s has a Resource represented by ID %s that refers to a EssenceDescriptor in the CPL's EssenceDescriptorList represented by the ID %s " +
"whose indicated %s value is %s, however the Resource Edit Rate is %s"
, compositionPlaylistType.getId().toString(), virtualTrack.getTrackID().toString(), imfBaseResourceType.getId(), imfTrackFileResourceType.getSourceEncoding(), essenceDescriptorField, essenceEditRate.toString(), imfBaseResourceType.getEditRate().toString()));
}
virtualTrackEssenceDescriptors.add(domNodeObjectModel);
}
}
//Section 6.8 st2067-2:2016
if(!(virtualTrackEssenceDescriptors.size() > 0)){
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
String.format("This Composition represented by the ID %s is invalid since the resources comprising the VirtualTrack represented by ID %s seem to refer to EssenceDescriptor/s in the CPL's EssenceDescriptorList that are absent", compositionPlaylistType.getId().toString(), virtualTrack.getTrackID().toString()));
}
else if( virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainAudioSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.IABSequence)
|| virtualTrack.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MGASADMSignalSequence)){
boolean isVirtualTrackHomogeneous = true;
Set<String> homogeneitySelectionSetAll = new HashSet<>(homogeneitySelectionSet);
homogeneitySelectionSetAll.addAll(IMFCoreConstraintsChecker.homogeneitySelectionSet);
if (isCDCIEssenceDescriptor(virtualTrackEssenceDescriptors.get(0))) {
homogeneitySelectionSetAll.add("CodingEquations");
}
DOMNodeObjectModel refDOMNodeObjectModel = virtualTrackEssenceDescriptors.get(0).createDOMNodeObjectModelSelectionSet(virtualTrackEssenceDescriptors.get(0), homogeneitySelectionSetAll);
for (int i = 1; i < virtualTrackEssenceDescriptors.size(); i++) {
DOMNodeObjectModel other = virtualTrackEssenceDescriptors.get(i).createDOMNodeObjectModelSelectionSet(virtualTrackEssenceDescriptors.get(i), homogeneitySelectionSetAll);
isVirtualTrackHomogeneous &= refDOMNodeObjectModel.equals(other);
}
List<DOMNodeObjectModel> modelsIgnoreSet = new ArrayList<>();
if (!isVirtualTrackHomogeneous) {
for(int i = 1; i< virtualTrackEssenceDescriptors.size(); i++){
DOMNodeObjectModel other = virtualTrackEssenceDescriptors.get(i).createDOMNodeObjectModelSelectionSet(virtualTrackEssenceDescriptors.get(i), homogeneitySelectionSetAll);
modelsIgnoreSet.add(other);
imfErrorLogger.addAllErrors(DOMNodeObjectModel.getNamespaceURIMismatchErrors(refDOMNodeObjectModel, other));
}
//Section 6.2 st2067-2:2016
imfErrorLogger.addAllErrors(refDOMNodeObjectModel.getErrors());
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
String.format("This Composition represented by the ID %s is invalid since the VirtualTrack represented by ID %s is not homogeneous based on a comparison of the EssenceDescriptors referenced by its resources in the Essence Descriptor List, " +
"the EssenceDescriptors corresponding to this VirtualTrack in the EssenceDescriptorList are as follows %n%n%s", compositionPlaylistType.getId().toString(), virtualTrack.getTrackID().toString(), Utilities.serializeObjectCollectionToString(modelsIgnoreSet)));
}
}
}
}
//TODO : Add a check to ensure that all the VirtualTracks have the same duration.
//Section 6.3.1 st2067-2:2016 and Section 6.9.3 st2067-3:2016
if(!foundMainImageEssence){
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 image essence in its first segment, exactly one is required", compositionPlaylistType.getId().toString()));
}
else{
if(numberOfMainImageEssences > 1){
imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CORE_CONSTRAINTS_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format("The Composition represented by Id %s seems to contain %d image essences in its first segment, exactly one is required", compositionPlaylistType.getId().toString(), numberOfMainImageEssences));
}
}
//Section 6.3.2 st2067-2:2016 and Section 6.9.3 st2067-3:2016
//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()));
}
return imfErrorLogger.getErrors();
}