/* * Copyright (C) 2011 in-somnia * * This file is part of JAAD. * * JAAD is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * JAAD is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * If not, see <http://www.gnu.org/licenses/>. */ package net.sourceforge.jaad.mp4; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.jaad.mp4.api.Brand; import net.sourceforge.jaad.mp4.api.Movie; import net.sourceforge.jaad.mp4.boxes.Box; import net.sourceforge.jaad.mp4.boxes.BoxFactory; import net.sourceforge.jaad.mp4.boxes.BoxTypes; import net.sourceforge.jaad.mp4.boxes.impl.FileTypeBox; import net.sourceforge.jaad.mp4.boxes.impl.ProgressiveDownloadInformationBox; /** * The MP4Container is the central class for the MP4 demultiplexer. It reads the * container and gives access to the containing data. * * The data source can be either an <code>InputStream</code> or a * <code>RandomAccessFile</code>. Since the specification does not decree a * specific order of the content, the data needed for parsing (the sample * tables) may be at the end of the stream. In this case, random access is * needed and reading from an <code>InputSteam</code> will cause an exception. * Thus, whenever possible, a <code>RandomAccessFile</code> should be used for * local files. Parsing from an <code>InputStream</code> is useful when reading * from a network stream. * * Each <code>MP4Container</code> can return the used file brand (file format * version). Optionally, the following data may be present: * <ul> * <li>progressive download informations: pairs of download rate and playback * delay, see {@link #getDownloadInformationPairs() getDownloadInformationPairs()}</li> * <li>a <code>Movie</code></li> * </ul> * * Additionally it gives access to the underlying MP4 boxes, that can be * retrieved by <code>getBoxes()</code>. However, it is not recommended to * access the boxes directly. * * @author in-somnia */ public class MP4Container { static { Logger log = Logger.getLogger("MP4 API"); for(Handler h : log.getHandlers()) { log.removeHandler(h); } log.setLevel(Level.WARNING); final ConsoleHandler h = new ConsoleHandler(); h.setLevel(Level.ALL); log.addHandler(h); } private final MP4InputStream in; private final List<Box> boxes; private Brand major, minor; private Brand[] compatible; private FileTypeBox ftyp; private ProgressiveDownloadInformationBox pdin; private Box moov; private Movie movie; public MP4Container(InputStream in) throws IOException { this.in = new MP4InputStream(in); boxes = new ArrayList<Box>(); readContent(); } public MP4Container(RandomAccessFile in) throws IOException { this.in = new MP4InputStream(in); boxes = new ArrayList<Box>(); readContent(); } private void readContent() throws IOException { //read all boxes Box box = null; long type; boolean moovFound = false; while(in.hasLeft()) { box = BoxFactory.parseBox(null, in); if(boxes.isEmpty()&&box.getType()!=BoxTypes.FILE_TYPE_BOX) throw new MP4Exception("no MP4 signature found"); boxes.add(box); type = box.getType(); if(type==BoxTypes.FILE_TYPE_BOX) { if(ftyp==null) ftyp = (FileTypeBox) box; } else if(type==BoxTypes.MOVIE_BOX) { if(movie==null) moov = box; moovFound = true; } else if(type==BoxTypes.PROGRESSIVE_DOWNLOAD_INFORMATION_BOX) { if(pdin==null) pdin = (ProgressiveDownloadInformationBox) box; } else if(type==BoxTypes.MEDIA_DATA_BOX) { if(moovFound) break; else if(!in.hasRandomAccess()) throw new MP4Exception("movie box at end of file, need random access"); } } } public Brand getMajorBrand() { if(major==null) major = Brand.forID(ftyp.getMajorBrand()); return major; } public Brand getMinorBrand() { if(minor==null) minor = Brand.forID(ftyp.getMajorBrand()); return minor; } public Brand[] getCompatibleBrands() { if(compatible==null) { final String[] s = ftyp.getCompatibleBrands(); compatible = new Brand[s.length]; for(int i = 0; i<s.length; i++) { compatible[i] = Brand.forID(s[i]); } } return compatible; } //TODO: pdin, movie fragments?? public Movie getMovie() { if(moov==null) return null; else if(movie==null) movie = new Movie(moov, in); return movie; } public List<Box> getBoxes() { return Collections.unmodifiableList(boxes); } }