public AssembledIMPResult assembleIMFFromFiles()

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()
        );
    }