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 to retrieve all the VirtualTracks that are a part of a Composition * * @param cpl - a payload corresponding to the Composition Playlist * @return list of VirtualTracks * @throws IOException - any I/O related error is exposed through an IOException */ public static List<? extends VirtualTrack> getVirtualTracks(PayloadRecord cpl) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); List<ErrorLogger.ErrorObject> errorList = validateCPL(cpl); imfErrorLogger.addAllErrors(errorList); if (imfErrorLogger.hasFatalErrors()) { throw new IMFException("Virtual track failed validation", imfErrorLogger); } Composition composition = new Composition(new ByteArrayByteRangeProvider(cpl.getPayload())); return composition.getVirtualTracks(); }
/** * 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(); }
/** * A stateless method that can be used to determine if a Composition is conformant. Conformance * checks perform deeper inspection of the Composition and the EssenceDescriptors corresponding to * all the Virtual Tracks that are a part of the Composition * * @param cplPayloadRecord a payload record corresponding to the Composition payload * @param essencesHeaderPartitionPayloads list of payload records containing the raw bytes of the * HeaderPartitions of the IMF Track files that are a part of the Virtual Track/s in the * Composition * @return list of error messages encountered while performing conformance validation of the * Composition document * @throws IOException - any I/O related error is exposed through an IOException */ public static List<ErrorLogger.ErrorObject> areAllVirtualTracksInCPLConformed( PayloadRecord cplPayloadRecord, List<PayloadRecord> essencesHeaderPartitionPayloads) throws IOException { IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl(); Composition composition = new Composition(new ByteArrayByteRangeProvider(cplPayloadRecord.getPayload())); imfErrorLogger.addAllErrors(composition.getErrors()); List<VirtualTrack> virtualTracks = new ArrayList<>(composition.getVirtualTracks()); imfErrorLogger.addAllErrors( checkVirtualTrackAndEssencesHeaderPartitionPayloadRecords( virtualTracks, essencesHeaderPartitionPayloads)); if (imfErrorLogger.hasFatalErrors()) { return imfErrorLogger.getErrors(); } imfErrorLogger.addAllErrors( conformVirtualTracksInCPL(cplPayloadRecord, essencesHeaderPartitionPayloads, true)); return imfErrorLogger.getErrors(); }
/** * A stateless method that determines if the Asset type of the payload is an IMF AssetMap, * Packinglist or Composition * * @param payloadRecord - a payload record corresponding to the asset whose type needs to be * confirmed Note: for now this method only supports text/xml documents identified in the PKL * application/mxf asset types cannot be determined. * @return asset type of the payload either one of AssetMap, PackingList or Composition * @throws IOException - any I/O related error is exposed through an IOException */ public static PayloadRecord.PayloadAssetType getPayloadType(PayloadRecord payloadRecord) throws IOException { ResourceByteRangeProvider resourceByteRangeProvider = new ByteArrayByteRangeProvider(payloadRecord.getPayload()); if (AssetMap.isFileOfSupportedSchema(resourceByteRangeProvider)) { return PayloadRecord.PayloadAssetType.AssetMap; } else if (PackingList.isFileOfSupportedSchema(resourceByteRangeProvider)) { return PayloadRecord.PayloadAssetType.PackingList; } else if (Composition.isCompositionPlaylist(resourceByteRangeProvider)) { return PayloadRecord.PayloadAssetType.CompositionPlaylist; } return PayloadRecord.PayloadAssetType.Unknown; }
/** * 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(); }