in src/main/java/com/netflix/imflibrary/writerTools/IMPAssembler.java [56:285]
public AssembledIMPResult assembleIMFFromFiles(SimpleTimeline simpleTimeline, File outputDirectory, boolean copyTrackFiles) throws IOException, JAXBException, ParserConfigurationException, URISyntaxException, SAXException {
Map<UUID, IMPBuilder.IMFTrackFileMetadata> imfTrackFileMetadataMap = new HashMap<>();
IMFErrorLogger imfErrors = new IMFErrorLoggerImpl();
List<Composition.VirtualTrack> virtualTracks = new ArrayList<>();
Map<UUID, UUID> trackFileIdToResourceMap = new HashMap<>();
Map<UUID, List<Long>> sampleRateMap = new HashMap<>();
Map<UUID, BigInteger> sampleCountMap = new HashMap<>();
Map<UUID, byte[]> hashMap = new HashMap<>();
long videoTotalSourceDuration = 0;
for (Track track : simpleTimeline.getEssenceTracks()) {
// build cpl track here
List<IMFTrackFileResourceType> trackFileResources = new ArrayList<>();
for (TrackEntry trackEntry : track.getTrackEntries()) {
if (trackEntry instanceof EssenceTrackEntry) {
EssenceTrackEntry essenceTrackEntry = (EssenceTrackEntry) trackEntry;
logger.info("track: {}, file: {}: path: {}", simpleTimeline.getEssenceTracks().indexOf(track), track.getTrackEntries().indexOf(trackEntry), essenceTrackEntry.getFile().getAbsolutePath());
ResourceByteRangeProvider resourceByteRangeProvider = new FileByteRangeProvider(essenceTrackEntry.getFile());
PayloadRecord headerPartitionPayloadRecord = IMPFixer.getHeaderPartitionPayloadRecord(resourceByteRangeProvider, imfErrors);
if (headerPartitionPayloadRecord == null) {
throw new IOException("Could not get header partition for file: " + essenceTrackEntry.getFile().getAbsolutePath());
}
byte[] headerPartitionBytes = headerPartitionPayloadRecord.getPayload();
// get sha-1 hash or use cached value
byte[] hash = null;
if (essenceTrackEntry.getHash() != null) {
logger.info("Using hash from user: {}", essenceTrackEntry.getHash());
hash = essenceTrackEntry.getHash();
hashMap.put(IMPFixer.getTrackFileId(headerPartitionPayloadRecord), essenceTrackEntry.getHash());
} else if (hashMap.containsKey(IMPFixer.getTrackFileId(headerPartitionPayloadRecord))) {
logger.info("Using cached hash: {}", hashMap.get(IMPFixer.getTrackFileId(headerPartitionPayloadRecord)));
hash = hashMap.get(IMPFixer.getTrackFileId(headerPartitionPayloadRecord));
} else {
logger.info("Generating hash for file: {}", essenceTrackEntry.getFile().getAbsolutePath());
hash = IMFUtils.generateSHA1Hash(resourceByteRangeProvider);
hashMap.put(IMPFixer.getTrackFileId(headerPartitionPayloadRecord), hash);
}
UUID trackFileId = IMPFixer.getTrackFileId(headerPartitionPayloadRecord);
logger.info("UUID read from file: {}: {}", essenceTrackEntry.getFile().getName(), trackFileId.toString());
logger.info("Adding file {} to imfTrackFileMetadataMap", essenceTrackEntry.getFile().getName());
imfTrackFileMetadataMap.put(
trackFileId,
new IMPBuilder.IMFTrackFileMetadata(headerPartitionBytes,
hash, // a byte[] containing the SHA-1, Base64 encoded hash of the IMFTrack file
CompositionPlaylistBuilder_2016.defaultHashAlgorithm,
essenceTrackEntry.getFile().getName(),
resourceByteRangeProvider.getResourceSize())
);
if (copyTrackFiles) {
File outputTrackFile = new File(outputDirectory.getAbsolutePath() + File.separator + essenceTrackEntry.getFile().getName());
logger.info("Copying track file from\n{} to\n{}", essenceTrackEntry.getFile().getAbsolutePath(), outputTrackFile.getAbsolutePath());
Files.copy(essenceTrackEntry.getFile().toPath(), outputTrackFile.toPath(), REPLACE_EXISTING);
}
IMFTrackFileReader imfTrackFileReader = new IMFTrackFileReader(outputDirectory, resourceByteRangeProvider);
// get sample rate or use cached value
List<Long> sampleRate = null;
if (essenceTrackEntry.getSampleRate() != null) {
// if user provided sample rate, use it
sampleRate = Arrays.asList(essenceTrackEntry.getSampleRate().getNumerator(), essenceTrackEntry.getSampleRate().getDenominator());
logger.info("Using sample rate from user: {}/{}", sampleRate.get(0), sampleRate.get(1));
} else if (!sampleRateMap.containsKey(trackFileId)) {
// sample rate has not already been found, find it
sampleRate = imfTrackFileReader.getEssenceEditRateAsList(imfErrors);
sampleRateMap.put(trackFileId, sampleRate);
logger.info("Found sample rate of: {}/{}", sampleRate.get(0), sampleRate.get(1));
} else {
sampleRate = sampleRateMap.get(trackFileId);
logger.info("Using cached sample rate of: {}/{}", sampleRate.get(0), sampleRate.get(1));
}
// get sample count or use cached value
BigInteger sampleCount = null;
if (essenceTrackEntry.getIntrinsicDuration() != null) {
// use sample count provided by user
sampleCount = essenceTrackEntry.getIntrinsicDuration();
logger.info("Intrinsic duration from user: {}", sampleCount);
} else if (!sampleCountMap.containsKey(trackFileId)) {
// compute sample count
sampleCount = imfTrackFileReader.getEssenceDuration(imfErrors);
sampleCountMap.put(trackFileId, sampleCount);
logger.info("Found essence duration of: {}", sampleCount);
} else {
// use cached sample count
sampleCount = sampleCountMap.get(trackFileId);
logger.info("Using cached intrinsic duration of: {}", sampleCount);
}
// delete temporary file left over from FileByteRangeProvider or ByteArrayByteRangeProvider
Path tempFilePath = Paths.get(outputDirectory.getAbsolutePath(), "range");
logger.info("Deleting temporary file if it exists: {}", tempFilePath);
Files.deleteIfExists(tempFilePath);
// add to resources
logger.info("Adding file to resources: {}..", essenceTrackEntry.getFile().getName());
if (track.getSequenceTypeEnum().equals(Composition.SequenceTypeEnum.MainImageSequence)) {
videoTotalSourceDuration += (essenceTrackEntry.getDuration() == null ? sampleCount : essenceTrackEntry.getDuration()).longValue();
}
trackFileResources.add(
new IMFTrackFileResourceType(
UUIDHelper.fromUUID(IMFUUIDGenerator.getInstance().generateUUID()),
UUIDHelper.fromUUID(trackFileId),
sampleRate, // defaults to 1/1
sampleCount,
essenceTrackEntry.getEntryPoint(), // defaults to 0 if null
essenceTrackEntry.getDuration() == null ? sampleCount : essenceTrackEntry.getDuration(), // defaults to intrinsic duration if null
essenceTrackEntry.getRepeatCount(), // defaults to 1 if null
UUIDHelper.fromUUID(getOrGenerateSourceEncoding(trackFileIdToResourceMap, trackFileId)), // used as the essence descriptor id
hash,
CompositionPlaylistBuilder_2016.defaultHashAlgorithm
)
);
}
}
// add to virtual tracks
logger.info("Creating virtual track..");
Composition.VirtualTrack virtualTrack = new IMFEssenceComponentVirtualTrack(
IMFUUIDGenerator.getInstance().generateUUID(),
track.getSequenceTypeEnum(),
trackFileResources,
simpleTimeline.getEditRate()
);
virtualTracks.add(virtualTrack);
}
for (Track track: simpleTimeline.getMarkerTracks()){
List<IMFMarkerResourceType> markerResources = new ArrayList<>();
List<IMFMarkerType> markerList = new ArrayList<>();
for (TrackEntry trackEntry : track.getTrackEntries()) {
if (trackEntry instanceof MarkerTrackEntry) {
MarkerTrackEntry markerTrackEntry = (MarkerTrackEntry) trackEntry;
IMFMarkerType marker = new IMFMarkerType(markerTrackEntry.getAnnotation(), markerTrackEntry.getLabel(), markerTrackEntry.getOffset());
markerList.add(marker);
}
}
List<Long> editRate = new ArrayList<>();
editRate.add(0, simpleTimeline.getEditRate().getNumerator());
editRate.add(1, simpleTimeline.getEditRate().getDenominator());
markerResources.add(new IMFMarkerResourceType(
UUIDHelper.fromUUID(IMFUUIDGenerator.getInstance().generateUUID()),
editRate,
BigInteger.valueOf(videoTotalSourceDuration),
BigInteger.ZERO,
BigInteger.valueOf(videoTotalSourceDuration), // source duration may not be necessary
BigInteger.ONE,
markerList));
logger.info("Creating marker track..");
Composition.VirtualTrack virtualTrack = new IMFMarkerVirtualTrack(IMFUUIDGenerator.getInstance().generateUUID(),
track.getSequenceTypeEnum(),
markerResources,
simpleTimeline.getEditRate());
virtualTracks.add(virtualTrack);
}
logger.debug("Created list of virtual tracks: {}", virtualTracks);
logger.debug("Created track file metadata map: {}", imfTrackFileMetadataMap);
logger.info("Building IMP here: {}...", outputDirectory.getAbsolutePath());
imfErrors.addAllErrors(IMPBuilder.buildIMP_2016("IMP",
"Netflix",
virtualTracks,
simpleTimeline.getEditRate(),
"http://www.smpte-ra.org/schemas/2067-21/2016",
imfTrackFileMetadataMap,
outputDirectory));
logger.info("Listing files in output dir..");
if (outputDirectory.isDirectory()) {
File[] files = outputDirectory.listFiles();
if (files != null) {
for (File file : files) {
if (file != null) {
logger.info("File in output dir: {}", file.getAbsolutePath());
}
}
}
}
BasicMapProfileV2MappedFileSet mapProfileV2MappedFileSet = new BasicMapProfileV2MappedFileSet(outputDirectory);
AssetMap assetMap = mapProfileV2MappedFileSet.getAssetMap();
File assetMapOutputFile = new File(mapProfileV2MappedFileSet.getAbsoluteAssetMapURI());
File pklOutputFile = null;
File cplOutputFile = null;
List<File> outputTrackFiles = new ArrayList<>();
if (assetMap.getPackingListAssets().size() > 1) {
throw new IllegalArgumentException("More than one packing list found in the output asset map");
}
for (AssetMap.Asset packingListAsset: assetMap.getPackingListAssets()) {
if (packingListAsset.isPackingList()) {
pklOutputFile = new File(outputDirectory, packingListAsset.getPath().toString());
PackingList packingList = new PackingList(pklOutputFile);
for (PackingList.Asset asset : packingList.getAssets()) {
logger.debug("Asset from packing list: {}", asset);
if (asset.getType().equals(PackingList.Asset.TEXT_XML_TYPE)
&& ApplicationComposition.isCompositionPlaylist(new FileByteRangeProvider((new File(outputDirectory, asset.getOriginalFilename()))))) {
logger.info("Adding output CPL asset to response: {}", asset);
cplOutputFile = new File(outputDirectory, asset.getOriginalFilename());
} else if (asset.getOriginalFilename() != null) {
logger.info("Adding output track file to response: {}", asset);
outputTrackFiles.add(new File(outputDirectory, asset.getOriginalFilename()));
}
}
}
}
return new AssembledIMPResult(
cplOutputFile,
pklOutputFile,
assetMapOutputFile,
outputTrackFiles,
imfErrors.getErrors()
);
}