
org.jcodec.containers.mp4.MP4Util Maven / Gradle / Ivy
package org.jcodec.containers.mp4;
import static org.jcodec.common.io.IOUtils.closeQuietly;
import static org.jcodec.common.io.NIOUtils.readableChannel;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jcodec.common.AutoFileChannelWrapper;
import org.jcodec.common.Codec;
import org.jcodec.common.io.IOUtils;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.logging.Logger;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.FileTypeBox;
import org.jcodec.containers.mp4.boxes.Header;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.MovieFragmentBox;
import org.jcodec.containers.mp4.boxes.TrakBox;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* @author The JCodec project
*
*/
public class MP4Util {
private static Map codecMapping = new HashMap();
static {
codecMapping.put(Codec.MPEG2, "m2v1");
codecMapping.put(Codec.H264, "avc1");
codecMapping.put(Codec.J2K, "mjp2");
}
public static class Movie {
private FileTypeBox ftyp;
private MovieBox moov;
public Movie(FileTypeBox ftyp, MovieBox moov) {
this.ftyp = ftyp;
this.moov = moov;
}
public FileTypeBox getFtyp() {
return ftyp;
}
public MovieBox getMoov() {
return moov;
}
}
public static MovieBox createRefMovie(SeekableByteChannel input, String url) throws IOException {
MovieBox movie = parseMovieChannel(input);
TrakBox[] tracks = movie.getTracks();
for (int i = 0; i < tracks.length; i++) {
TrakBox trakBox = tracks[i];
trakBox.setDataRef(url);
}
return movie;
}
public static MovieBox parseMovieChannel(SeekableByteChannel input) throws IOException {
for (Atom atom : getRootAtoms(input)) {
if ("moov".equals(atom.getHeader().getFourcc())) {
return (MovieBox) atom.parseBox(input);
}
}
return null;
}
public static Movie createRefFullMovie(SeekableByteChannel input, String url) throws IOException {
Movie movie = parseFullMovieChannel(input);
TrakBox[] tracks = movie.moov.getTracks();
for (int i = 0; i < tracks.length; i++) {
TrakBox trakBox = tracks[i];
trakBox.setDataRef(url);
}
return movie;
}
public static Movie parseFullMovieChannel(SeekableByteChannel input) throws IOException {
FileTypeBox ftyp = null;
for (Atom atom : getRootAtoms(input)) {
if ("ftyp".equals(atom.getHeader().getFourcc())) {
ftyp = (FileTypeBox) atom.parseBox(input);
} else if ("moov".equals(atom.getHeader().getFourcc())) {
return new Movie(ftyp, (MovieBox) atom.parseBox(input));
}
}
return null;
}
public static List parseMovieFragments(SeekableByteChannel input) throws IOException {
MovieBox moov = null;
LinkedList fragments = new LinkedList();
for (Atom atom : getRootAtoms(input)) {
if ("moov".equals(atom.getHeader().getFourcc())) {
moov = (MovieBox) atom.parseBox(input);
} else if ("moof".equalsIgnoreCase(atom.getHeader().getFourcc())) {
fragments.add((MovieFragmentBox) atom.parseBox(input));
}
}
for (MovieFragmentBox fragment : fragments) {
fragment.setMovie(moov);
}
return fragments;
}
public static List getRootAtoms(SeekableByteChannel input) throws IOException {
input.setPosition(0);
List result = new ArrayList();
long off = 0;
Header atom;
while (off < input.size()) {
input.setPosition(off);
atom = Header.read(NIOUtils.fetchFromChannel(input, 16));
if (atom == null)
break;
result.add(new Atom(atom, off));
off += atom.getSize();
}
return result;
}
public static Atom findFirstAtomInFile(String fourcc, File input) throws IOException {
SeekableByteChannel c = new AutoFileChannelWrapper(input);
try {
return findFirstAtom(fourcc, c);
} finally {
IOUtils.closeQuietly(c);
}
}
public static Atom findFirstAtom(String fourcc, SeekableByteChannel input) throws IOException {
List rootAtoms = getRootAtoms(input);
for (Atom atom : rootAtoms) {
if (fourcc.equals(atom.getHeader().getFourcc()))
return atom;
}
return null;
}
public static Atom atom(SeekableByteChannel input) throws IOException {
long off = input.position();
Header atom = Header.read(NIOUtils.fetchFromChannel(input, 16));
return atom == null ? null : new Atom(atom, off);
}
public static class Atom {
private long offset;
private Header header;
public Atom(Header header, long offset) {
this.header = header;
this.offset = offset;
}
public long getOffset() {
return offset;
}
public Header getHeader() {
return header;
}
public Box parseBox(SeekableByteChannel input) throws IOException {
input.setPosition(offset + header.headerSize());
return BoxUtil.parseBox(NIOUtils.fetchFromChannel(input, (int) header.getBodySize()), header, BoxFactory.getDefault());
}
public void copy(SeekableByteChannel input, WritableByteChannel out) throws IOException {
input.setPosition(offset);
NIOUtils.copy(input, out, header.getSize());
}
}
public static MovieBox parseMovie(File source) throws IOException {
SeekableByteChannel input = null;
try {
input = readableChannel(source);
return parseMovieChannel(input);
} finally {
if (input != null)
input.close();
}
}
public static MovieBox createRefMovieFromFile(File source) throws IOException {
SeekableByteChannel input = null;
try {
input = readableChannel(source);
return createRefMovie(input, "file://" + source.getCanonicalPath());
} finally {
if (input != null)
input.close();
}
}
public static void writeMovieToFile(File f, MovieBox movie) throws IOException {
SeekableByteChannel out = null;
try {
out = NIOUtils.writableChannel(f);
writeMovie(out, movie);
} finally {
closeQuietly(out);
}
}
public static void writeMovie(SeekableByteChannel out, MovieBox movie) throws IOException {
doWriteMovieToChannel(out, movie, 0);
}
public static void doWriteMovieToChannel(SeekableByteChannel out, MovieBox movie, int additionalSize) throws IOException {
int sizeHint = estimateMoovBoxSize(movie) + additionalSize;
Logger.debug("Using " + sizeHint + " bytes for MOOV box");
ByteBuffer buf = ByteBuffer.allocate(sizeHint * 4);
movie.write(buf);
buf.flip();
out.write(buf);
}
public static Movie parseFullMovie(File source) throws IOException {
SeekableByteChannel input = null;
try {
input = readableChannel(source);
return parseFullMovieChannel(input);
} finally {
if (input != null)
input.close();
}
}
public static Movie createRefFullMovieFromFile(File source) throws IOException {
SeekableByteChannel input = null;
try {
input = readableChannel(source);
return createRefFullMovie(input, "file://" + source.getCanonicalPath());
} finally {
if (input != null)
input.close();
}
}
public static void writeFullMovieToFile(File f, Movie movie) throws IOException {
SeekableByteChannel out = null;
try {
out = NIOUtils.writableChannel(f);
writeFullMovie(out, movie);
} finally {
closeQuietly(out);
}
}
public static void writeFullMovie(SeekableByteChannel out, Movie movie) throws IOException {
doWriteFullMovieToChannel(out, movie, 0);
}
public static void doWriteFullMovieToChannel(SeekableByteChannel out, Movie movie, int additionalSize) throws IOException {
int sizeHint = estimateMoovBoxSize(movie.getMoov()) + additionalSize;
Logger.debug("Using " + sizeHint + " bytes for MOOV box");
ByteBuffer buf = ByteBuffer.allocate(sizeHint + 128);
movie.getFtyp().write(buf);
movie.getMoov().write(buf);
buf.flip();
out.write(buf);
}
/**
* Estimate buffer size needed to write MOOV box based on the amount of
* stuff in there
*
* @param movie
* @return
*/
public static int estimateMoovBoxSize(MovieBox movie) {
return movie.estimateSize() + (4 << 10);
}
public static String getFourcc(Codec codec) {
return codecMapping.get(codec);
}
public static ByteBuffer writeBox(Box box, int approxSize) {
ByteBuffer buf = ByteBuffer.allocate(approxSize);
box.write(buf);
buf.flip();
return buf;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy