1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/*
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Copyright 2012 Sebastian Annies, Hamburg
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Licensed under the Apache License, Version 2.0 (the License);
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * you may not use this file except in compliance with the License.
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * You may obtain a copy of the License at
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *     http://www.apache.org/licenses/LICENSE-2.0
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Unless required by applicable law or agreed to in writing, software
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * distributed under the License is distributed on an AS IS BASIS,
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * See the License for the specific language governing permissions and
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * limitations under the License.
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupackage com.googlecode.mp4parser.authoring.adaptivestreaming;
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.IsoFile;
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.Box;
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.SoundMediaHeaderBox;
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.VideoMediaHeaderBox;
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.fragment.MovieFragmentBox;
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.Movie;
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.Track;
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.builder.*;
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.tracks.ChangeTimeScaleTrack;
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.File;
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.FileOutputStream;
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.FileWriter;
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.IOException;
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.channels.FileChannel;
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.Iterator;
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.logging.Logger;
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupublic class FlatPackageWriterImpl implements PackageWriter {
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private static Logger LOG = Logger.getLogger(FlatPackageWriterImpl.class.getName());
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    long timeScale = 10000000;
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private File outputDirectory;
41dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private boolean debugOutput;
42dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private FragmentedMp4Builder ismvBuilder;
43dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    ManifestWriter manifestWriter;
44dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
45dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public FlatPackageWriterImpl() {
46dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ismvBuilder = new FragmentedMp4Builder();
47dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl();
48dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ismvBuilder.setIntersectionFinder(intersectionFinder);
49dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
50dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
51dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
52dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /**
53dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * Creates a factory for a smooth streaming package. A smooth streaming package is
54dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * a collection of files that can be served by a webserver as a smooth streaming
55dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * stream.
56dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @param minFragmentDuration the smallest allowable duration of a fragment (0 == no restriction).
57dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     */
58dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public FlatPackageWriterImpl(int minFragmentDuration) {
59dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ismvBuilder = new FragmentedMp4Builder();
60dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl(minFragmentDuration);
61dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ismvBuilder.setIntersectionFinder(intersectionFinder);
62dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
63dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
64dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
65dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setOutputDirectory(File outputDirectory) {
66dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        assert outputDirectory.isDirectory();
67dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.outputDirectory = outputDirectory;
68dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
69dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
70dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
71dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setDebugOutput(boolean debugOutput) {
72dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.debugOutput = debugOutput;
73dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
74dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
75dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setIsmvBuilder(FragmentedMp4Builder ismvBuilder) {
76dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.ismvBuilder = ismvBuilder;
77dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.manifestWriter = new FlatManifestWriterImpl(ismvBuilder.getFragmentIntersectionFinder());
78dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
79dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
80dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setManifestWriter(ManifestWriter manifestWriter) {
81dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.manifestWriter = manifestWriter;
82dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
83dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
84dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /**
85dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * Writes the movie given as <code>qualities</code> flattened into the
86dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * <code>outputDirectory</code>.
87dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     *
88dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @param source the source movie with all qualities
89dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @throws IOException
90dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     */
91dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void write(Movie source) throws IOException {
92dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
93dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (debugOutput) {
94dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            outputDirectory.mkdirs();
95dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
96dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoFile muxed = defaultMp4Builder.build(source);
97dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            File muxedFile = new File(outputDirectory, "debug_1_muxed.mp4");
98dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
99dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            muxed.getBox(muxedFileOutputStream.getChannel());
100dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            muxedFileOutputStream.close();
101dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
102dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        Movie cleanedSource = removeUnknownTracks(source);
103dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        Movie movieWithAdjustedTimescale = correctTimescale(cleanedSource);
104dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
105dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (debugOutput) {
106dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
107dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoFile muxed = defaultMp4Builder.build(movieWithAdjustedTimescale);
108dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            File muxedFile = new File(outputDirectory, "debug_2_timescale.mp4");
109dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
110dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            muxed.getBox(muxedFileOutputStream.getChannel());
111dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            muxedFileOutputStream.close();
112dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
113dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        IsoFile isoFile = ismvBuilder.build(movieWithAdjustedTimescale);
114dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (debugOutput) {
115dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            File allQualities = new File(outputDirectory, "debug_3_fragmented.mp4");
116dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            FileOutputStream allQualis = new FileOutputStream(allQualities);
117dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            isoFile.getBox(allQualis.getChannel());
118dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            allQualis.close();
119dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
120dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
121dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
122dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (Track track : movieWithAdjustedTimescale.getTracks()) {
123dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            String bitrate = Long.toString(manifestWriter.getBitrate(track));
124dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long trackId = track.getTrackMetaData().getTrackId();
125dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            Iterator<Box> boxIt = isoFile.getBoxes().iterator();
126dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            File mediaOutDir;
127dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (track.getMediaHeaderBox() instanceof SoundMediaHeaderBox) {
128dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                mediaOutDir = new File(outputDirectory, "audio");
129dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
130dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else if (track.getMediaHeaderBox() instanceof VideoMediaHeaderBox) {
131dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                mediaOutDir = new File(outputDirectory, "video");
132dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
133dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                System.err.println("Skipping Track with handler " + track.getHandler() + " and " + track.getMediaHeaderBox().getClass().getSimpleName());
134dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                continue;
135dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
136dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            File bitRateOutputDir = new File(mediaOutDir, bitrate);
137dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bitRateOutputDir.mkdirs();
138dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.finer("Created : " + bitRateOutputDir.getCanonicalPath());
139dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
140dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long[] fragmentTimes = manifestWriter.calculateFragmentDurations(track, movieWithAdjustedTimescale);
141dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long startTime = 0;
142dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int currentFragment = 0;
143dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            while (boxIt.hasNext()) {
144dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                Box b = boxIt.next();
145dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (b instanceof MovieFragmentBox) {
146dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    assert ((MovieFragmentBox) b).getTrackCount() == 1;
147dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (((MovieFragmentBox) b).getTrackNumbers()[0] == trackId) {
148dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        FileOutputStream fos = new FileOutputStream(new File(bitRateOutputDir, Long.toString(startTime)));
149dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        startTime += fragmentTimes[currentFragment++];
150dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        FileChannel fc = fos.getChannel();
151dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        Box mdat = boxIt.next();
152dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        assert mdat.getType().equals("mdat");
153dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        b.getBox(fc); // moof
154dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        mdat.getBox(fc); // mdat
155dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        fc.truncate(fc.position());
156dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        fc.close();
157dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
158dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
159dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
160dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
161dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
162dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        FileWriter fw = new FileWriter(new File(outputDirectory, "Manifest"));
163dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        fw.write(manifestWriter.getManifest(movieWithAdjustedTimescale));
164dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        fw.close();
165dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
166dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
167dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
168dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private Movie removeUnknownTracks(Movie source) {
169dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        Movie nuMovie = new Movie();
170dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (Track track : source.getTracks()) {
171dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if ("vide".equals(track.getHandler()) || "soun".equals(track.getHandler())) {
172dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                nuMovie.addTrack(track);
173dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
174dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                LOG.fine("Removed track " + track);
175dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
176dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
177dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return nuMovie;
178dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
179dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
180dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
181dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /**
182dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * Returns a new <code>Movie</code> in that all tracks have the timescale 10000000. CTS & DTS are modified
183dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * in a way that even with more than one framerate the fragments exactly begin at the same time.
184dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     *
185dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @param movie
186dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @return a movie with timescales suitable for smooth streaming manifests
187dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     */
188dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public Movie correctTimescale(Movie movie) {
189dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        Movie nuMovie = new Movie();
190dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (Track track : movie.getTracks()) {
191dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            nuMovie.addTrack(new ChangeTimeScaleTrack(track, timeScale, ismvBuilder.getFragmentIntersectionFinder().sampleNumbers(track, movie)));
192dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
193dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return nuMovie;
194dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
195dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
196dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
197dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}
198