package org.jcodec.containers.mp4.boxes; import org.jcodec.common.model.Rational; import org.jcodec.common.model.Size; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Creates MP4 file out of a set of samples * * @author The JCodec project * */ public class MovieBox extends NodeBox { public MovieBox(Header atom) { super(atom); } public static String fourcc() { return "moov"; } public static MovieBox createMovieBox() { return new MovieBox(new Header(fourcc())); } public TrakBox[] getTracks() { return NodeBox.findAll(this, TrakBox.class, "trak"); } public TrakBox getVideoTrack() { TrakBox[] tracks = getTracks(); for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; if (trakBox.isVideo()) return trakBox; } return null; } public TrakBox getTimecodeTrack() { TrakBox[] tracks = getTracks(); for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; if (trakBox.isTimecode()) return trakBox; } return null; } public int getTimescale() { return getMovieHeader().getTimescale(); } public long rescale(long tv, long ts) { return (tv * getTimescale()) / ts; } public void fixTimescale(int newTs) { int oldTs = getTimescale(); setTimescale(newTs); TrakBox[] tracks = getTracks(); for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; trakBox.setDuration(rescale(trakBox.getDuration(), oldTs)); List<Edit> edits = trakBox.getEdits(); if (edits == null) continue; ListIterator<Edit> lit = edits.listIterator(); while (lit.hasNext()) { Edit edit = lit.next(); lit.set(new Edit(rescale(edit.getDuration(), oldTs), edit.getMediaTime(), edit.getRate())); } } setDuration(rescale(getDuration(), oldTs)); } private void setTimescale(int newTs) { NodeBox.findFirst(this, MovieHeaderBox.class, "mvhd").setTimescale(newTs); } public void setDuration(long movDuration) { getMovieHeader().setDuration(movDuration); } private MovieHeaderBox getMovieHeader() { return NodeBox.findFirst(this, MovieHeaderBox.class, "mvhd"); } public List<TrakBox> getAudioTracks() { ArrayList<TrakBox> result = new ArrayList<TrakBox>(); TrakBox[] tracks = getTracks(); for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; if (trakBox.isAudio()) result.add(trakBox); } return result; } public long getDuration() { return getMovieHeader().getDuration(); } public TrakBox importTrack(MovieBox movie, TrakBox track) { TrakBox newTrack = (TrakBox) NodeBox.cloneBox(track, 1024 * 1024, factory); List<Edit> edits = newTrack.getEdits(); ArrayList<Edit> result = new ArrayList<Edit>(); if (edits != null) { for (Edit edit : edits) { result.add(new Edit(rescale(edit.getDuration(), movie.getTimescale()), edit.getMediaTime(), edit .getRate())); } } newTrack.setEdits(result); return newTrack; } public void appendTrack(TrakBox newTrack) { newTrack.getTrackHeader().setNo(getMovieHeader().getNextTrackId()); getMovieHeader().setNextTrackId(getMovieHeader().getNextTrackId() + 1); boxes.add(newTrack); } public boolean isPureRefMovie(MovieBox movie) { boolean pureRef = true; TrakBox[] tracks = movie.getTracks(); for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; pureRef &= trakBox.isPureRef(); } return pureRef; } public void updateDuration() { TrakBox[] tracks = getTracks(); long min = Integer.MAX_VALUE; for (int i = 0; i < tracks.length; i++) { TrakBox trakBox = tracks[i]; if (trakBox.getDuration() < min) min = trakBox.getDuration(); } getMovieHeader().setDuration(min); } public Size getDisplaySize() { TrakBox videoTrack = getVideoTrack(); if (videoTrack == null) return null; ClearApertureBox clef = NodeBox.findFirstPath(videoTrack, ClearApertureBox.class, Box.path("tapt.clef")); if (clef != null) { return applyMatrix(videoTrack, new Size((int) clef.getWidth(), (int) clef.getHeight())); } Box box = NodeBox.findFirstPath(videoTrack, SampleDescriptionBox.class, Box.path("mdia.minf.stbl.stsd")).getBoxes() .get(0); if (box == null || !(box instanceof VideoSampleEntry)) return null; VideoSampleEntry vs = (VideoSampleEntry) box; Rational par = videoTrack.getPAR(); return applyMatrix(videoTrack, new Size((int) ((vs.getWidth() * par.getNum()) / par.getDen()), (int) vs.getHeight())); } private Size applyMatrix(TrakBox videoTrack, Size size) { int[] matrix = videoTrack.getTrackHeader().getMatrix(); return new Size((int) ((double) size.getWidth() * matrix[0] / 65536), (int) ((double) size.getHeight() * matrix[4] / 65536)); } public Size getStoredSize() { TrakBox videoTrack = getVideoTrack(); if (videoTrack == null) return null; EncodedPixelBox enof = NodeBox.findFirstPath(videoTrack, EncodedPixelBox.class, Box.path("tapt.enof")); if (enof != null) { return new Size((int) enof.getWidth(), (int) enof.getHeight()); } Box box = NodeBox.findFirstPath(videoTrack, SampleDescriptionBox.class, Box.path("mdia.minf.stbl.stsd")).getBoxes() .get(0); if (box == null || !(box instanceof VideoSampleEntry)) return null; VideoSampleEntry vs = (VideoSampleEntry) box; return new Size((int) vs.getWidth(), (int) vs.getHeight()); } protected void getModelFields(List<String> model) { } }