public static List<ErrorLogger.ErrorObject> conformVirtualTracksInCPL( PayloadRecord cplPayloadRecord, List<PayloadRecord> essencesHeaderPartitionPayloads, boolean conformAllVirtualTracks) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); List<PayloadRecord> essencesHeaderPartition = Collections.unmodifiableList(essencesHeaderPartitionPayloads); try { imfErrorLogger.addAllErrors(validateCPL(cplPayloadRecord)); if (imfErrorLogger.hasFatalErrors()) return Collections.unmodifiableList(imfErrorLogger.getErrors()); Composition composition = new Composition(new ByteArrayByteRangeProvider(cplPayloadRecord.getPayload())); imfErrorLogger.addAllErrors(validateIMFTrackFileHeaderMetadata(essencesHeaderPartition)); List<HeaderPartitionTuple> headerPartitionTuples = new ArrayList<>(); for (PayloadRecord payloadRecord : essencesHeaderPartition) { if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Payload asset type is %s, expected asset type %s", payloadRecord.getPayloadAssetType(), PayloadRecord.PayloadAssetType.EssencePartition.toString())); continue; } headerPartitionTuples.add( new HeaderPartitionTuple( new HeaderPartition( new ByteArrayDataProvider(payloadRecord.getPayload()), 0L, (long) payloadRecord.getPayload().length, imfErrorLogger), new ByteArrayByteRangeProvider(payloadRecord.getPayload()))); } if (imfErrorLogger.hasFatalErrors()) { return imfErrorLogger.getErrors(); } imfErrorLogger.addAllErrors( composition.conformVirtualTracksInComposition( Collections.unmodifiableList(headerPartitionTuples), conformAllVirtualTracks)); imfErrorLogger.addAllErrors(composition.getErrors()); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } return imfErrorLogger.getErrors(); }
/** * A stateless method that validates an IMFEssenceComponent's header partition and verifies MXF * OP1A and IMF compliance. This could be utilized to perform preliminary validation of IMF * essences * * @param essencesHeaderPartitionPayloads - a list of IMF Essence Component header partition * payloads * @return a list of errors encountered while performing compliance checks on the IMF Essence * Component Header partition * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> validateIMFTrackFileHeaderMetadata( List<PayloadRecord> essencesHeaderPartitionPayloads) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); List<PayloadRecord> essencesHeaderPartition = Collections.unmodifiableList(essencesHeaderPartitionPayloads); for (PayloadRecord payloadRecord : essencesHeaderPartition) { if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Payload asset type is %s, expected asset type %s", payloadRecord.getPayloadAssetType(), PayloadRecord.PayloadAssetType.EssencePartition.toString())); continue; } HeaderPartition headerPartition = null; try { headerPartition = new HeaderPartition( new ByteArrayDataProvider(payloadRecord.getPayload()), 0L, (long) payloadRecord.getPayload().length, imfErrorLogger); MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A = MXFOperationalPattern1A.checkOperationalPattern1ACompliance( headerPartition, imfErrorLogger); IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger); } catch (IMFException | MXFException e) { if (headerPartition != null) { Preface preface = headerPartition.getPreface(); GenericPackage genericPackage = preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage(); SourcePackage filePackage = (SourcePackage) genericPackage; UUID packageUUID = filePackage.getPackageMaterialNumberasUUID(); imfErrorLogger.addError( new ErrorLogger.ErrorObject( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "IMFTrackFile with ID %s has fatal errors", packageUUID.toString()))); } if (e instanceof IMFException) { IMFException imfException = (IMFException) e; imfErrorLogger.addAllErrors(imfException.getErrors()); } else if (e instanceof MXFException) { MXFException mxfException = (MXFException) e; imfErrorLogger.addAllErrors(mxfException.getErrors()); } } } return imfErrorLogger.getErrors(); }
/** * A stateless method that will validate an IMF AssetMap document * * @param assetMapPayload - a payload record for an AssetMap document * @return list of error messages encountered while validating an AssetMap document * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> validateAssetMap(PayloadRecord assetMapPayload) throws IOException { if (assetMapPayload.getPayloadAssetType() != PayloadRecord.PayloadAssetType.AssetMap) { throw new IMFException( String.format( "Payload asset type is %s, expected asset type %s", assetMapPayload.getPayloadAssetType(), PayloadRecord.PayloadAssetType.AssetMap.toString())); } try { AssetMap assetMap = new AssetMap(new ByteArrayByteRangeProvider(assetMapPayload.getPayload())); return assetMap.getErrors(); } catch (IMFException e) { return e.getErrors(); } }
/** * A stateless method that will validate an IMF PackingList document * * @param pkl - a payload record for a Packing List document * @return list of error messages encountered while validating a Packing List document * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> validatePKL(PayloadRecord pkl) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); if (pkl.getPayloadAssetType() != PayloadRecord.PayloadAssetType.PackingList) { throw new IMFException( String.format( "Payload asset type is %s, expected asset type %s", pkl.getPayloadAssetType(), PayloadRecord.PayloadAssetType.PackingList.toString()), imfErrorLogger); } try { PackingList packingList = new PackingList(new ByteArrayByteRangeProvider(pkl.getPayload())); imfErrorLogger.addAllErrors(packingList.getErrors()); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } return imfErrorLogger.getErrors(); }
/** * A stateless method that will validate an IMF Composition document * * @param cpl - a payload record for a Composition document * @return list of error messages encountered while validating an AssetMap document * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> validateCPL(PayloadRecord cpl) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); if (cpl.getPayloadAssetType() != PayloadRecord.PayloadAssetType.CompositionPlaylist) { throw new IMFException( String.format( "Payload asset type is %s, expected asset type %s", cpl.getPayloadAssetType(), PayloadRecord.PayloadAssetType.CompositionPlaylist.toString())); } try { Composition composition = new Composition(new ByteArrayByteRangeProvider(cpl.getPayload())); imfErrorLogger.addAllErrors(composition.getErrors()); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } return imfErrorLogger.getErrors(); }
private static List<ErrorLogger.ErrorObject> checkVirtualTrackAndEssencesHeaderPartitionPayloadRecords( List<VirtualTrack> virtualTracks, List<PayloadRecord> essencesHeaderPartition) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); Set<UUID> trackFileIDsSet = new HashSet<>(); for (PayloadRecord payloadRecord : essencesHeaderPartition) { if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) { throw new IMFException( String.format( "Payload asset type is %s, expected asset type %s", payloadRecord.getPayloadAssetType(), PayloadRecord.PayloadAssetType.EssencePartition.toString()), imfErrorLogger); } HeaderPartition headerPartition = new HeaderPartition( new ByteArrayDataProvider(payloadRecord.getPayload()), 0L, (long) payloadRecord.getPayload().length, imfErrorLogger); try { /** * Add the Top Level Package UUID to the set of TrackFileIDs, this is required to validate * that the essences header partition that were passed in are in fact from the constituent * resources of the VirtualTack */ MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A = MXFOperationalPattern1A.checkOperationalPattern1ACompliance( headerPartition, imfErrorLogger); IMFConstraints.HeaderPartitionIMF headerPartitionIMF = IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger); Preface preface = headerPartitionIMF.getHeaderPartitionOP1A().getHeaderPartition().getPreface(); GenericPackage genericPackage = preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage(); SourcePackage filePackage = (SourcePackage) genericPackage; UUID packageUUID = filePackage.getPackageMaterialNumberasUUID(); trackFileIDsSet.add(packageUUID); } catch (IMFException | MXFException e) { Preface preface = headerPartition.getPreface(); GenericPackage genericPackage = preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage(); SourcePackage filePackage = (SourcePackage) genericPackage; UUID packageUUID = filePackage.getPackageMaterialNumberasUUID(); imfErrorLogger.addError( new ErrorLogger.ErrorObject( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format("IMFTrackFile with ID %s has fatal errors", packageUUID.toString()))); if (e instanceof IMFException) { IMFException imfException = (IMFException) e; imfErrorLogger.addAllErrors(imfException.getErrors()); } else if (e instanceof MXFException) { MXFException mxfException = (MXFException) e; imfErrorLogger.addAllErrors(mxfException.getErrors()); } } } Set<UUID> virtualTrackResourceIDsSet = new HashSet<>(); for (Composition.VirtualTrack virtualTrack : virtualTracks) { if (virtualTrack instanceof IMFEssenceComponentVirtualTrack) { virtualTrackResourceIDsSet.addAll( IMFEssenceComponentVirtualTrack.class.cast(virtualTrack).getTrackResourceIds()); } else { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "VirtualTrack with TrackId %s is not an Essence Component Virtual Track", virtualTrack.getTrackID().toString())); } } /** * Following check ensures that the Header Partitions corresponding to all the Resources of the * VirtualTracks were passed in. */ Set<UUID> unreferencedResourceIDsSet = new HashSet<>(); for (UUID uuid : virtualTrackResourceIDsSet) { if (!trackFileIDsSet.contains(uuid)) { unreferencedResourceIDsSet.add(uuid); } } if (unreferencedResourceIDsSet.size() > 0) { imfErrorLogger.addError( new ErrorLogger.ErrorObject( IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "It seems that no EssenceHeaderPartition data was passed in for " + "VirtualTrack Resource Ids %s, please verify that the correct Header Partition payloads for the " + "Virtual Track were passed in", Utilities.serializeObjectCollectionToString(unreferencedResourceIDsSet)))); } /** * Following check ensures that the Header Partitions corresponding to only the Resource that * are a part of the VirtualTracks were passed in. */ Set<UUID> unreferencedTrackFileIDsSet = new HashSet<>(); for (UUID uuid : trackFileIDsSet) { if (!virtualTrackResourceIDsSet.contains(uuid)) { unreferencedTrackFileIDsSet.add(uuid); } } if (unreferencedTrackFileIDsSet.size() > 0) { imfErrorLogger.addError( new ErrorLogger.ErrorObject( IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "It seems that EssenceHeaderPartition data was passed in for " + "Resource Ids %s which are not part of this virtual track, please verify that only the Header " + "Partition payloads for the Virtual Track were passed in", Utilities.serializeObjectCollectionToString(unreferencedTrackFileIDsSet)))); } return imfErrorLogger.getErrors(); }
/** * A stateless method that determines if 2 or more Composition documents corresponding to the same * title can be inferred to represent the same presentation timeline. This method is present to * work around current limitations in the IMF eco system wherein CPL's might not be built * incrementally to include all the IMF essences that are a part of the same timeline * * @param referenceCPLPayloadRecord - a payload record corresponding to a Reference Composition * document, perhaps the first composition playlist document that was delivered for a * particular composition. * @param cplPayloads - a list of payload records corresponding to each of the Composition * documents that need to be verified for mergeability * @return a boolean indicating if the CPLs can be merged or not * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> isCPLMergeable( PayloadRecord referenceCPLPayloadRecord, List<PayloadRecord> cplPayloads) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); List<PayloadRecord> cplPayloadRecords = Collections.unmodifiableList(cplPayloads); List<Composition> compositions = new ArrayList<>(); try { compositions.add( new Composition(new ByteArrayByteRangeProvider(referenceCPLPayloadRecord.getPayload()))); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } for (PayloadRecord cpl : cplPayloadRecords) { try { compositions.add(new Composition(new ByteArrayByteRangeProvider(cpl.getPayload()))); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } } if (imfErrorLogger.hasFatalErrors()) { return imfErrorLogger.getErrors(); } VirtualTrack referenceVideoVirtualTrack = compositions.get(0).getVideoVirtualTrack(); UUID referenceCPLUUID = compositions.get(0).getUUID(); for (int i = 1; i < compositions.size(); i++) { if (!referenceVideoVirtualTrack.equivalent(compositions.get(i).getVideoVirtualTrack())) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.WARNING, String.format( "CPL Id %s can't be merged with Reference CPL Id %s, since the video virtual tracks do not seem to represent the same timeline.", compositions.get(i).getUUID(), referenceCPLUUID)); } } /** * Perform AudioTrack mergeability checks 1) Identify AudioTracks that are the same language 2) * Compare language tracks to see if they represent the same timeline */ Boolean bAudioVirtualTrackMapFail = false; List<Map<Set<DOMNodeObjectModel>, ? extends VirtualTrack>> audioVirtualTracksMapList = new ArrayList<>(); for (Composition composition : compositions) { try { audioVirtualTracksMapList.add(composition.getAudioVirtualTracksMap()); } catch (IMFException e) { bAudioVirtualTrackMapFail = false; imfErrorLogger.addAllErrors(e.getErrors()); } } if (!bAudioVirtualTrackMapFail) { Map<Set<DOMNodeObjectModel>, ? extends VirtualTrack> referenceAudioVirtualTracksMap = audioVirtualTracksMapList.get(0); for (int i = 1; i < audioVirtualTracksMapList.size(); i++) { if (!compareAudioVirtualTrackMaps( Collections.unmodifiableMap(referenceAudioVirtualTracksMap), Collections.unmodifiableMap(audioVirtualTracksMapList.get(i)), imfErrorLogger)) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.WARNING, String.format( "CPL Id %s can't be merged with Reference CPL Id %s, since 2 same language audio tracks do not seem to represent the same timeline.", compositions.get(i).getUUID(), referenceCPLUUID)); } } } /** Perform MarkerTrack mergeability checks */ Composition.VirtualTrack referenceMarkerVirtualTrack = compositions.get(0).getMarkerVirtualTrack(); if (referenceMarkerVirtualTrack != null) { UUID referenceMarkerCPLUUID = compositions.get(0).getUUID(); for (int i = 1; i < compositions.size(); i++) { if (!referenceVideoVirtualTrack.equivalent(compositions.get(i).getMarkerVirtualTrack())) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_CPL_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.WARNING, String.format( "CPL Id %s can't be merged with Reference CPL Id %s, since the marker virtual tracks do not seem to represent the same timeline.", compositions.get(i).getUUID(), referenceMarkerCPLUUID)); } } } return imfErrorLogger.getErrors(); }
/** * A stateless method that will validate IMF AssetMap and PackingList documents for all the data * that should be cross referenced by both * * @param assetMapPayload - a payload record for an AssetMap document * @param pklPayloads - a list of payload records for Packing List documents referenced by the * AssetMap * @return list of error messages encountered while validating an AssetMap document * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> validatePKLAndAssetMap( PayloadRecord assetMapPayload, List<PayloadRecord> pklPayloads) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); List<PayloadRecord> packingListPayloadRecords = Collections.unmodifiableList(pklPayloads); if (assetMapPayload.getPayloadAssetType() != PayloadRecord.PayloadAssetType.AssetMap) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Payload asset type is %s, expected asset type %s", assetMapPayload.getPayloadAssetType(), PayloadRecord.PayloadAssetType.AssetMap.toString())); } ResourceByteRangeProvider assetMapByteRangeProvider = new ByteArrayByteRangeProvider(assetMapPayload.getPayload()); AssetMap assetMapObjectModel = null; try { assetMapObjectModel = new AssetMap(assetMapByteRangeProvider); imfErrorLogger.addAllErrors(assetMapObjectModel.getErrors()); if (assetMapObjectModel.getPackingListAssets().size() == 0) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Asset map should reference atleast one PackingList, %d " + "references found", assetMapObjectModel.getPackingListAssets().size())); } } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); } List<ResourceByteRangeProvider> packingLists = new ArrayList<>(); for (PayloadRecord payloadRecord : packingListPayloadRecords) { if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.PackingList) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Payload asset type is %s, expected asset type %s", assetMapPayload.getPayloadAssetType(), PayloadRecord.PayloadAssetType.PackingList.toString())); } else { packingLists.add(new ByteArrayByteRangeProvider(payloadRecord.getPayload())); } } if (packingLists.size() == 0) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "Atleast one PackingList is expected, %d were detected", packingLists.size())); } if (imfErrorLogger.hasFatalErrors()) { return imfErrorLogger.getErrors(); } List<PackingList> packingListObjectModels = new ArrayList<>(); for (ResourceByteRangeProvider resourceByteRangeProvider : packingLists) { try { PackingList packingList = new PackingList(resourceByteRangeProvider); packingListObjectModels.add(packingList); imfErrorLogger.addAllErrors(packingList.getErrors()); } catch (IMFException e) { imfErrorLogger.addAllErrors(e.getErrors()); return imfErrorLogger.getErrors(); } } List<UUID> assetUUIDsAssetMapList = new ArrayList<>(); for (AssetMap.Asset asset : assetMapObjectModel.getAssetList()) { assetUUIDsAssetMapList.add(asset.getUUID()); } /* //Sort the UUIDs in the AssetMap assetUUIDsAssetMapList.sort(new Comparator<UUID>() { @Override public int compare(UUID o1, UUID o2) { return o1.compareTo(o2); } }); */ /* Collect all the assets in all of the PKLs that are a part of this IMP delivery */ List<UUID> assetUUIDsPackingList = new ArrayList<>(); for (PackingList packingList : packingListObjectModels) { assetUUIDsPackingList.add( packingList .getUUID()); // PKL's UUID is also added to this list since that should be present in // the AssetMap for (PackingList.Asset asset : packingList.getAssets()) { assetUUIDsPackingList.add(asset.getUUID()); } } /* //Sort the UUIDs in the PackingList assetUUIDsPackingList.sort(new Comparator<UUID>() { @Override public int compare(UUID o1, UUID o2) { return o1.compareTo(o2); } }); */ /* Check to see if all the Assets referenced in the PKL are also referenced by the Asset Map */ Set<UUID> assetUUIDsAssetMapSet = new HashSet<>(assetUUIDsAssetMapList); Set<UUID> assetUUIDsPKLSet = new HashSet<>(assetUUIDsPackingList); StringBuilder unreferencedPKLAssetsUUIDs = new StringBuilder(); for (UUID uuid : assetUUIDsPKLSet) { if (!assetUUIDsAssetMapSet.contains(uuid)) { unreferencedPKLAssetsUUIDs.append(uuid.toString()); unreferencedPKLAssetsUUIDs.append(", "); } } if (!unreferencedPKLAssetsUUIDs.toString().isEmpty()) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "The following UUID/s %s in the Packing list are not referenced by the AssetMap.", unreferencedPKLAssetsUUIDs.toString())); return imfErrorLogger.getErrors(); } /* Check if all the assets in the AssetMap that are supposed to be PKLs have the same UUIDs as the PKLs themselves */ Set<UUID> packingListAssetsUUIDsSet = new HashSet<>(); for (AssetMap.Asset asset : assetMapObjectModel.getPackingListAssets()) { packingListAssetsUUIDsSet.add(asset.getUUID()); } StringBuilder unreferencedPKLUUIDs = new StringBuilder(); for (PackingList packingList : packingListObjectModels) { if (!packingListAssetsUUIDsSet.contains(packingList.getUUID())) { unreferencedPKLUUIDs.append(packingList.getUUID()); } } if (!unreferencedPKLUUIDs.toString().isEmpty()) { imfErrorLogger.addError( IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR, IMFErrorLogger.IMFErrors.ErrorLevels.FATAL, String.format( "The following Packing lists %s are not referenced in the AssetMap", unreferencedPKLUUIDs.toString())); return imfErrorLogger.getErrors(); } return imfErrorLogger.getErrors(); }