/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.castlabs.dash.dashfragmenter.formats.csf; import com.coremedia.iso.boxes.*; import com.coremedia.iso.boxes.fragment.MovieFragmentBox; import com.coremedia.iso.boxes.fragment.TrackRunBox; import com.googlecode.mp4parser.BasicContainer; import com.googlecode.mp4parser.authoring.Movie; import com.googlecode.mp4parser.authoring.builder.FragmentedMp4Builder; import com.googlecode.mp4parser.boxes.threegpp26244.SegmentIndexBox; import com.googlecode.mp4parser.util.Path; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import static com.castlabs.dash.helpers.Timing.getPtss; import static com.castlabs.dash.helpers.Timing.getTimeMappingEditTime; import static com.googlecode.mp4parser.util.CastUtils.l2i; /** * Creates a fragmented Dash conforming MP4 file. */ public class DashBuilder extends FragmentedMp4Builder { public DashBuilder() { } private SegmentIndexBox createSidx(BasicContainer isoFile, List<Box> moofMdats, long offsetBetweenSidxAndFirstMoof) { SegmentIndexBox sidx = new SegmentIndexBox(); sidx.setVersion(0); sidx.setFlags(0); sidx.setReserved(0); sidx.setFirstOffset(offsetBetweenSidxAndFirstMoof); List<SegmentIndexBox.Entry> entries = sidx.getEntries(); MovieFragmentBox firstMoof = null; for (Box moofMdat : moofMdats) { if (moofMdat.getType().equals("moof")) { if (firstMoof == null) { firstMoof = (MovieFragmentBox) moofMdat; } entries.add(new SegmentIndexBox.Entry()); } } TrackHeaderBox tkhd = Path.getPath(isoFile, "/moov[0]/trak[0]/tkhd[0]"); MediaHeaderBox mdhd = Path.getPath(isoFile, "/moov[0]/trak[0]/mdia[0]/mdhd[0]"); sidx.setReferenceId(tkhd.getTrackId()); sidx.setTimeScale(mdhd.getTimescale()); // we only have one TrackRunBox trun = firstMoof.getTrackRunBoxes().get(0); long[] ptss = getPtss(trun); Arrays.sort(ptss); // index 0 has now the earliest presentation time stamp! long timeMappingEdit = getTimeMappingEditTime(isoFile); sidx.setEarliestPresentationTime(ptss[0] - timeMappingEdit); // ugly code ... int size = 0; int i = 0; MovieFragmentBox lassMoof = null; for (Box moofMdat : moofMdats) { if (moofMdat.getType().equals("moof") && size > 0) { SegmentIndexBox.Entry entry = entries.get(i++); entry.setReferencedSize(size); ptss = getPtss(Path.<TrackRunBox>getPath(lassMoof, "traf[0]/trun[0]")); entry.setSapType(getFirstFrameSapType(ptss)); entry.setSubsegmentDuration(getTrunDuration(Path.<TrackRunBox>getPath(lassMoof, "traf[0]/trun[0]"))); entry.setStartsWithSap((byte) 1); // we know it - no need to lookup size = l2i(moofMdat.getSize()); } else { size += l2i(moofMdat.getSize()); } if (moofMdat.getType().equals("moof")) { lassMoof = (MovieFragmentBox) moofMdat; } } SegmentIndexBox.Entry entry = entries.get(i); ptss = getPtss(Path.<TrackRunBox>getPath(lassMoof, "traf[0]/trun[0]")); entry.setSapType(getFirstFrameSapType(ptss)); entry.setSubsegmentDuration(getTrunDuration(Path.<TrackRunBox>getPath(lassMoof, "traf[0]/trun[0]"))); entry.setReferencedSize(size); entry.setStartsWithSap((byte) 1); // we know it - no need to lookup return sidx; } protected long getTrunDuration(TrackRunBox trun) { final List<TrackRunBox.Entry> trunEntries = trun.getEntries(); long duration = 0; for (TrackRunBox.Entry trunEntry : trunEntries) { duration += trunEntry.getSampleDuration(); } return duration; } protected byte getFirstFrameSapType(long[] ptss) { long idrTimeStamp = ptss[0]; Arrays.sort(ptss); if (idrTimeStamp > ptss[0]) { return 0; } else { return 1; } } @Override public Container build(Movie movie) { if (movie.getTracks().size() != 1) { throw new RuntimeException("Only onetrack allowed"); } BasicContainer isoFile = new BasicContainer(); isoFile.addBox(createFtyp(movie)); isoFile.addBox(createMoov(movie)); List<Box> moofMdats = createMoofMdat(movie); isoFile.addBox(createSidx(isoFile, moofMdats, 0)); for (Box box : moofMdats) { isoFile.addBox(box); } isoFile.addBox(createMfra(movie, isoFile)); return isoFile; } @Override public Box createFtyp(Movie movie) { List<String> minorBrands = new LinkedList<String>(); minorBrands.add("mp42"); minorBrands.add("dash"); minorBrands.add("msdh"); minorBrands.add("msix"); minorBrands.add("iso6"); minorBrands.add("avc1"); minorBrands.add("isom"); return new FileTypeBox("iso6", 1, minorBrands); } }