Skip to content

Commit

Permalink
Merge pull request #26 from Netflix/feature/IMFWorkflowRelatedMods
Browse files Browse the repository at this point in the history
Feature/imf workflow related mods
  • Loading branch information
schakrovorthy committed Mar 1, 2016
2 parents acf3ac6 + 1a5c490 commit b819b16
Show file tree
Hide file tree
Showing 10 changed files with 913 additions and 57 deletions.
27 changes: 16 additions & 11 deletions src/main/java/com/netflix/imflibrary/app/IMFEssenceCPLBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
Expand Down Expand Up @@ -134,7 +136,8 @@ public File getCompositionPlaylist() throws IOException {
/*Build ContentVersionList*/
buildContentVersionList();
/*EssenceDescriptorList*/
buildEssenceDescriptorList();
List<String> essenceDescriptorIdsList = Collections.synchronizedList(new LinkedList<String>());
buildEssenceDescriptorList(essenceDescriptorIdsList);
/*CompositionTimeCode*/
buildCompositionTimeCode();
/*Edit Rate*/
Expand All @@ -143,7 +146,7 @@ public File getCompositionPlaylist() throws IOException {
/*Locale List*/
this.buildLocaleList();
/*Segment List*/
this.buildSegmentList();
this.buildSegmentList(essenceDescriptorIdsList);
/*Signature elements*/
this.buildSignatureElements();

Expand Down Expand Up @@ -190,7 +193,7 @@ private void buildExtensionProperties(){
this.cplRoot.setExtensionProperties(null);
}

private void buildEssenceDescriptorList() throws IOException{
private void buildEssenceDescriptorList(List<String> uuidList) throws IOException{

try {
List<EssenceDescriptorBaseType> essenceDescriptorList = this.cplRoot.getEssenceDescriptorList().getEssenceDescriptor();
Expand All @@ -204,7 +207,9 @@ private void buildEssenceDescriptorList() throws IOException{
Document document = docBuilder.newDocument();

EssenceDescriptorBaseType essenceDescriptorBaseType = new EssenceDescriptorBaseType();
essenceDescriptorBaseType.setId(IMFUUIDGenerator.getInstance().getUUID());
String uuid = IMFUUIDGenerator.getInstance().getUUID();
essenceDescriptorBaseType.setId(uuid);
uuidList.add(uuid);

DocumentFragment documentFragment = this.getEssenceDescriptorAsDocumentFragment(document, essenceDescriptorHeader, subDescriptorHeaders);
Node node = documentFragment.getFirstChild();
Expand All @@ -216,7 +221,6 @@ private void buildEssenceDescriptorList() throws IOException{
catch(ParserConfigurationException e){
throw new IMFException(e);
}

}

private void buildCompositionTimeCode(){
Expand Down Expand Up @@ -264,7 +268,7 @@ private void buildSampleLocaleList(){
list.add(localeType);
}

private void buildSegmentList() throws IOException {
private void buildSegmentList(List<String> uuidList) throws IOException {
/*Segment Id*/
List<SegmentType>segments = this.cplRoot.getSegmentList().getSegment();
SegmentType segmentType = new SegmentType();
Expand All @@ -274,7 +278,8 @@ private void buildSegmentList() throws IOException {
segmentType.setAnnotation(buildUserTextType(name, "en"));
/*Sequence List*/
SegmentType.SequenceList sequenceList = new SegmentType.SequenceList();
SequenceType sequenceType = this.buildSequenceType();
int index = 0;
SequenceType sequenceType = this.buildSequenceType(uuidList, index);
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<SequenceType> element = null;
if(this.imfEssenceComponentReader.getEssenceType().equals("MainImageSequence")){
Expand All @@ -292,19 +297,19 @@ else if(this.imfEssenceComponentReader.getEssenceType().equals("MainAudioSequenc
segments.add(segmentType);
}

private SequenceType buildSequenceType() throws IOException {
private SequenceType buildSequenceType(List<String> uuidList, int index) throws IOException {
SequenceType sequenceType = new SequenceType();
/*Id*/
sequenceType.setId(IMFUUIDGenerator.getInstance().getUUID());
/*Track Id*/
String trackId = IMFUUIDGenerator.getInstance().getUUID();
sequenceType.setTrackId(trackId);
/*ResourceList*/
sequenceType.setResourceList(buildTrackResourceList());
sequenceType.setResourceList(buildTrackResourceList(uuidList, index));
return sequenceType;
}

private SequenceType.ResourceList buildTrackResourceList() throws IOException {
private SequenceType.ResourceList buildTrackResourceList(List<String> uuidList, int index) throws IOException {
SequenceType.ResourceList resourceList = new SequenceType.ResourceList();
List<BaseResourceType> baseResourceTypes = resourceList.getResource();
TrackFileResourceType trackFileResourceType = new TrackFileResourceType();
Expand All @@ -323,7 +328,7 @@ private SequenceType.ResourceList buildTrackResourceList() throws IOException {
/*Repeat Count*/
trackFileResourceType.setRepeatCount(BigInteger.valueOf(1));
/*Source Encoding*/
trackFileResourceType.setSourceEncoding(IMFUUIDGenerator.getInstance().getUUID());
trackFileResourceType.setSourceEncoding(uuidList.get(index));/*For the moment we assume that an EssenceDescriptor reference changes only at the Sequence Level*/
/*Track File Id*/
trackFileResourceType.setTrackFileId(IMFUUIDGenerator.getInstance().getUUID());
/*Key Id*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.netflix.imflibrary.imp_validation.cpl;

import com.netflix.imflibrary.exceptions.IMFException;
import com.netflix.imflibrary.reader_interfaces.MXFEssenceReader;
import com.netflix.imflibrary.st2067_2.CompositionPlaylist;
import com.netflix.imflibrary.utils.ResourceByteRangeProvider;
import com.netflix.imflibrary.utils.UUIDHelper;
import org.smpte_ra.schemas.st2067_2_2013.EssenceDescriptorBaseType;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
* This class implements the logic to perform conformance validation of an IMF CompositionPlaylist document.
* Conformance validation will verify that the essences that are described in the CPL EssenceDescriptionList comply with
* IMF-CoreConstraints (SMPTE st2067-2:2013) and that the essence description contained within every essence that the CPL
* references through its virtual track resource list is contained in the EssenceDescription List.
*/
public class CompositionPlaylistConformanceValidator {

/**
* This method can be used to determine if a CompositionPlaylist is conformant. Conformance checks
* perform deeper inspection of the CompositionPlaylist.
* @param compositionPlaylistRecord corresponding to the CompositionPlaylist
* @return boolean to indicate of the CompositionPlaylist is conformant or not
* @throws IOException - any I/O related error is exposed through an IOException.
* @throws IMFException - any non compliant CPL documents will be signalled through an IMFException
* @throws SAXException - exposes any issues with instantiating a {@link javax.xml.validation.Schema Schema} object
* @throws JAXBException - any issues in serializing the XML document using JAXB are exposed through a JAXBException
* @throws URISyntaxException exposes any issues instantiating a {@link java.net.URI URI} object
*/
public boolean isCompositionPlaylistConformed(CompositionPlaylistRecord compositionPlaylistRecord) throws IOException, IMFException, SAXException, JAXBException, URISyntaxException {

/*
* The algorithm for conformance checking a CompositionPlaylist (CPL) would be
* 1) Verify that every EssenceDescriptor element in the EssenceDescriptor list is referenced through its id element
* by at least one TrackFileResource within the Virtual tracks in the CompositionPlaylist (see section 6.1.10 of SMPTE st2067-3:2-13).
* 2) Verify that all track file resources within a virtual track have a corresponding essence descriptor in the essence descriptor list.
* 3) Verify that the EssenceDescriptors in the EssenceDescriptorList element in the CompositionPlaylist are present in
* the physical essence files referenced by the resources of a virtual track and are equal.
*/
CompositionPlaylist compositionPlaylist = compositionPlaylistRecord.getCompositionPlaylist();
List<EssenceDescriptorBaseType> essenceDescriptorList = compositionPlaylist.getCompositionPlaylistType().getEssenceDescriptorList().getEssenceDescriptor();
HashSet<UUID> essenceDescriptorIdsSet = new LinkedHashSet<>();
Map<UUID, List<Node>> eDLMap = new HashMap<>();/*Map <sourceEncodingElement List<Node>> from the EssenceDescriptorList*/
for(EssenceDescriptorBaseType essenceDescriptorBaseType : essenceDescriptorList){
UUID sourceEncodingElement = UUIDHelper.fromUUIDAsURNStringToUUID(essenceDescriptorBaseType.getId());
essenceDescriptorIdsSet.add(sourceEncodingElement);
List<Node> essenceDescriptorNodes = new ArrayList<>();
for(Object object : essenceDescriptorBaseType.getAny()){
essenceDescriptorNodes.add((Node) object);
}
eDLMap.put(sourceEncodingElement, essenceDescriptorNodes);

}

/**
* Get the complete list of SourceEncoding elements in all the TrackFileResources in the CPL.
*/
List<CompositionPlaylist.VirtualTrack>virtualTracks = CompositionPlaylistHelper.getVirtualTracks(compositionPlaylistRecord);
LinkedHashSet<UUID> resourceSourceEncodingElementsSet = new LinkedHashSet<>();
Map<UUID, ResourceByteRangeProvider>sourceEncodingElementByteRangeProviderMap = new HashMap<>();/*Map containing <SourceEncodingElement, ResourceByteRangeProvider> entries*/
for(CompositionPlaylist.VirtualTrack virtualTrack : virtualTracks){
List<CompositionPlaylistHelper.ResourceIdTuple> resourceIdTuples = CompositionPlaylistHelper.getVirtualTrackResourceIDs(virtualTrack);
Map<UUID, ResourceByteRangeProvider> imfEssenceMap = compositionPlaylistRecord.getImfEssenceMap();
for(CompositionPlaylistHelper.ResourceIdTuple resourceIdTuple : resourceIdTuples){
resourceSourceEncodingElementsSet.add(resourceIdTuple.getSourceEncoding());
sourceEncodingElementByteRangeProviderMap.put(resourceIdTuple.getSourceEncoding(), imfEssenceMap.get(resourceIdTuple.getTrackFileId()));
}
}
/*The following check simultaneously verifies 1) and 2) from above.*/
if(!essenceDescriptorIdsSet.equals(resourceSourceEncodingElementsSet)){
throw new IMFException(String.format("At least one of the EssenceDescriptors in the EssenceDescriptorList is not referenced by a TrackFileResource or there is at least one TrackFileResource that is not referenced by a EssenceDescriptor in the EssenceDescriptorList"));
}

/**
* Creating a HashMap <sourceEncodingElement List<Node></>> from reading the metadata in the Essences, and
* using the sourceEncodingElementByteRangeProviderMap.
*/
Map<UUID, List<Node>> essenceDescriptorMap = new LinkedHashMap<>();/*Map <sourceEncodingElement List<Node>> from the physical essence files*/
for(Map.Entry entry : sourceEncodingElementByteRangeProviderMap.entrySet()){
MXFEssenceReader mxfEssenceReader = new MXFEssenceReader(sourceEncodingElementByteRangeProviderMap.get((UUID) entry.getKey()));
if(essenceDescriptorMap.get((UUID) entry.getKey()) == null) {
essenceDescriptorMap.put((UUID) entry.getKey(), mxfEssenceReader.getEssenceDescriptors());
}
}

/**
* An exhaustive compare of the eDLMap and essenceDescriptorMap is required to ensure that the essence descriptors
* in the EssenceDescriptorList and the EssenceDescriptors in the physical essence files corresponding to the
* same source encoding element as indicated in the TrackFileResource and EDL are a good match.
*/
Map<UUID, List<DOMNodeObjectModel>> virtualTracksEssenceDescriptorsMap = getEssenceDescriptorsObjectModel(essenceDescriptorMap);
Map<UUID, List<DOMNodeObjectModel>> cplEDLEssenceDescriptorsMap = getEssenceDescriptorsObjectModel(eDLMap);

/**
* We now have the DOMObjectModel for every EssenceDescriptor in the EssenceDescriptorList in the CPL and
* the essence descriptor in each of the essences referenced from the track file resource within each
* virtual track.
*/
for(Map.Entry entry : cplEDLEssenceDescriptorsMap.entrySet()){
if(cplEDLEssenceDescriptorsMap.get(entry.getKey()).size() > 0){
List<DOMNodeObjectModel> cplEDList = cplEDLEssenceDescriptorsMap.get(entry.getKey());
List<DOMNodeObjectModel> virtualTrackEDList = virtualTracksEssenceDescriptorsMap.get(entry.getKey());
for(DOMNodeObjectModel domNodeObjectModel : cplEDList){
boolean intermediateResult = false;
for(DOMNodeObjectModel otherDomNodeObjectModel : virtualTrackEDList) {
intermediateResult |= domNodeObjectModel.equals(otherDomNodeObjectModel);
if(intermediateResult){
break;
}
}
if(!intermediateResult) {
return false;
}
}
}
}
return true;
}

private Map<UUID, List<DOMNodeObjectModel>> getEssenceDescriptorsObjectModel(Map<UUID, List<Node>> map){
Map<UUID, List<DOMNodeObjectModel>> essenceDescriptorsMap = new LinkedHashMap<>();
for(Map.Entry entry : map.entrySet()){
List<Node> cplEssenceDescriptorNodes = (List<Node>) entry.getValue(); /*List of nodes corresponding to this essence's essence descriptors*/
/**
* Every essence descriptor in the CPL's EDL would be represented using a map as its object model.
* In this case the implementation allows for multiple essence descriptors of the same type in the EDL, for e.g.
* multiple CDCIPictureEssenceDescriptors in a single essence.
*/
List<DOMNodeObjectModel> essenceDescriptorsNodeList = new ArrayList<>();
for(Node node : cplEssenceDescriptorNodes){
DOMNodeObjectModel DOMNodeObjectModel = CompositionPlaylistHelper.getObjectModelForDOMNode(node);
essenceDescriptorsNodeList.add(DOMNodeObjectModel);
}
essenceDescriptorsMap.put((UUID)entry.getKey(), essenceDescriptorsNodeList);
}
return essenceDescriptorsMap;
}

}
Loading

0 comments on commit b819b16

Please sign in to comment.