private InterleaveChunkMdat(Movie movie, Map<Track, int[]> chunks, long contentSize) {
      this.contentSize = contentSize;
      this.tracks = movie.getTracks();
      List<Track> tracks = new ArrayList<Track>(chunks.keySet());
      Collections.sort(
          tracks,
          new Comparator<Track>() {
            public int compare(Track o1, Track o2) {
              return l2i(o1.getTrackMetaData().getTrackId() - o2.getTrackMetaData().getTrackId());
            }
          });
      Map<Track, Integer> trackToChunk = new HashMap<Track, Integer>();
      Map<Track, Integer> trackToSample = new HashMap<Track, Integer>();
      Map<Track, Double> trackToTime = new HashMap<Track, Double>();
      for (Track track : tracks) {
        trackToChunk.put(track, 0);
        trackToSample.put(track, 0);
        trackToTime.put(track, 0.0);
      }

      while (true) {
        Track nextChunksTrack = null;
        for (Track track : tracks) {
          if ((nextChunksTrack == null || trackToTime.get(track) < trackToTime.get(nextChunksTrack))
              &&
              // either first OR track's next chunk's starttime is smaller than nextTrack's next
              // chunks starttime
              // AND their need to be chunks left!
              (trackToChunk.get(track) < chunks.get(track).length)) {
            nextChunksTrack = track;
          }
        }
        if (nextChunksTrack == null) {
          break;
        }
        // found the next one

        int nextChunksIndex = trackToChunk.get(nextChunksTrack);
        int numberOfSampleInNextChunk = chunks.get(nextChunksTrack)[nextChunksIndex];
        int startSample = trackToSample.get(nextChunksTrack);
        double time = trackToTime.get(nextChunksTrack);
        for (int j = startSample; j < startSample + numberOfSampleInNextChunk; j++) {
          time +=
              (double) nextChunksTrack.getSampleDurations()[j]
                  / nextChunksTrack.getTrackMetaData().getTimescale();
        }
        chunkList.add(
            nextChunksTrack
                .getSamples()
                .subList(startSample, startSample + numberOfSampleInNextChunk));

        trackToChunk.put(nextChunksTrack, nextChunksIndex + 1);
        trackToSample.put(nextChunksTrack, startSample + numberOfSampleInNextChunk);
        trackToTime.put(nextChunksTrack, time);
      }
    }
 /**
  * Changes the time scale of the source track to the target time scale and makes sure that any
  * rounding errors that may have summed are corrected exactly before the syncSamples.
  *
  * @param source the source track
  * @param targetTimeScale the resulting time scale of this track.
  * @param syncSamples at these sync points where rounding error are corrected.
  */
 public ChangeTimeScaleTrack(Track source, long targetTimeScale, long[] syncSamples) {
   this.source = source;
   this.timeScale = targetTimeScale;
   double timeScaleFactor = (double) targetTimeScale / source.getTrackMetaData().getTimescale();
   ctts = adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
   decodingTimes =
       adjustTts(
           source.getSampleDurations(),
           timeScaleFactor,
           syncSamples,
           getTimes(source, syncSamples, targetTimeScale));
 }
  protected void createStts(Track track, SampleTableBox stbl) {
    TimeToSampleBox.Entry lastEntry = null;
    List<TimeToSampleBox.Entry> entries = new ArrayList<TimeToSampleBox.Entry>();

    for (long delta : track.getSampleDurations()) {
      if (lastEntry != null && lastEntry.getDelta() == delta) {
        lastEntry.setCount(lastEntry.getCount() + 1);
      } else {
        lastEntry = new TimeToSampleBox.Entry(1, delta);
        entries.add(lastEntry);
      }
    }
    TimeToSampleBox stts = new TimeToSampleBox();
    stts.setEntries(entries);
    stbl.addBox(stts);
  }
  private static long[] getTimes(Track track, long[] syncSamples, long targetTimeScale) {
    long[] syncSampleTimes = new long[syncSamples.length];

    int currentSample = 1; // first syncsample is 1
    long currentDuration = 0;
    int currentSyncSampleIndex = 0;

    while (currentSample <= syncSamples[syncSamples.length - 1]) {
      if (currentSample == syncSamples[currentSyncSampleIndex]) {
        syncSampleTimes[currentSyncSampleIndex++] =
            (currentDuration * targetTimeScale) / track.getTrackMetaData().getTimescale();
      }
      currentDuration += track.getSampleDurations()[currentSample - 1];
      currentSample++;
    }
    return syncSampleTimes;
  }
  protected void createStco(
      Track targetTrack, Movie movie, Map<Track, int[]> chunks, SampleTableBox stbl) {
    if (chunkOffsetBoxes.get(targetTrack) == null) {
      // The ChunkOffsetBox we create here is just a stub
      // since we haven't created the whole structure we can't tell where the
      // first chunk starts (mdat box). So I just let the chunk offset
      // start at zero and I will add the mdat offset later.

      long offset = 0;
      // all tracks have the same number of chunks
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine(
            "Calculating chunk offsets for track_" + targetTrack.getTrackMetaData().getTrackId());
      }

      List<Track> tracks = new ArrayList<Track>(chunks.keySet());
      Collections.sort(
          tracks,
          new Comparator<Track>() {
            public int compare(Track o1, Track o2) {
              return l2i(o1.getTrackMetaData().getTrackId() - o2.getTrackMetaData().getTrackId());
            }
          });
      Map<Track, Integer> trackToChunk = new HashMap<Track, Integer>();
      Map<Track, Integer> trackToSample = new HashMap<Track, Integer>();
      Map<Track, Double> trackToTime = new HashMap<Track, Double>();
      for (Track track : tracks) {
        trackToChunk.put(track, 0);
        trackToSample.put(track, 0);
        trackToTime.put(track, 0.0);
        chunkOffsetBoxes.put(track, new StaticChunkOffsetBox());
      }

      while (true) {
        Track nextChunksTrack = null;
        for (Track track : tracks) {
          // This always chooses the least progressed track
          if ((nextChunksTrack == null || trackToTime.get(track) < trackToTime.get(nextChunksTrack))
              &&
              // either first OR track's next chunk's starttime is smaller than nextTrack's next
              // chunks starttime
              // AND their need to be chunks left!
              (trackToChunk.get(track) < chunks.get(track).length)) {
            nextChunksTrack = track;
          }
        }
        if (nextChunksTrack == null) {
          break; // no next
        }
        // found the next one
        ChunkOffsetBox chunkOffsetBox = chunkOffsetBoxes.get(nextChunksTrack);
        chunkOffsetBox.setChunkOffsets(
            Mp4Arrays.copyOfAndAppend(chunkOffsetBox.getChunkOffsets(), offset));

        int nextChunksIndex = trackToChunk.get(nextChunksTrack);

        int numberOfSampleInNextChunk = chunks.get(nextChunksTrack)[nextChunksIndex];
        int startSample = trackToSample.get(nextChunksTrack);
        double time = trackToTime.get(nextChunksTrack);

        for (int j = startSample; j < startSample + numberOfSampleInNextChunk; j++) {
          offset += track2SampleSizes.get(nextChunksTrack)[j];
          time +=
              (double) nextChunksTrack.getSampleDurations()[j]
                  / nextChunksTrack.getTrackMetaData().getTimescale();
        }
        trackToChunk.put(nextChunksTrack, nextChunksIndex + 1);
        trackToSample.put(nextChunksTrack, startSample + numberOfSampleInNextChunk);
        trackToTime.put(nextChunksTrack, time);
      }
    }

    stbl.addBox(chunkOffsetBoxes.get(targetTrack));
  }