org.jcodec.movtool.Cut Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcodec Show documentation
Show all versions of jcodec Show documentation
Pure Java implementation of video/audio codecs and formats
package org.jcodec.movtool;
import static java.lang.Integer.parseInt;
import static java.lang.Math.max;
import static org.apache.commons.io.FilenameUtils.removeExtension;
import static org.jcodec.common.NIOUtils.readableFileChannel;
import static org.jcodec.common.NIOUtils.writableFileChannel;
import static org.jcodec.containers.mp4.MP4Util.createRefMovie;
import static org.jcodec.movtool.Util.forceEditList;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.lang.StringUtils;
import org.jcodec.common.NIOUtils;
import org.jcodec.common.SeekableByteChannel;
import org.jcodec.containers.mp4.MP4Util;
import org.jcodec.containers.mp4.boxes.Edit;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.TrakBox;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Cut on ref movies
*
* @author The JCodec project
*
*/
public class Cut {
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.out
.println("Syntax: cut [-command arg]...[-command arg] [-self] \n"
+ "\tCreates a reference movie out of the file and applies a set of changes specified by the commands to it.");
System.exit(-1);
}
List slices = new ArrayList();
List sliceNames = new ArrayList();
boolean selfContained = false;
int shift = 0;
while (true) {
if ("-cut".equals(args[shift])) {
String[] pt = StringUtils.split(args[shift + 1], ":");
slices.add(new Slice(parseInt(pt[0]), parseInt(pt[1])));
if (pt.length > 2)
sliceNames.add(pt[2]);
else
sliceNames.add(null);
shift += 2;
} else if ("-self".equals(args[shift])) {
++shift;
selfContained = true;
} else
break;
}
File source = new File(args[shift]);
SeekableByteChannel input = null;
SeekableByteChannel out = null;
List outs = new ArrayList();
try {
input = readableFileChannel(source);
MovieBox movie = createRefMovie(input, "file://" + source.getCanonicalPath());
List slicesMovs;
if (!selfContained) {
out = writableFileChannel(new File(source.getParentFile(), removeExtension(source.getName())
+ ".ref.mov"));
slicesMovs = new Cut().cut(movie, slices);
MP4Util.writeMovie(out, movie);
} else {
out = writableFileChannel(new File(source.getParentFile(), removeExtension(source.getName())
+ ".self.mov"));
slicesMovs = new Cut().cut(movie, slices);
new Strip().strip(movie);
new Flattern().flattern(movie, out);
}
saveSlices(slicesMovs, sliceNames, source.getParentFile());
} finally {
if (input != null)
input.close();
if (out != null)
out.close();
for (SeekableByteChannel o : outs) {
o.close();
}
}
}
private static void saveSlices(List slices, List names, File parentFile) throws IOException {
for (int i = 0; i < slices.size(); i++) {
if (names.get(i) == null)
continue;
SeekableByteChannel out = null;
try {
out = writableFileChannel(new File(parentFile, names.get(i)));
MP4Util.writeMovie(out, slices.get(i));
} finally {
NIOUtils.closeQuietly(out);
}
}
}
public static class Slice {
private double inSec;
private double outSec;
public Slice(double in, double out) {
super();
this.inSec = in;
this.outSec = out;
}
}
public List cut(MovieBox movie, List commands) {
TrakBox videoTrack = movie.getVideoTrack();
if (videoTrack != null && videoTrack.getTimescale() != movie.getTimescale())
movie.fixTimescale(videoTrack.getTimescale());
TrakBox[] tracks = movie.getTracks();
for (TrakBox trakBox : tracks) {
forceEditList(movie, trakBox);
List edits = trakBox.getEdits();
for (Slice cut : commands) {
split(edits, cut.inSec, movie, trakBox);
split(edits, cut.outSec, movie, trakBox);
}
}
ArrayList result = new ArrayList();
for (Slice cut : commands) {
MovieBox clone = (MovieBox) MP4Util.cloneBox(movie, 16 * 1024 * 1024);
for (TrakBox trakBox : clone.getTracks()) {
selectInner(trakBox.getEdits(), cut, movie, trakBox);
}
result.add(clone);
}
long movDuration = 0;
for (TrakBox trakBox : movie.getTracks()) {
selectOuter(trakBox.getEdits(), commands, movie, trakBox);
trakBox.setEdits(trakBox.getEdits());
movDuration = max(movDuration, trakBox.getDuration());
}
movie.setDuration(movDuration);
return result;
}
private void selectOuter(List edits, List commands, MovieBox movie, TrakBox trakBox) {
long[] inMv = new long[commands.size()];
long[] outMv = new long[commands.size()];
for (int i = 0; i < commands.size(); i++) {
inMv[i] = (long) (commands.get(i).inSec * movie.getTimescale());
outMv[i] = (long) (commands.get(i).outSec * movie.getTimescale());
}
long editStartMv = 0;
ListIterator lit = edits.listIterator();
while (lit.hasNext()) {
Edit edit = lit.next();
for (int i = 0; i < inMv.length; i++) {
if (editStartMv + edit.getDuration() > inMv[i] && editStartMv < outMv[i])
lit.remove();
}
editStartMv += edit.getDuration();
}
}
private void selectInner(List edits, Slice cut, MovieBox movie, TrakBox trakBox) {
long inMv = (long) (movie.getTimescale() * cut.inSec);
long outMv = (long) (movie.getTimescale() * cut.outSec);
long editStart = 0;
ListIterator lit = edits.listIterator();
while (lit.hasNext()) {
Edit edit = lit.next();
if (editStart + edit.getDuration() <= inMv || editStart >= outMv)
lit.remove();
editStart += edit.getDuration();
}
}
private void split(List edits, double sec, MovieBox movie, TrakBox trakBox) {
Util.split(movie, trakBox, (long) (sec * movie.getTimescale()));
}
}