コード例 #1
0
  public static void main(String[] args) throws IOException {

    Movie video =
        MovieCreator.build(
            Channels.newChannel(
                Mp4WithAudioDelayExample.class.getResourceAsStream("/count-video.mp4")));
    Movie audio =
        MovieCreator.build(
            Channels.newChannel(
                Mp4WithAudioDelayExample.class.getResourceAsStream("/count-english-audio.mp4")));

    List<Track> videoTracks = video.getTracks();
    video.setTracks(new LinkedList<Track>());

    List<Track> audioTracks = audio.getTracks();

    for (Track videoTrack : videoTracks) {
      video.addTrack(new AppendTrack(videoTrack, videoTrack));
    }
    for (Track audioTrack : audioTracks) {
      audioTrack.getTrackMetaData().setStartTime(10.0);
      video.addTrack(audioTrack);
    }

    IsoFile out = new DefaultMp4Builder().build(video);
    FileOutputStream fos = new FileOutputStream(new File(String.format("output.mp4")));
    out.getBox(fos.getChannel());
    fos.close();
  }
コード例 #2
0
 public long getTimescale(Movie movie) {
   long timescale = movie.getTracks().iterator().next().getTrackMetaData().getTimescale();
   for (Track track : movie.getTracks()) {
     timescale = gcd(track.getTrackMetaData().getTimescale(), timescale);
   }
   return timescale;
 }
コード例 #3
0
 protected void createSdtp(Track track, SampleTableBox stbl) {
   if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) {
     SampleDependencyTypeBox sdtp = new SampleDependencyTypeBox();
     sdtp.setEntries(track.getSampleDependencies());
     stbl.addBox(sdtp);
   }
 }
コード例 #4
0
  public long[] getDecodingTimes() {
    long[] scaled = new long[source.getDecodingTimes().length];

    LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
    for (int i = 0; i < source.getDecodingTimes().length; i++) {
      scaled[i] = source.getDecodingTimes()[i] * timeScaleFactor;
    }
    return scaled;
  }
コード例 #5
0
    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);
      }
    }
コード例 #6
0
  protected Box createStbl(Track track, Movie movie, Map<Track, int[]> chunks) {
    SampleTableBox stbl = new SampleTableBox();

    createStsd(track, stbl);
    createStts(track, stbl);
    createCtts(track, stbl);
    createStss(track, stbl);
    createSdtp(track, stbl);
    createStsc(track, chunks, stbl);
    createStsz(track, stbl);
    createStco(track, movie, chunks, stbl);

    Map<String, List<GroupEntry>> groupEntryFamilies = new HashMap<String, List<GroupEntry>>();
    for (Map.Entry<GroupEntry, long[]> sg : track.getSampleGroups().entrySet()) {
      String type = sg.getKey().getType();
      List<GroupEntry> groupEntries = groupEntryFamilies.get(type);
      if (groupEntries == null) {
        groupEntries = new ArrayList<GroupEntry>();
        groupEntryFamilies.put(type, groupEntries);
      }
      groupEntries.add(sg.getKey());
    }
    for (Map.Entry<String, List<GroupEntry>> sg : groupEntryFamilies.entrySet()) {
      SampleGroupDescriptionBox sgdb = new SampleGroupDescriptionBox();
      String type = sg.getKey();
      sgdb.setGroupEntries(sg.getValue());
      SampleToGroupBox sbgp = new SampleToGroupBox();
      sbgp.setGroupingType(type);
      SampleToGroupBox.Entry last = null;
      for (int i = 0; i < track.getSamples().size(); i++) {
        int index = 0;
        for (int j = 0; j < sg.getValue().size(); j++) {
          GroupEntry groupEntry = sg.getValue().get(j);
          long[] sampleNums = track.getSampleGroups().get(groupEntry);
          if (Arrays.binarySearch(sampleNums, i) >= 0) {
            index = j + 1;
          }
        }
        if (last == null || last.getGroupDescriptionIndex() != index) {
          last = new SampleToGroupBox.Entry(1, index);
          sbgp.getEntries().add(last);
        } else {
          last.setSampleCount(last.getSampleCount() + 1);
        }
      }
      stbl.addBox(sgdb);
      stbl.addBox(sbgp);
    }

    if (track instanceof CencEncryptedTrack) {
      createCencBoxes((CencEncryptedTrack) track, stbl, chunks.get(track));
    }
    createSubs(track, stbl);

    return stbl;
  }
コード例 #7
0
 /**
  * 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));
 }
コード例 #8
0
 /**
  * Calculates the timestamp of all tracks' sync samples.
  *
  * @param movie
  * @param track
  * @return
  */
 public static List<long[]> getSyncSamplesTimestamps(Movie movie, Track track) {
   List<long[]> times = new LinkedList<long[]>();
   for (Track currentTrack : movie.getTracks()) {
     if (currentTrack.getHandler().equals(track.getHandler())) {
       long[] currentTrackSyncSamples = currentTrack.getSyncSamples();
       if (currentTrackSyncSamples != null && currentTrackSyncSamples.length > 0) {
         final long[] currentTrackTimes = getTimes(movie, currentTrack);
         times.add(currentTrackTimes);
       }
     }
   }
   return times;
 }
コード例 #9
0
  private static long[] getTimes(Movie m, Track track) {
    long[] syncSamples = track.getSyncSamples();
    long[] syncSampleTimes = new long[syncSamples.length];
    Queue<TimeToSampleBox.Entry> timeQueue =
        new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());

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

    long timeScale = 1;
    for (Track track1 : m.getTracks()) {
      if (track1.getHandler().equals(track.getHandler())) {
        if (track1.getTrackMetaData().getTimescale() != track.getTrackMetaData().getTimescale()) {
          timeScale = lcm(timeScale, track1.getTrackMetaData().getTimescale());
        }
      }
    }

    while (currentSample <= syncSamples[syncSamples.length - 1]) {
      if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
        syncSampleTimes[currentSyncSampleIndex++] = currentDuration * timeScale;
      }
      if (left-- == 0) {
        TimeToSampleBox.Entry entry = timeQueue.poll();
        left = entry.getCount() - 1;
        currentDelta = entry.getDelta();
      }
      currentDuration += currentDelta;
    }
    return syncSampleTimes;
  }
コード例 #10
0
 protected static long getDuration(Track track) {
   long duration = 0;
   for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
     duration += entry.getCount() * entry.getDelta();
   }
   return duration;
 }
コード例 #11
0
  protected MovieBox createMovieBox(Movie movie, Map<Track, int[]> chunks) {
    MovieBox movieBox = new MovieBox();
    MovieHeaderBox mvhd = new MovieHeaderBox();

    mvhd.setCreationTime(new Date());
    mvhd.setModificationTime(new Date());
    mvhd.setMatrix(movie.getMatrix());
    long movieTimeScale = getTimescale(movie);
    long duration = 0;

    for (Track track : movie.getTracks()) {
      long tracksDuration;

      if (track.getEdits() == null || track.getEdits().isEmpty()) {
        tracksDuration =
            (track.getDuration() * movieTimeScale / track.getTrackMetaData().getTimescale());
      } else {
        double d = 0;
        for (Edit edit : track.getEdits()) {
          d += (long) edit.getSegmentDuration();
        }
        tracksDuration = (long) (d * movieTimeScale);
      }

      if (tracksDuration > duration) {
        duration = tracksDuration;
      }
    }

    mvhd.setDuration(duration);
    mvhd.setTimescale(movieTimeScale);
    // find the next available trackId
    long nextTrackId = 0;
    for (Track track : movie.getTracks()) {
      nextTrackId =
          nextTrackId < track.getTrackMetaData().getTrackId()
              ? track.getTrackMetaData().getTrackId()
              : nextTrackId;
    }
    mvhd.setNextTrackId(++nextTrackId);

    movieBox.addBox(mvhd);
    for (Track track : movie.getTracks()) {
      movieBox.addBox(createTrackBox(track, movie, chunks));
    }
    // metadata here
    Box udta = createUdta(movie);
    if (udta != null) {
      movieBox.addBox(udta);
    }
    return movieBox;
  }
コード例 #12
0
 protected void createStss(Track track, SampleTableBox stbl) {
   long[] syncSamples = track.getSyncSamples();
   if (syncSamples != null && syncSamples.length > 0) {
     SyncSampleBox stss = new SyncSampleBox();
     stss.setSampleNumber(syncSamples);
     stbl.addBox(stss);
   }
 }
コード例 #13
0
  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;
  }
コード例 #14
0
 protected void createCtts(Track track, SampleTableBox stbl) {
   List<CompositionTimeToSample.Entry> compositionTimeToSampleEntries =
       track.getCompositionTimeEntries();
   if (compositionTimeToSampleEntries != null && !compositionTimeToSampleEntries.isEmpty()) {
     CompositionTimeToSample ctts = new CompositionTimeToSample();
     ctts.setEntries(compositionTimeToSampleEntries);
     stbl.addBox(ctts);
   }
 }
コード例 #15
0
  public static void main(String[] args) throws IOException {
    String audioEnglish =
        RemoveSomeSamplesExample.class.getProtectionDomain().getCodeSource().getLocation().getFile()
            + "/count-english-audio.mp4";
    Movie originalMovie = MovieCreator.build(new FileInputStream(audioEnglish).getChannel());

    Track audio = originalMovie.getTracks().get(0);

    Movie nuMovie = new Movie();
    nuMovie.addTrack(
        new AppendTrack(
            new CroppedTrack(audio, 0, 10),
            new CroppedTrack(audio, 100, audio.getSamples().size())));

    Container out = new DefaultMp4Builder().build(nuMovie);
    FileOutputStream fos = new FileOutputStream(new File("output.mp4"));
    out.writeContainer(fos.getChannel());
    fos.close();
  }
コード例 #16
0
 private long getTimeMappingEditTime(Track track) {
   final EditListBox editList = track.getTrackMetaData().getEditList();
   if (editList != null) {
     final List<EditListBox.Entry> entries = editList.getEntries();
     for (EditListBox.Entry entry : entries) {
       if (entry.getMediaTime() > 0) {
         return entry.getMediaTime();
       }
     }
   }
   return 0;
 }
コード例 #17
0
  private MovieBox createMovieBox(Movie movie) {
    MovieBox movieBox = new MovieBox();
    MovieHeaderBox mvhd = new MovieHeaderBox();
    mvhd.setVersion(1);
    mvhd.setCreationTime(DateHelper.convert(new Date()));
    mvhd.setModificationTime(DateHelper.convert(new Date()));

    long movieTimeScale = getTimescale(movie);
    long duration = 0;

    for (Track track : movie.getTracks()) {
      long tracksDuration =
          getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale();
      if (tracksDuration > duration) {
        duration = tracksDuration;
      }
    }

    mvhd.setDuration(duration);
    mvhd.setTimescale(movieTimeScale);
    // find the next available trackId
    long nextTrackId = 0;
    for (Track track : movie.getTracks()) {
      nextTrackId =
          nextTrackId < track.getTrackMetaData().getTrackId()
              ? track.getTrackMetaData().getTrackId()
              : nextTrackId;
    }
    mvhd.setNextTrackId(++nextTrackId);
    movieBox.addBox(mvhd);
    for (Track track : movie.getTracks()) {
      movieBox.addBox(createTrackBox(track, movie));
    }
    // metadata here
    Box udta = createUdta(movie);
    if (udta != null) {
      movieBox.addBox(udta);
    }
    return movieBox;
  }
コード例 #18
0
 /**
  * we try to get the nearest synchronized sample to our desired start time. Afterwards, we are
  * ready to crop each track of the video.
  */
 private double correctTimeToNextSyncSample(Track track, double cropVidPlace, boolean nextPlace) {
   double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
   long currentSample = 0;
   double currentTime = 0;
   for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
     TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
     for (int j = 0; j < entry.getCount(); j++) {
       if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
         // samples always start with 1 but we start with zero therefore +1
         timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] =
             currentTime;
       }
       currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
       currentSample++;
     }
   }
   double previous = 0;
   for (double timeOfSyncSample : timeOfSyncSamples) {
     if (timeOfSyncSample > cropVidPlace) {
       if (nextPlace) {
         return timeOfSyncSample;
       } else {
         return previous;
       }
     }
     previous = timeOfSyncSample;
   }
   return timeOfSyncSamples[timeOfSyncSamples.length - 1];
 }
コード例 #19
0
  /** {@inheritDoc} */
  public IsoFile build(Movie movie) {
    LOG.fine("Creating movie " + movie);
    for (Track track : movie.getTracks()) {
      // getting the samples may be a time consuming activity
      List<ByteBuffer> samples = track.getSamples();
      putSamples(track, samples);
      long[] sizes = new long[samples.size()];
      for (int i = 0; i < sizes.length; i++) {
        sizes[i] = samples.get(i).limit();
      }
      putSampleSizes(track, sizes);
    }

    IsoFile isoFile = new IsoFile();
    // ouch that is ugly but I don't know how to do it else
    List<String> minorBrands = new LinkedList<String>();
    minorBrands.add("isom");
    minorBrands.add("iso2");
    minorBrands.add("avc1");

    isoFile.addBox(new FileTypeBox("isom", 0, minorBrands));
    isoFile.addBox(createMovieBox(movie));
    InterleaveChunkMdat mdat = new InterleaveChunkMdat(movie);
    isoFile.addBox(mdat);

    /*
    dataOffset is where the first sample starts. In this special mdat the samples always start
    at offset 16 so that we can use the same offset for large boxes and small boxes
     */
    long dataOffset = mdat.getDataOffset();
    for (StaticChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
      long[] offsets = chunkOffsetBox.getChunkOffsets();
      for (int i = 0; i < offsets.length; i++) {
        offsets[i] += dataOffset;
      }
    }

    return isoFile;
  }
コード例 #20
0
  protected Box createEdts(Track track, Movie movie) {
    if (track.getEdits() != null && track.getEdits().size() > 0) {
      EditListBox elst = new EditListBox();
      elst.setVersion(0); // quicktime won't play file when version = 1
      List<EditListBox.Entry> entries = new ArrayList<EditListBox.Entry>();

      for (Edit edit : track.getEdits()) {
        entries.add(
            new EditListBox.Entry(
                elst,
                Math.round(edit.getSegmentDuration() * movie.getTimescale()),
                edit.getMediaTime() * track.getTrackMetaData().getTimescale() / edit.getTimeScale(),
                edit.getMediaRate()));
      }

      elst.setEntries(entries);
      EditBox edts = new EditBox();
      edts.addBox(elst);
      return edts;
    } else {
      return null;
    }
  }
コード例 #21
0
  /** Creates a 'sidx' box */
  protected Box createSidx(
      Track track,
      long earliestPresentationTime,
      long firstOffset,
      int referencedSize,
      long subSegmentDuration,
      byte sap,
      int sapDelta) {
    SegmentIndexBox sidx = new SegmentIndexBox();

    sidx.setEarliestPresentationTime(earliestPresentationTime);
    sidx.setFirstOffset(firstOffset);
    sidx.setReferenceId(track.getTrackMetaData().getTrackId());
    sidx.setTimeScale(track.getTrackMetaData().getTimescale());
    sidx.setFlags(0);
    sidx.setReserved(0);
    SegmentIndexBox.Entry sidxentry =
        createSidxEntry(referencedSize, subSegmentDuration, sap, sapDelta);

    ArrayList<SegmentIndexBox.Entry> sidxEntries = new ArrayList<SegmentIndexBox.Entry>();
    sidxEntries.add(sidxentry);
    sidx.setEntries(sidxEntries);
    return sidx;
  }
コード例 #22
0
  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);
  }
コード例 #23
0
  /**
   * Gets the chunk sizes for the given track.
   *
   * @param track the track we are talking about
   * @return the size of each chunk in number of samples
   */
  int[] getChunkSizes(Track track) {

    long[] referenceChunkStarts = fragmenter.sampleNumbers(track);
    int[] chunkSizes = new int[referenceChunkStarts.length];

    for (int i = 0; i < referenceChunkStarts.length; i++) {
      long start = referenceChunkStarts[i] - 1;
      long end;
      if (referenceChunkStarts.length == i + 1) {
        end = track.getSamples().size();
      } else {
        end = referenceChunkStarts[i + 1] - 1;
      }

      chunkSizes[i] = l2i(end - start);
      // The Stretch makes sure that there are as much audio and video chunks!
    }
    assert DefaultMp4Builder.this.track2Sample.get(track).size() == sum(chunkSizes)
        : "The number of samples and the sum of all chunk lengths must be equal";
    return chunkSizes;
  }
コード例 #24
0
 public List<Sample> getSamples() {
   return source.getSamples();
 }
コード例 #25
0
 public boolean isInPoster() {
   return source.isInPoster();
 }
コード例 #26
0
 public boolean isInPreview() {
   return source.isInPreview();
 }
コード例 #27
0
 public boolean isInMovie() {
   return source.isInMovie();
 }
コード例 #28
0
 public boolean isEnabled() {
   return source.isEnabled();
 }
コード例 #29
0
 public String getHandler() {
   return source.getHandler();
 }
コード例 #30
0
 public TrackMetaData getTrackMetaData() {
   TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
   trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() * this.timeScaleFactor);
   return trackMetaData;
 }