Skip to content

Commit e3fd12f

Browse files
wruppelxIMFToolfschleich
authored
Add support for MGA S-ADM Virtual Tracks (SMPTE ST 2067-203) (#376)
* Adds support for ST 2067-203 MGA S-ADM Plug-in --------- Co-authored-by: IMFTool <[email protected]> Co-authored-by: Florian Schleich <[email protected]>
1 parent 9c0f01e commit e3fd12f

File tree

137 files changed

+9417
-24
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+9417
-24
lines changed

src/main/java/com/netflix/imflibrary/IMFConstraints.java

+34
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.netflix.imflibrary.st0377.PartitionPack;
2525
import com.netflix.imflibrary.st0377.header.*;
2626
import com.netflix.imflibrary.st2067_201.IABEssenceDescriptor;
27+
import com.netflix.imflibrary.st2067_203.MGASoundEssenceDescriptor;
2728
import com.netflix.imflibrary.utils.ErrorLogger;
2829
import com.netflix.imflibrary.utils.Utilities;
2930

@@ -450,6 +451,10 @@ else if(essenceType.equals(HeaderPartition.EssenceTypeEnum.IABEssence))
450451
{
451452
targetMXFDataDefinition = MXFDataDefinition.SOUND;
452453
}
454+
else if(essenceType.equals(HeaderPartition.EssenceTypeEnum.MGASADMEssence))
455+
{
456+
targetMXFDataDefinition = MXFDataDefinition.SOUND;
457+
}
453458
else{
454459
targetMXFDataDefinition = MXFDataDefinition.DATA;
455460
}
@@ -561,6 +566,35 @@ private boolean hasWaveAudioEssenceDescriptor()
561566
return iabEssenceDescriptor;
562567
}
563568

569+
/**
570+
* Gets the first MGASoundEssenceDescriptor structural metadata set from
571+
* an OP1A-conformant MXF Header partition. Returns null if none is found
572+
* @return returns the first MGASoundEssenceDescriptor
573+
*/
574+
public @Nullable MGASoundEssenceDescriptor getMGASoundEssenceDescriptor()
575+
{
576+
MGASoundEssenceDescriptor mgaSoundEssenceDescriptor = null;
577+
GenericPackage genericPackage = this.headerPartitionOP1A.getHeaderPartition().getPreface().getContentStorage().
578+
getEssenceContainerDataList().get(0).getLinkedPackage();
579+
SourcePackage filePackage = (SourcePackage)genericPackage;
580+
for (TimelineTrack timelineTrack : filePackage.getTimelineTracks())
581+
{
582+
Sequence sequence = timelineTrack.getSequence();
583+
MXFDataDefinition filePackageMxfDataDefinition = sequence.getMxfDataDefinition();
584+
if (filePackageMxfDataDefinition.equals(MXFDataDefinition.SOUND))
585+
{
586+
GenericDescriptor genericDescriptor = filePackage.getGenericDescriptor();
587+
if (genericDescriptor instanceof MGASoundEssenceDescriptor)
588+
{
589+
mgaSoundEssenceDescriptor = (MGASoundEssenceDescriptor)genericDescriptor;
590+
break;
591+
}
592+
}
593+
}
594+
595+
return mgaSoundEssenceDescriptor;
596+
}
597+
564598
/**
565599
* A method that returns the IMF Essence Component type.
566600
* @return essenceTypeEnum an enumeration constant corresponding to the IMFEssenceComponent type

src/main/java/com/netflix/imflibrary/RESTfulInterfaces/IMPValidator.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.netflix.imflibrary.st2067_2.Composition.VirtualTrack;
2424
import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack;
2525
import com.netflix.imflibrary.st2067_201.IABTrackFileConstraints;
26+
import com.netflix.imflibrary.st2067_203.MGASADMTrackFileConstraints;
2627
import com.netflix.imflibrary.utils.ByteArrayByteRangeProvider;
2728
import com.netflix.imflibrary.utils.ByteArrayDataProvider;
2829
import com.netflix.imflibrary.utils.ByteProvider;
@@ -592,12 +593,14 @@ public static List<ErrorLogger.ErrorObject> validateIMFTrackFileHeaderMetadata(L
592593
0L,
593594
(long)payloadRecord.getPayload().length,
594595
imfErrorLogger);
595-
596596
MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A = MXFOperationalPattern1A.checkOperationalPattern1ACompliance(headerPartition, imfErrorLogger);
597597
IMFConstraints.HeaderPartitionIMF headerPartitionIMF = IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger);
598598
if (headerPartitionIMF.getEssenceType() == HeaderPartition.EssenceTypeEnum.IABEssence) {
599599
IABTrackFileConstraints.checkCompliance(headerPartitionIMF, imfErrorLogger);
600600
}
601+
if (headerPartitionIMF.getEssenceType() == HeaderPartition.EssenceTypeEnum.MGASADMEssence) {
602+
MGASADMTrackFileConstraints.checkCompliance(headerPartitionIMF, imfErrorLogger);
603+
}
601604
}
602605
catch (IMFException | MXFException e){
603606
if(headerPartition != null) {
@@ -710,6 +713,9 @@ private static List<ErrorLogger.ErrorObject> checkVirtualTrackAndEssencesHeaderP
710713
if (headerPartitionIMF.hasMatchingEssence(HeaderPartition.EssenceTypeEnum.IABEssence)) {
711714
IABTrackFileConstraints.checkCompliance(headerPartitionIMF, imfErrorLogger);
712715
}
716+
if (headerPartitionIMF.hasMatchingEssence(HeaderPartition.EssenceTypeEnum.MGASADMEssence)) {
717+
MGASADMTrackFileConstraints.checkCompliance(headerPartitionIMF, imfErrorLogger);
718+
}
713719
}
714720
catch (IMFException | MXFException e){
715721
if(headerPartition != null) {
@@ -922,7 +928,7 @@ public static List<ErrorLogger.ErrorObject> validateOPL(PayloadRecord opl) throw
922928
}
923929

924930
/**
925-
* A stateless method, used for IMP containing IAB tracks, that will validate that the index edit rate in the index segment matches the one in the descriptor (according to Section 5.7 of SMPTE ST 2067-201:2019)
931+
* A stateless method, used for IMP containing IAB and/or MGA S-ADM tracks, that will validate that the index edit rate in the index segment matches the one in the descriptor (according to Section 5.7 of SMPTE ST 2067-201:2019)
926932
* @param headerPartitionPayloadRecords - a list of IMF Essence Component partition payloads for header partitions
927933
* @param indexSegmentPayloadRecords - a list of IMF Essence Component partition payloads for index partitions
928934
* @return list of error messages encountered while validating
@@ -970,6 +976,8 @@ public static List<ErrorLogger.ErrorObject> validateIndexEditRate(List<PayloadRe
970976
IndexTableSegment indexTableSegment = new IndexTableSegment(imfEssenceComponentByteProvider, header);
971977
if (headerPartitionIMF.hasMatchingEssence(HeaderPartition.EssenceTypeEnum.IABEssence)) {
972978
IABTrackFileConstraints.checkIndexEditRate(headerPartitionIMF, indexTableSegment, imfErrorLogger);
979+
} else if (headerPartitionIMF.hasMatchingEssence(HeaderPartition.EssenceTypeEnum.MGASADMEssence)) {
980+
MGASADMTrackFileConstraints.checkIndexEditRate(headerPartitionIMF, indexTableSegment, imfErrorLogger);
973981
}
974982
} else {
975983
imfEssenceComponentByteProvider.skipBytes(header.getVSize());

src/main/java/com/netflix/imflibrary/app/IMFTrackFileReader.java

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import com.netflix.imflibrary.st0377.header.InterchangeObject;
3737
import com.netflix.imflibrary.st0377.header.Preface;
3838
import com.netflix.imflibrary.st0377.header.SourcePackage;
39+
import com.netflix.imflibrary.st2067_201.IABTrackFileConstraints;
40+
import com.netflix.imflibrary.st2067_203.MGASADMTrackFileConstraints;
3941
import com.netflix.imflibrary.utils.ByteArrayDataProvider;
4042
import com.netflix.imflibrary.utils.ByteProvider;
4143
import com.netflix.imflibrary.utils.ErrorLogger;
@@ -133,6 +135,10 @@ private void setHeaderPartitionIMF(long inclusiveRangeStart, long inclusiveRange
133135
//validate header partition
134136
MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A = MXFOperationalPattern1A.checkOperationalPattern1ACompliance(headerPartition, imfErrorLogger);
135137
this.headerPartition = IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger);
138+
if (this.headerPartition != null) {
139+
IABTrackFileConstraints.checkCompliance(this.headerPartition, imfErrorLogger);
140+
MGASADMTrackFileConstraints.checkCompliance(this.headerPartition, imfErrorLogger);
141+
}
136142
}
137143
catch (MXFException | IMFException e){
138144
if(headerPartition == null){
@@ -500,6 +506,7 @@ HeaderPartition.EssenceTypeEnum getEssenceType(@Nonnull IMFErrorLogger imfErrorL
500506
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MainAudioEssence);
501507
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MarkerEssence);
502508
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.IABEssence);
509+
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MGASADMEssence);
503510
List<HeaderPartition.EssenceTypeEnum> supportedEssenceTypesFound = new ArrayList<>();
504511
List<HeaderPartition.EssenceTypeEnum> essenceTypes = this.getHeaderPartitionIMF(imfErrorLogger).getHeaderPartitionOP1A().getHeaderPartition().getEssenceTypes();
505512

@@ -720,6 +727,7 @@ else if(e instanceof MXFException){
720727
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MainImageEssence);
721728
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MainAudioEssence);
722729
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MarkerEssence);
730+
supportedEssenceComponentTypes.add(HeaderPartition.EssenceTypeEnum.MGASADMEssence);
723731
if(imfTrackFileReader != null
724732
&& imfTrackFileCPLBuilder != null
725733
&& supportedEssenceComponentTypes.contains(imfTrackFileReader.getEssenceType(imfErrorLogger))) {

src/main/java/com/netflix/imflibrary/app/IMPAnalyzer.java

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.netflix.imflibrary.st2067_2.ApplicationCompositionFactory;
2121
import com.netflix.imflibrary.st2067_2.Composition;
2222
import com.netflix.imflibrary.st2067_2.IMFEssenceComponentVirtualTrack;
23+
import com.netflix.imflibrary.st2067_203.IMFMGASADMConstraintsChecker;
2324
import com.netflix.imflibrary.utils.ByteArrayDataProvider;
2425
import com.netflix.imflibrary.utils.ByteProvider;
2526
import com.netflix.imflibrary.utils.ErrorLogger;
@@ -456,6 +457,12 @@ public static List<ApplicationComposition> analyzeApplicationCompositions( File
456457
Set<UUID> trackFileIDsSet = trackFileIDToHeaderPartitionPayLoadMap
457458
.keySet();
458459

460+
// ST 2067-203 MGASADMVirtualTrackParameterSet checks
461+
List<ErrorLogger.ErrorObject> errors = IMFMGASADMConstraintsChecker.checkMGASADMVirtualTrackParameterSet(applicationComposition);
462+
// Report MGASADMVirtualTrackParameterSet as both CPL and Virtual Track errors
463+
compositionConformanceErrorLogger.addAllErrors(errors);
464+
compositionErrorLogger.addAllErrors(errors);
465+
459466
try {
460467
if (!isCompositionComplete(applicationComposition, trackFileIDsSet, compositionConformanceErrorLogger)) {
461468
for (IMFEssenceComponentVirtualTrack virtualTrack : applicationComposition.getEssenceVirtualTracks()) {

src/main/java/com/netflix/imflibrary/st0377/HeaderPartition.java

+43-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.netflix.imflibrary.st2067_2.Composition;
3232
import com.netflix.imflibrary.st2067_201.IABEssenceDescriptor;
3333
import com.netflix.imflibrary.st2067_201.IABSoundfieldLabelSubDescriptor;
34+
import com.netflix.imflibrary.st2067_203.MGASoundEssenceDescriptor;
35+
import com.netflix.imflibrary.st2067_203.MGASoundfieldGroupLabelSubDescriptor;
3436
import com.netflix.imflibrary.utils.ByteArrayDataProvider;
3537
import com.netflix.imflibrary.utils.ByteProvider;
3638
import com.netflix.imflibrary.utils.ErrorLogger;
@@ -431,6 +433,10 @@ else if (dependentInterchangeObject instanceof SoundFieldGroupLabelSubDescriptor
431433
IABEssenceDescriptor iabEssenceDescriptor = new IABEssenceDescriptor((IABEssenceDescriptor.IABEssenceDescriptorBO) interchangeObjectBO);
432434
this.cacheInterchangeObject(iabEssenceDescriptor);
433435
uidToMetadataSets.put(interchangeObjectBO.getInstanceUID(), iabEssenceDescriptor);
436+
} else if(interchangeObjectBO.getClass().getEnclosingClass().equals(MGASoundEssenceDescriptor.class)){
437+
MGASoundEssenceDescriptor mgaSoundEssenceDescriptor = new MGASoundEssenceDescriptor((MGASoundEssenceDescriptor.MGASoundEssenceDescriptorBO) interchangeObjectBO);
438+
this.cacheInterchangeObject(mgaSoundEssenceDescriptor);
439+
uidToMetadataSets.put(interchangeObjectBO.getInstanceUID(), mgaSoundEssenceDescriptor);
434440
} else if(interchangeObjectBO.getClass().getEnclosingClass().equals(TimedTextDescriptor.class)){
435441
List<TimeTextResourceSubDescriptor> subDescriptorList = new ArrayList<>();
436442
for(Node dependent : node.depends) {
@@ -476,7 +482,6 @@ else if (dependentInterchangeObject instanceof SoundFieldGroupLabelSubDescriptor
476482
*/
477483
private InterchangeObject.InterchangeObjectBO constructInterchangeObjectBO(Class clazz, KLVPacket.Header header, ByteProvider byteProvider, Map localTagToUIDMap, IMFErrorLogger imfErrorLogger) throws IOException{
478484
try {
479-
480485
Constructor<?> constructor = clazz.getConstructor(KLVPacket.Header.class, ByteProvider.class, Map.class, IMFErrorLogger.class);
481486
InterchangeObject.InterchangeObjectBO interchangeObjectBO = (InterchangeObject.InterchangeObjectBO)constructor.newInstance(header, byteProvider, localTagToUIDMap, imfErrorLogger);
482487
String simpleClassName = interchangeObjectBO.getClass().getSimpleName();
@@ -721,6 +726,16 @@ public String getAudioEssenceSpokenLanguage() throws IOException {
721726
this.imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, String.format("Language Codes (%s, %s) do not match across the IABSoundFieldLabelSubDescriptors", rfc5646SpokenLanguage, iabSoundfieldLabelSubDescriptor.getRFC5646SpokenLanguage()));
722727
}
723728
}
729+
} else if (this.hasMGASoundEssenceDescriptor()) {
730+
List<InterchangeObject> mgaSoundfieldGroupLabelSubDescriptors = this.getMGASoundfieldGroupLabelSubDescriptors();
731+
for (InterchangeObject subDescriptor : mgaSoundfieldGroupLabelSubDescriptors) {
732+
MGASoundfieldGroupLabelSubDescriptor mgaSoundfieldLabelSubDescriptor = (MGASoundfieldGroupLabelSubDescriptor) subDescriptor;
733+
if (rfc5646SpokenLanguage == null) {
734+
rfc5646SpokenLanguage = mgaSoundfieldLabelSubDescriptor.getRFC5646SpokenLanguage();
735+
} else if (!rfc5646SpokenLanguage.equals(mgaSoundfieldLabelSubDescriptor.getRFC5646SpokenLanguage())) {
736+
this.imfErrorLogger.addError(IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL, String.format("Language Codes (%s, %s) do not match across the MGASoundfieldGroupLabelSubDescriptor", rfc5646SpokenLanguage, mgaSoundfieldLabelSubDescriptor.getRFC5646SpokenLanguage()));
737+
}
738+
}
724739
}
725740
return rfc5646SpokenLanguage;
726741
}
@@ -789,6 +804,15 @@ public boolean hasIABEssenceDescriptor()
789804
return this.hasInterchangeObject(IABEssenceDescriptor.class);
790805
}
791806

807+
/**
808+
* Checks if this HeaderPartition object has a MGA Sound Essence Descriptor
809+
* @return true/false depending on whether this HeaderPartition contains a MGASoundEssenceDescriptor or not
810+
*/
811+
public boolean hasMGASoundEssenceDescriptor()
812+
{
813+
return this.hasInterchangeObject(MGASoundEssenceDescriptor.class);
814+
}
815+
792816
/**
793817
* Checks if this HeaderPartition object has a CDCI Picture Essence Descriptor
794818
* @return true/false depending on whether this HeaderPartition contains a CDCIPictureEssenceDescriptor or not
@@ -875,6 +899,15 @@ public List<InterchangeObject> getIABSoundFieldLabelSubDescriptors()
875899
return this.getInterchangeObjects(IABSoundfieldLabelSubDescriptor.class);
876900
}
877901

902+
/**
903+
* Gets all the MGA Soundfield Group Label SubDescriptors associated with this HeaderPartition object
904+
* @return list MGA Soundfield Group Label SubDescriptors contained in this header partition
905+
*/
906+
public List<InterchangeObject> getMGASoundfieldGroupLabelSubDescriptors()
907+
{
908+
return this.getInterchangeObjects(MGASoundfieldGroupLabelSubDescriptor.class);
909+
}
910+
878911
/**
879912
* Gets the timeline track associated with this HeaderPartition object corresponding to the specified UID. Returns
880913
* null if none is found
@@ -1276,9 +1309,12 @@ public List<EssenceTypeEnum> getEssenceTypes() {
12761309
if(interchangeObjectBO.getClass().getEnclosingClass().equals(WaveAudioEssenceDescriptor.class)){
12771310
essenceTypes.add(EssenceTypeEnum.MainAudioEssence);
12781311
}
1279-
if(interchangeObjectBO.getClass().getEnclosingClass().equals(IABEssenceDescriptor.class)){
1312+
else if(interchangeObjectBO.getClass().getEnclosingClass().equals(IABEssenceDescriptor.class)){
12801313
essenceTypes.add(EssenceTypeEnum.IABEssence);
12811314
}
1315+
else if(interchangeObjectBO.getClass().getEnclosingClass().equals(MGASoundEssenceDescriptor.class)){
1316+
essenceTypes.add(EssenceTypeEnum.MGASADMEssence);
1317+
}
12821318
else if(interchangeObjectBO.getClass().getEnclosingClass().equals(CDCIPictureEssenceDescriptor.class)){
12831319
essenceTypes.add(EssenceTypeEnum.MainImageEssence);
12841320
}
@@ -1315,6 +1351,7 @@ public enum EssenceTypeEnum {
13151351
ForcedNarrativeEssence(Composition.SequenceTypeEnum.ForcedNarrativeSequence),
13161352
AncillaryDataEssence(Composition.SequenceTypeEnum.AncillaryDataSequence),
13171353
IABEssence(Composition.SequenceTypeEnum.IABSequence),
1354+
MGASADMEssence(Composition.SequenceTypeEnum.MGASADMSignalSequence),
13181355
UnsupportedEssence(Composition.SequenceTypeEnum.UnsupportedSequence);
13191356

13201357
private final Composition.SequenceTypeEnum sequenceType;
@@ -1352,6 +1389,8 @@ private static EssenceTypeEnum getEssenceTypeEnum(String name)
13521389
return AncillaryDataEssence;
13531390
case "IABEssence":
13541391
return IABEssence;
1392+
case "MGASADMEssence":
1393+
return MGASADMEssence;
13551394
case "UnsupportedEssence":
13561395
default:
13571396
return UnsupportedEssence;
@@ -1384,6 +1423,8 @@ private static String getEssenceTypeString(Composition.SequenceTypeEnum sequence
13841423
return "AncillaryDataEssence";
13851424
case IABSequence:
13861425
return "IABEssence";
1426+
case MGASADMSignalSequence:
1427+
return "MGASADMEssence";
13871428
case UnsupportedSequence:
13881429
default:
13891430
return "UnsupportedEssence";

0 commit comments

Comments
 (0)