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

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

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

import static java.lang.System.arraycopy;
import static org.jcodec.containers.mp4.boxes.Box.findFirst;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jcodec.containers.mp4.Chunk;
import org.jcodec.containers.mp4.ChunkReader;
import org.jcodec.containers.mp4.MP4Util;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.ChunkOffsets64Box;
import org.jcodec.containers.mp4.boxes.ChunkOffsetsBox;
import org.jcodec.containers.mp4.boxes.Edit;
import org.jcodec.containers.mp4.boxes.MediaHeaderBox;
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.SampleToChunkBox;
import org.jcodec.containers.mp4.boxes.SampleToChunkBox.SampleToChunkEntry;
import org.jcodec.containers.mp4.boxes.TimeToSampleBox;
import org.jcodec.containers.mp4.boxes.TimeToSampleBox.TimeToSampleEntry;
import org.jcodec.containers.mp4.boxes.TrakBox;
import org.jcodec.containers.mp4.io.FileInput;

/**
 * This class is part of JCodec ( www.jcodec.org )
 * This software is distributed under FreeBSD License
 * 
 * Strips movie to editlist
 * 
 * @author Stanislav Vitvitskiy
 * 
 */
public class Strip {
    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Syntax: strip  ");
            System.exit(-1);
        }
        FileInput input = null;
        RandomAccessFile out = null;
        try {
            input = new FileInput(new File(args[0]));
            File file = new File(args[1]);
            file.delete();
            out = new RandomAccessFile(file, "rw");
            MovieBox movie = MP4Util.createRefMovie(input, "file://" + new File(args[0]).getAbsolutePath());
            new Strip().strip(movie);
            movie.write(out);
        } finally {
            if (input != null)
                input.close();
            if (out != null)
                out.close();
        }
    }

    public void strip(MovieBox movie) throws IOException {
        for (TrakBox track : movie.getTracks()) {
            stripTrack(movie, track);
        }
    }

    public void stripTrack(MovieBox movie, TrakBox track) {
        ChunkReader chunks = new ChunkReader(track);
        List edits = track.getEdits();
        List oldEdits = deepCopy(edits);
        List result = new ArrayList();

        Chunk chunk;
        while ((chunk = chunks.next()) != null) {
            boolean intersects = false;
            for (Edit edit : oldEdits) {
                if (edit.getMediaTime() == -1)
                    continue; // track offset, not real edit
                long editS = edit.getMediaTime();
                long editE = edit.getMediaTime() + track.rescale(edit.getDuration(), movie.getTimescale());
                long chunkS = chunk.getStartTv();
                long chunkE = chunk.getStartTv() + chunk.getDuration();

                intersects = intersects(editS, editE, chunkS, chunkE);
                if (intersects)
                    break;
            }
            if (!intersects) {
                for (int i = 0; i < oldEdits.size(); i++) {
                    if (oldEdits.get(i).getMediaTime() >= chunk.getStartTv() + chunk.getDuration())
                        edits.get(i).shift(-chunk.getDuration());
                }
            } else
                result.add(chunk);
        }

        NodeBox stbl = findFirst(track, NodeBox.class, "mdia", "minf", "stbl");
        stbl.replace("stts", getTimeToSamples(result));
        stbl.replace("stsz", getSampleSizes(result));
        stbl.replace("stsc", getSamplesToChunk(result));
        stbl.filter(Box.not("stco"));
        stbl.filter(Box.not("co64"));
        stbl.add(getChunkOffsets(result));
        findFirst(track, MediaHeaderBox.class, "mdia", "mdhd").setDuration(totalDuration(result));
    }

    private long totalDuration(List result) {
        long duration = 0;
        for (Chunk chunk : result) {
            duration += chunk.getDuration();
        }
        return duration;
    }

    private List deepCopy(List edits) {
        ArrayList newList = new ArrayList();
        for (Edit edit : edits) {
            newList.add(new Edit(edit));
        }
        return newList;
    }

    public Box getChunkOffsets(List chunks) {
        long[] result = new long[chunks.size()];
        boolean longBox = false;
        int i = 0;
        for (Chunk chunk : chunks) {
            if (chunk.getOffset() >= 0x100000000L)
                longBox = true;
            result[i++] = chunk.getOffset();
        }
        return longBox ? new ChunkOffsets64Box(result) : new ChunkOffsetsBox(result);
    }

    public TimeToSampleBox getTimeToSamples(List chunks) {
        ArrayList tts = new ArrayList();
        int curTts = -1, cnt = 0;
        for (Chunk chunk : chunks) {
            if (chunk.getSampleDur() > 0) {
                if (curTts == -1 || curTts != chunk.getSampleDur()) {
                    if (curTts != -1)
                        tts.add(new TimeToSampleEntry(cnt, curTts));
                    cnt = 0;
                    curTts = chunk.getSampleDur();
                }
                cnt += chunk.getSampleCount();
            } else {
                for (int dur : chunk.getSampleDurs()) {
                    if (curTts == -1 || curTts != dur) {
                        if (curTts != -1)
                            tts.add(new TimeToSampleEntry(cnt, curTts));
                        cnt = 0;
                        curTts = dur;
                    }
                    ++cnt;
                }
            }
        }
        if (cnt > 0)
            tts.add(new TimeToSampleEntry(cnt, curTts));
        return new TimeToSampleBox(tts.toArray(new TimeToSampleEntry[0]));
    }

    public SampleSizesBox getSampleSizes(List chunks) {
        int nSamples = 0, prevSize = chunks.get(0).getSampleSize();
        for (Chunk chunk : chunks) {
            nSamples += chunk.getSampleCount();
            if (prevSize == 0 && chunk.getSampleSize() != 0)
                throw new RuntimeException("Mixed sample sizes not supported");
        }

        if (prevSize > 0)
            return new SampleSizesBox(prevSize, nSamples);

        int[] sizes = new int[nSamples];
        int startSample = 0;
        for (Chunk chunk : chunks) {
            arraycopy(chunk.getSampleSizes(), 0, sizes, startSample, chunk.getSampleCount());
            startSample += chunk.getSampleCount();
        }
        return new SampleSizesBox(sizes);
    }

    public SampleToChunkBox getSamplesToChunk(List chunks) {
        ArrayList result = new ArrayList();
        Iterator it = chunks.iterator();
        Chunk chunk = it.next();
        int curSz = chunk.getSampleCount();
        int curEntry = chunk.getEntry();
        int first = 1, cnt = 1;
        while (it.hasNext()) {
            chunk = it.next();
            int newSz = chunk.getSampleCount();
            int newEntry = chunk.getEntry();
            if (curSz != newSz || curEntry != newEntry) {
                result.add(new SampleToChunkEntry(first, curSz, curEntry));
                curSz = newSz;
                curEntry = newEntry;
                first += cnt;
                cnt = 0;
            }
            ++cnt;
        }
        if (cnt > 0)
            result.add(new SampleToChunkEntry(first, curSz, curEntry));
        return new SampleToChunkBox(result.toArray(new SampleToChunkEntry[0]));
    }

    private boolean intersects(long a, long b, long c, long d) {
        return (a >= c && a < d) || (b >= c && b < d) || (c >= a && c < b) || (d >= a && d < b);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy