Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect sample rate & duration in IMPAssembler, parameterize unit test… #364

Merged
merged 1 commit into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
* A simple application to exercise the core logic of Photon for reading and validating IMF Track files.
*/
@ThreadSafe
final class IMFTrackFileReader
public final class IMFTrackFileReader
{
private final File workingDirectory;
private final ResourceByteRangeProvider resourceByteRangeProvider;
Expand All @@ -89,7 +89,7 @@ final class IMFTrackFileReader
* @param workingDirectory the working directory
* @param resourceByteRangeProvider the MXF file represented as a {@link com.netflix.imflibrary.utils.ResourceByteRangeProvider}
*/
IMFTrackFileReader(File workingDirectory, ResourceByteRangeProvider resourceByteRangeProvider)
public IMFTrackFileReader(File workingDirectory, ResourceByteRangeProvider resourceByteRangeProvider)
{
this.workingDirectory = workingDirectory;
this.resourceByteRangeProvider = resourceByteRangeProvider;
Expand Down Expand Up @@ -602,7 +602,7 @@ BigInteger getEssenceEditRate(@Nonnull IMFErrorLogger imfErrorLogger) throws IOE
* @param imfErrorLogger an error logger for recording any errors - cannot be null
* @return editRate of the essence as a List of Long Integers
*/
List<Long> getEssenceEditRateAsList(@Nonnull IMFErrorLogger imfErrorLogger) throws IOException {
public List<Long> getEssenceEditRateAsList(@Nonnull IMFErrorLogger imfErrorLogger) throws IOException {
if(!(this.getHeaderPartition(imfErrorLogger).getEssenceDescriptors().size() > 0)){
throw new MXFException(String.format("No EssenceDescriptors were found in the MXF essence"));
}
Expand All @@ -615,7 +615,7 @@ List<Long> getEssenceEditRateAsList(@Nonnull IMFErrorLogger imfErrorLogger) thro
* @param imfErrorLogger an error logger for recording any errors - cannot be null
* @return essenceDuration
*/
BigInteger getEssenceDuration(@Nonnull IMFErrorLogger imfErrorLogger) throws IOException {
public BigInteger getEssenceDuration(@Nonnull IMFErrorLogger imfErrorLogger) throws IOException {
return this.getHeaderPartition(imfErrorLogger).getEssenceDuration();
}

Expand Down
66 changes: 56 additions & 10 deletions src/main/java/com/netflix/imflibrary/writerTools/IMPAssembler.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.netflix.imflibrary.IMFErrorLogger;
import com.netflix.imflibrary.IMFErrorLoggerImpl;
import com.netflix.imflibrary.RESTfulInterfaces.PayloadRecord;
import com.netflix.imflibrary.app.IMFTrackFileReader;
import com.netflix.imflibrary.app.IMPFixer;
import com.netflix.imflibrary.st0429_8.PackingList;
import com.netflix.imflibrary.st0429_9.AssetMap;
Expand Down Expand Up @@ -30,6 +31,8 @@
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
Expand All @@ -39,7 +42,6 @@ public class IMPAssembler {
private static final Logger logger = LoggerFactory.getLogger(IMPAssembler.class);



/**
* Generate the CPL, PKL, and AssetMap XML files given a simple timeline of track entries
* Code adapted from IMPFixer
Expand All @@ -52,6 +54,8 @@ public AssembledIMPResult assembleIMFFromFiles(SimpleTimeline simpleTimeline, Fi
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<>();


for (Track track : simpleTimeline.getTracks()) {
Expand Down Expand Up @@ -86,16 +90,58 @@ public AssembledIMPResult assembleIMFFromFiles(SimpleTimeline simpleTimeline, Fi
Files.copy(trackEntry.getFile().toPath(), outputTrackFile.toPath(), REPLACE_EXISTING);
}

IMFTrackFileReader imfTrackFileReader = new IMFTrackFileReader(outputDirectory, resourceByteRangeProvider);

// get sample rate or use cached value
List<Long> sampleRate = null;
if (trackEntry.getSampleRate() != null) {
// if user provided sample rate, use it
sampleRate = Arrays.asList(trackEntry.getSampleRate().getNumerator(), trackEntry.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 (trackEntry.getIntrinsicDuration() != null) {
// use sample count provided by user
sampleCount = trackEntry.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: {}..", trackEntry.getFile().getName());
resources.add(
new IMFTrackFileResourceType(
UUIDHelper.fromUUID(IMFUUIDGenerator.getInstance().generateUUID()),
UUIDHelper.fromUUID(trackFileId),
Arrays.asList(trackEntry.getSampleRate().getNumerator(), trackEntry.getSampleRate().getDenominator()), // defaults to 1/1
trackEntry.getIntrinsicDuration(),
sampleRate, // defaults to 1/1
sampleCount,
trackEntry.getEntryPoint(), // defaults to 0 if null
trackEntry.getDuration(), // defaults to intrinsic duration if null
trackEntry.getDuration() == null ? sampleCount : trackEntry.getDuration(), // defaults to intrinsic duration if null
trackEntry.getRepeatCount(), // defaults to 1 if null
UUIDHelper.fromUUID(getOrGenerateSourceEncoding(trackFileIdToResourceMap, trackFileId)), // used as the essence descriptor id
hash,
Expand Down Expand Up @@ -336,13 +382,13 @@ public static class TrackEntry {
/**
* Constructor for a track entry to be used to construct a simple timeline
* @param file - the MXF file
* @param sampleRate - the sample rate
* @param intrinsicDuration - the intrinsic duration
* @param entryPoint - the entry point (if null, defaults to 0)
* @param duration - the duration (if null, defaults to intrinsic duration)
* @param repeatCount - the repeat count (if null, defaults to 1)
* @param sampleRate - the sample rate, optional, introspected if null
* @param intrinsicDuration - the intrinsic duration, optional, introspected if null
* @param entryPoint - the entry point, if null, defaults to 0
* @param duration - the duration, if null, defaults to intrinsic duration
* @param repeatCount - the repeat count, if null, defaults to 1
*/
public TrackEntry(@Nonnull File file, @Nonnull Composition.EditRate sampleRate, @Nonnull BigInteger intrinsicDuration, @Nullable BigInteger entryPoint, @Nullable BigInteger duration, @Nullable BigInteger repeatCount) {
public TrackEntry(@Nonnull File file, @Nullable Composition.EditRate sampleRate, @Nullable BigInteger intrinsicDuration, @Nullable BigInteger entryPoint, @Nullable BigInteger duration, @Nullable BigInteger repeatCount) {
this.file = file;
this.sampleRate = sampleRate;
this.intrinsicDuration = intrinsicDuration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.netflix.imflibrary.st2067_2.Composition;
import com.netflix.imflibrary.utils.ErrorLogger;
import org.slf4j.Logger;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;
import testUtils.TestHelper;
Expand All @@ -27,48 +28,75 @@ public class IMPAssemblerTest {

private static final Logger logger = org.slf4j.LoggerFactory.getLogger(IMPAssemblerTest.class);

@Test
public void testAssembleIMFFromFiles() throws IOException, JAXBException, ParserConfigurationException, URISyntaxException, SAXException {

IMPAssembler.TrackEntry videoFile1 = new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_00.mxf"),
new Composition.EditRate(60000L, 1001L),
BigInteger.valueOf(10),
BigInteger.valueOf(0),
BigInteger.valueOf(10),
BigInteger.valueOf(1)
);

@DataProvider(name = "trackEntries")
private Object[][] trackEntries() {
return new Object[][] {
{
// video & audio have all values provided by the user
new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_00.mxf"),
new Composition.EditRate(60000L, 1001L),
BigInteger.valueOf(10),
BigInteger.valueOf(0),
BigInteger.valueOf(10),
BigInteger.valueOf(1)
),
new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_ENG-51_00.mxf"),
new Composition.EditRate(48000L, 1L),
BigInteger.valueOf(8008),
BigInteger.valueOf(0),
BigInteger.valueOf(8008),
BigInteger.valueOf(1)
)

},
{
// video & audio values are left null for Photon to figure out
new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_00.mxf"),
null,
null,
null,
null,
null
),
new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_ENG-51_00.mxf"),
null,
null,
null,
null,
null
)
}


};
}

@Test(dataProvider = "trackEntries")
public void testAssembleIMFFromFiles(IMPAssembler.TrackEntry videoTrackEntry, IMPAssembler.TrackEntry audioTrackEntry) throws IOException, JAXBException, ParserConfigurationException, URISyntaxException, SAXException {

IMPAssembler.Track videoTrack = new IMPAssembler.Track();
videoTrack.getTrackEntries().add(videoFile1);
videoTrack.getTrackEntries().add(videoFile1);
videoTrack.getTrackEntries().add(videoTrackEntry);
videoTrack.getTrackEntries().add(videoTrackEntry);
videoTrack.setSequenceTypeEnum(Composition.SequenceTypeEnum.MainImageSequence);
List<IMPAssembler.Track> trackList = new ArrayList<>();
trackList.add(videoTrack);



IMPAssembler.TrackEntry audioFile1 = new IMPAssembler.TrackEntry(
TestHelper.findResourceByPath("TestIMP/MERIDIAN_Netflix_Photon_161006/MERIDIAN_Netflix_Photon_161006_ENG-51_00.mxf"),
new Composition.EditRate(48000L, 1L),
BigInteger.valueOf(8008),
BigInteger.valueOf(0),
BigInteger.valueOf(8008),
BigInteger.valueOf(1)
);

IMPAssembler.Track audioTrack = new IMPAssembler.Track();
audioTrack.getTrackEntries().add(audioFile1);
audioTrack.getTrackEntries().add(audioFile1);
audioTrack.getTrackEntries().add(audioTrackEntry);
audioTrack.getTrackEntries().add(audioTrackEntry);
audioTrack.setSequenceTypeEnum(Composition.SequenceTypeEnum.MainAudioSequence);
trackList.add(audioTrack);

IMPAssembler.SimpleTimeline simpleTimeline = new IMPAssembler.SimpleTimeline(trackList, new Composition.EditRate(Arrays.asList(60000L, 1001L)));



Path outputDirPath = Files.createTempDirectory(Paths.get(System.getProperty("java.io.tmpdir")), "IMPAssemblerTest");
File outputDir = outputDirPath.toFile();
// File outputDirectory = new File("outputDirectory");
IMPAssembler impAssembler = new IMPAssembler();
IMPAssembler.AssembledIMPResult result = impAssembler.assembleIMFFromFiles(simpleTimeline, outputDir, true);

Expand All @@ -95,7 +123,5 @@ public void testAssembleIMFFromFiles() throws IOException, JAXBException, Parser
for (File outputTrackFile : result.getTrackFiles()) {
assert outputTrackFile.exists();
}


}
}
Loading