/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.restcomm.media.resource.player.video.mpeg; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.List; import org.restcomm.media.spi.format.Format; /** * * @author kulikov * @author amit bhayani */ public class MpegPresentation { //File container private FileTypeBox fileTypeBox; //Movei box container private MovieBox movieBox; private MediaDataBox mediaDataBox; //audio tack box and audio hint box private volatile TrackBox audioTrackBox = null; private volatile TrackBox audioHintTrackBox = null; //video track box and hint box private volatile TrackBox videoTrackBox = null; private volatile TrackBox videoHintTrackBox = null; private AudioTrack audioTrack; private VideoTrack videoTrack; private Format audioForamt = null; private Format videFormat = null; public MpegPresentation(URL url) throws IOException { File file = new File(url.getPath()); InputStream input = url.openStream(); DataInputStream ds = new DataInputStream(input); try { //parse file and create tracks parseFile(ds); prepareTracks(); if(audioTrackBox != null && audioHintTrackBox != null){ audioTrack = new AudioTrack(audioTrackBox, audioHintTrackBox, file); } if(videoTrackBox != null && videoHintTrackBox != null){ videoTrack = new VideoTrack(videoTrackBox, videoHintTrackBox, file); } } finally { ds.close(); input.close(); } } public AudioTrack getAudioTrack() { return audioTrack; } public VideoTrack getVideoTrack() { return videoTrack; } public Format getAudioForamt() { return audioForamt; } public Format getVideFormat() { return videFormat; } public void prepareTracks() { for (TrackBox tBox : this.movieBox.getTrackBoxes()) { String type = tBox.getMediaBox().getHandlerReferenceBox().getHandlerType(); if (type.equalsIgnoreCase("soun")) { audioTrackBox = tBox; } else if (type.equalsIgnoreCase("vide")) { videoTrackBox = tBox; } else if (type.equalsIgnoreCase("hint")) { TrackReferenceBox trackReferenceBox = tBox.getTrackReferenceBox(); List<TrackReferenceTypeBox> trackRefTypList = trackReferenceBox.getTrackReferenceTypeBoxes(); for (TrackReferenceTypeBox trkRefTypBox : trackRefTypList) { if (trkRefTypBox.getType().equals(HintTrackReferenceTypeBox.TYPE_S)) { long[] trackIds = trkRefTypBox.getTrackIDs(); // FIXME we are assuming here there is always 1 track that this hint track is referencing // but there could be more than 1 long trackId = trackIds[0]; // FIXME audioTrackBox can be still null if hint track is placed before audioTrack if (audioTrackBox.getTrackHeaderBox().getTrackID() == trackId) { audioHintTrackBox = tBox; //Calculate the Format for(Box b : audioHintTrackBox.getUserDataBox().getUserDefinedBoxes()){ if(b.getType().compareTo(TrackHintInformation.TYPE_S) == 0){ TrackHintInformation trackHintInfBox = (TrackHintInformation)b; RTPTrackSdpHintInformation rtpTrackSdpHintInf = trackHintInfBox.getRtpTrackSdpHintInformation(); if(rtpTrackSdpHintInf != null ){ String sdp = rtpTrackSdpHintInf.getSdpText(); // SessionDescriptor sessionDescriptor = new SessionDescriptor(sdp, false); // MediaDescriptor md = sessionDescriptor.getMediaDescriptor(MediaType.AUDIO); // audioForamt = md.getFormat(0); } } } } else if (videoTrackBox.getTrackHeaderBox().getTrackID() == trackId) { videoHintTrackBox = tBox; } } } } }// for } private void parseFile(DataInputStream ds) throws IOException { long count = 0; while (ds.available() > 0) { long len = readU32(ds); byte[] type = read(ds); if (comparebytes(type, FileTypeBox.TYPE)) { fileTypeBox = new FileTypeBox(len); count += fileTypeBox.load(ds); } else if (comparebytes(type, MovieBox.TYPE)) { movieBox = new MovieBox(len); count += movieBox.load(ds); } else if (comparebytes(type, FreeSpaceBox.TYPE_FREE)) { FreeSpaceBox free = new FreeSpaceBox(len, FreeSpaceBox.TYPE_FREE_S); count += free.load(ds); } else if (comparebytes(type, FreeSpaceBox.TYPE_SKIP)) { FreeSpaceBox skip = new FreeSpaceBox(len, FreeSpaceBox.TYPE_SKIP_S); count += skip.load(ds); } else if (comparebytes(type, MediaDataBox.TYPE)) { // TODO : How should we handle multiple MediaDataBox? mediaDataBox = new MediaDataBox(len); count += mediaDataBox.load(ds); // if (file == null) { // file = new File(this.fileName); // } } else { // TODO : Do we care for other boxes? if (len - 8 > 0) { ds.skipBytes((int) len - 8); } } } } private byte[] read(DataInputStream in) throws IOException { byte[] buff = new byte[4]; for (int i = 0; i < buff.length; i++) { buff[i] = in.readByte(); } return buff; } private boolean comparebytes(byte[] arg1, byte[] arg2) { if (arg1.length != arg2.length) { return false; } for (int i = 0; i < arg1.length; i++) { if (arg1[i] != arg2[i]) { return false; } } return true; } private long readU32(DataInputStream in) throws IOException { return ((long) (in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read())) & 0xFFFFFFFFL; } public void close(){ if(this.audioTrack != null){ this.audioTrack.close(); this.audioTrack = null; } if(this.videoTrack != null){ this.videoTrack.close(); this.videoTrack = null; } } }