/*
* 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.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import net.sourceforge.jaad.mp4.MP4InputStream;
import net.sourceforge.jaad.mp4.boxes.Box;
import net.sourceforge.jaad.mp4.boxes.BoxTypes;
import net.sourceforge.jaad.mp4.boxes.impl.HandlerBox;
import net.sourceforge.jaad.mp4.boxes.impl.MovieHeaderBox;
public class Movie {
private final MP4InputStream in;
private final MovieHeaderBox mvhd;
private final List<Track> tracks;
private final MetaData metaData;
private final List<Protection> protections;
public Movie(Box moov, MP4InputStream in) {
this.in = in;
//create tracks
mvhd = (MovieHeaderBox) moov.getChild(BoxTypes.MOVIE_HEADER_BOX);
List<Box> trackBoxes = moov.getChildren(BoxTypes.TRACK_BOX);
tracks = new ArrayList<Track>(trackBoxes.size());
Track track;
for(int i = 0; i<trackBoxes.size(); i++) {
track = createTrack(trackBoxes.get(i));
if(track!=null) tracks.add(track);
}
//read metadata: moov.meta/moov.udta.meta
metaData = new MetaData();
if(moov.hasChild(BoxTypes.META_BOX)) metaData.parse(null, moov.getChild(BoxTypes.META_BOX));
else if(moov.hasChild(BoxTypes.USER_DATA_BOX)) {
final Box udta = moov.getChild(BoxTypes.USER_DATA_BOX);
if(udta.hasChild(BoxTypes.META_BOX)) metaData.parse(udta, udta.getChild(BoxTypes.META_BOX));
}
//detect DRM
protections = new ArrayList<Protection>();
if(moov.hasChild(BoxTypes.ITEM_PROTECTION_BOX)) {
Box ipro = moov.getChild(BoxTypes.ITEM_PROTECTION_BOX);
for(Box sinf : ipro.getChildren(BoxTypes.PROTECTION_SCHEME_INFORMATION_BOX)) {
protections.add(Protection.parse(sinf));
}
}
}
//TODO: support hint and meta
private Track createTrack(Box trak) {
final HandlerBox hdlr = (HandlerBox) trak.getChild(BoxTypes.MEDIA_BOX).getChild(BoxTypes.HANDLER_BOX);
final Track track;
switch((int) hdlr.getHandlerType()) {
case HandlerBox.TYPE_VIDEO:
track = new VideoTrack(trak, in);
break;
case HandlerBox.TYPE_SOUND:
track = new AudioTrack(trak, in);
break;
default:
track = null;
}
return track;
}
/**
* Returns an unmodifiable list of all tracks in this movie. The tracks are
* ordered as they appeare in the file/stream.
*
* @return the tracks contained by this movie
*/
public List<Track> getTracks() {
return Collections.unmodifiableList(tracks);
}
/**
* Returns an unmodifiable list of all tracks in this movie with the
* specified type. The tracks are ordered as they appeare in the
* file/stream.
*
* @return the tracks contained by this movie with the passed type
*/
public List<Track> getTracks(Type type) {
final List<Track> l = new ArrayList<Track>();
for(Track t : tracks) {
if(t.getType().equals(type)) l.add(t);
}
return Collections.unmodifiableList(l);
}
/**
* Returns an unmodifiable list of all tracks in this movie whose samples
* are encoded with the specified codec. The tracks are ordered as they
* appeare in the file/stream.
*
* @return the tracks contained by this movie with the passed type
*/
public List<Track> getTracks(Track.Codec codec) {
final List<Track> l = new ArrayList<Track>();
for(Track t : tracks) {
if(t.getCodec().equals(codec)) l.add(t);
}
return Collections.unmodifiableList(l);
}
/**
* Indicates if this movie contains metadata. If false the <code>MetaData</code>
* object returned by <code>getMetaData()</code> will not contain any field.
*
* @return true if this movie contains any metadata
*/
public boolean containsMetaData() {
return metaData.containsMetaData();
}
/**
* Returns the MetaData object for this movie.
*
* @return the MetaData for this movie
*/
public MetaData getMetaData() {
return metaData;
}
/**
* Returns the <code>ProtectionInformation</code> objects that contains
* details about the DRM systems used. If no protection is present the
* returned list will be empty.
*
* @return a list of protection informations
*/
public List<Protection> getProtections() {
return Collections.unmodifiableList(protections);
}
//mvhd
/**
* Returns the time this movie was created.
* @return the creation time
*/
public Date getCreationTime() {
return Utils.getDate(mvhd.getCreationTime());
}
/**
* Returns the last time this movie was modified.
* @return the modification time
*/
public Date getModificationTime() {
return Utils.getDate(mvhd.getModificationTime());
}
/**
* Returns the duration in seconds.
* @return the duration
*/
public double getDuration() {
return (double) mvhd.getDuration()/(double) mvhd.getTimeScale();
}
/**
* Indicates if there are more frames to be read in this movie.
*
* @return true if there is at least one track in this movie that has at least one more frame to read.
*/
public boolean hasMoreFrames() {
for(Track track : tracks) {
if(track.hasMoreFrames()) return true;
}
return false;
}
/**
* Reads the next frame from this movie (from one of the contained tracks).
* The frame is the next in time-order, thus the next for playback. If none
* of the tracks contains any more frames, null is returned.
*
* @return the next frame or null if there are no more frames to read from this movie.
* @throws IOException if reading fails
*/
public Frame readNextFrame() throws IOException {
Track track = null;
for(Track t : tracks) {
if(t.hasMoreFrames()&&(track==null||t.getNextTimeStamp()<track.getNextTimeStamp())) track = t;
}
return (track==null) ? null : track.readNextFrame();
}
}