All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jcodec.movtool.Paste Maven / Gradle / Ivy

There is a newer version: 0.2.5
Show newest version
package org.jcodec.movtool;

import static java.util.Arrays.fill;
import static org.jcodec.containers.mp4.MP4Util.createRefMovie;
import static org.jcodec.movtool.Util.forceEditList;
import static org.jcodec.movtool.Util.insertTo;
import static org.jcodec.movtool.Util.shift;
import static org.jcodec.movtool.Util.spread;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.Arrays;

import org.jcodec.containers.mp4.boxes.ClipRegionBox;
import org.jcodec.containers.mp4.boxes.LoadSettingsBox;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleSizesBox;
import org.jcodec.containers.mp4.boxes.SoundMediaHeaderBox;
import org.jcodec.containers.mp4.boxes.TrackHeaderBox;
import org.jcodec.containers.mp4.boxes.TrakBox;
import org.jcodec.containers.mp4.boxes.VideoMediaHeaderBox;
import org.jcodec.containers.mp4.io.FileInput;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * Paste on ref movies
 * 
 * @author Stanislav Vitvitskiy
 * 
 */
public class Paste {

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Syntax: paste   [frame number]");
            System.exit(-1);
        }
        File toFile = new File(args[0]);
        FileInput to = null;
        FileInput from = null;
        RandomAccessFile out = null;
        try {
            File outFile = new File(toFile.getParentFile(), toFile.getName().replaceAll("\\.mov$", "") + ".paste.mov");
            outFile.delete();
            out = new RandomAccessFile(outFile, "rw");
            to = new FileInput(toFile);
            File fromFile = new File(args[1]);
            from = new FileInput(fromFile);
            MovieBox toMov = createRefMovie(to, "file://" + toFile.getCanonicalPath());
            MovieBox fromMov = createRefMovie(from, "file://" + fromFile.getCanonicalPath());
            new Strip().strip(fromMov);
            if (args.length > 2) {
                new Paste().paste(toMov, fromMov, Integer.parseInt(args[2]));
            } else {
                new Paste().addToMovie(toMov, fromMov);
            }
            toMov.write(out);
        } finally {
            if (to != null)
                to.close();
            if (from != null)
                from.close();
            if (out != null)
                out.close();
        }
    }

    public void paste(MovieBox to, MovieBox from, int frameNo) {
        TrakBox videoTrack = to.getVideoTrack();
        if (videoTrack != null && videoTrack.getTimescale() != to.getTimescale())
            to.fixTimescale(videoTrack.getTimescale());

        long frameTv = to.rescale(getFrameTv(videoTrack, frameNo), videoTrack.getTimescale());

        forceEditList(to);
        forceEditList(from);
        TrakBox[] fromTracks = from.getTracks();
        TrakBox[] toTracks = to.getTracks();
        int[][] matches = findMatches(fromTracks, toTracks);

        for (int i = 0; i < matches[0].length; i++) {
            TrakBox localTrack = to.importTrack(from, fromTracks[i]);
            if (matches[0][i] != -1) {
                insertTo(to, toTracks[matches[0][i]], localTrack, frameTv);
            } else {
                to.appendTrack(localTrack);
                shift(to, localTrack, frameTv);
            }
        }

        for (int i = 0; i < matches[1].length; i++) {
            if (matches[1][i] == -1) {
                spread(to, toTracks[i], frameTv, to.rescale(from.getDuration(), from.getTimescale()));
            }
        }

        to.updateDuration();
    }

    public void addToMovie(MovieBox to, MovieBox from) {
        for (TrakBox track : from.getTracks()) {
            to.appendTrack(to.importTrack(from, track));
        }
    }

    long[] tv;

    private long getFrameTv(TrakBox videoTrack, int frame) {
        if (tv == null) {
            tv = Util.getTimevalues(videoTrack);
        }
        return tv[frame];
    }

    private int[][] findMatches(TrakBox[] fromTracks, TrakBox[] toTracks) {
        int[] f2t = new int[fromTracks.length];
        int[] t2f = new int[toTracks.length];

        fill(f2t, -1);
        fill(t2f, -1);

        for (int i = 0; i < fromTracks.length; i++) {
            if (f2t[i] != -1)
                continue;
            for (int j = 0; j < toTracks.length; j++) {
                if (t2f[j] != -1)
                    continue;
                if (matches(fromTracks[i], toTracks[j])) {
                    f2t[i] = j;
                    t2f[j] = i;
                    break;
                }
            }
        }

        return new int[][] { f2t, t2f };
    }

    private boolean matches(TrakBox trakBox1, TrakBox trakBox2) {
        return trakBox1.getHandlerType().equals(trakBox2.getHandlerType()) && matchHeaders(trakBox1, trakBox2)
                && matchSampleSizes(trakBox1, trakBox2) && matchMediaHeader(trakBox1, trakBox2)
                && matchClip(trakBox1, trakBox2) && matchLoad(trakBox1, trakBox2);
    }

    private boolean matchSampleSizes(TrakBox trakBox1, TrakBox trakBox2) {
        SampleSizesBox stsz1 = NodeBox.findFirst(trakBox1, SampleSizesBox.class, "mdia", "minf", "stbl", "stsz");
        SampleSizesBox stsz2 = NodeBox.findFirst(trakBox1, SampleSizesBox.class, "mdia", "minf", "stbl", "stsz");
        return stsz1.getDefaultSize() == stsz2.getDefaultSize();
    }

    private boolean matchMediaHeader(TrakBox trakBox1, TrakBox trakBox2) {
        VideoMediaHeaderBox vmhd1 = NodeBox.findFirst(trakBox1, VideoMediaHeaderBox.class, "mdia", "minf", "vmhd");
        VideoMediaHeaderBox vmhd2 = NodeBox.findFirst(trakBox2, VideoMediaHeaderBox.class, "mdia", "minf", "vmhd");
        if ((vmhd1 != null && vmhd2 == null) || (vmhd1 == null && vmhd2 != null))
            return false;
        else if (vmhd1 != null && vmhd2 != null) {
            return vmhd1.getGraphicsMode() == vmhd2.getGraphicsMode() && vmhd1.getbOpColor() == vmhd2.getbOpColor()
                    && vmhd1.getgOpColor() == vmhd2.getgOpColor() && vmhd1.getrOpColor() == vmhd2.getrOpColor();
        } else {
            SoundMediaHeaderBox smhd1 = NodeBox.findFirst(trakBox1, SoundMediaHeaderBox.class, "mdia", "minf", "smhd");
            SoundMediaHeaderBox smhd2 = NodeBox.findFirst(trakBox2, SoundMediaHeaderBox.class, "mdia", "minf", "smhd");
            if ((smhd1 == null && smhd2 != null) || (smhd1 != null && smhd2 == null))
                return false;
            else if (smhd1 != null && smhd2 != null)
                return smhd1.getBalance() == smhd1.getBalance();
        }

        return true;
    }

    private boolean matchHeaders(TrakBox trakBox1, TrakBox trakBox2) {
        TrackHeaderBox th1 = trakBox1.getTrackHeader();
        TrackHeaderBox th2 = trakBox2.getTrackHeader();

        return ("vide".equals(trakBox1.getHandlerType()) && Arrays.equals(th1.getMatrix(), th2.getMatrix())
                && th1.getLayer() == th2.getLayer() && th1.getWidth() == th2.getWidth() && th1.getHeight() == th2
                .getHeight())
                || ("soun".equals(trakBox1.getHandlerType()) && th1.getVolume() == th2.getVolume())
                || "tmcd".equals(trakBox1.getHandlerType());
    }

    private boolean matchLoad(TrakBox trakBox1, TrakBox trakBox2) {
        LoadSettingsBox load1 = NodeBox.findFirst(trakBox1, LoadSettingsBox.class, "load");
        LoadSettingsBox load2 = NodeBox.findFirst(trakBox2, LoadSettingsBox.class, "load");
        if (load1 == null && load2 == null)
            return true;
        if ((load1 == null && load2 != null) || (load1 != null && load2 == null))
            return false;

        return load1.getPreloadStartTime() == load2.getPreloadStartTime()
                && load1.getPreloadDuration() == load2.getPreloadDuration()
                && load1.getPreloadFlags() == load2.getPreloadFlags()
                && load1.getDefaultHints() == load2.getDefaultHints();
    }

    private boolean matchClip(TrakBox trakBox1, TrakBox trakBox2) {
        ClipRegionBox crgn1 = NodeBox.findFirst(trakBox1, ClipRegionBox.class, "clip", "crgn");
        ClipRegionBox crgn2 = NodeBox.findFirst(trakBox2, ClipRegionBox.class, "clip", "crgn");
        if ((crgn1 == null && crgn2 != null) || (crgn1 != null && crgn2 == null))
            return false;
        if (crgn1 == null && crgn2 == null)
            return true;

        return crgn1.getRgnSize() == crgn2.getRgnSize() && crgn1.getX() == crgn2.getX() && crgn1.getY() == crgn2.getY()
                && crgn1.getWidth() == crgn2.getWidth() && crgn1.getHeight() == crgn2.getHeight();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy