Back to project page streamvid.
The source code is released under:
GNU General Public License
If you think the Android project streamvid listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package net.majorkernelpanic.streaming.mp4; /*/*ww w. j a v a 2 s. c o m*/ * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com * * This file is part of libstreaming (https://github.com/fyhertz/libstreaming) * * Spydroid is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This source code 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.HashMap; import android.util.Base64; import android.util.Log; /** * Parse an mp4 file. * An mp4 file contains a tree where each node has a name and a size. * This class is used by H264Stream.java to determine the SPS and PPS parameters of a short video recorded by the phone. */ public class MP4Parser { private static final String TAG = "MP4Parser"; private HashMap<String, Long> mBoxes = new HashMap<String, Long>(); private final RandomAccessFile mFile; private long mPos = 0; /** Parses the mp4 file. **/ public static MP4Parser parse(String path) throws IOException { return new MP4Parser(path); } private MP4Parser(final String path) throws IOException, FileNotFoundException { mFile = new RandomAccessFile(new File(path), "r"); try { parse("",mFile.length()); } catch (Exception e) { e.printStackTrace(); throw new IOException("Parse error: malformed mp4 file"); } } public void close() { try { mFile.close(); } catch (Exception e) {}; } public long getBoxPos(String box) throws IOException { Long r = mBoxes.get(box); if (r==null) throw new IOException("Box not found: "+box); return mBoxes.get(box); } public StsdBox getStsdBox() throws IOException { try { return new StsdBox(mFile,getBoxPos("/moov/trak/mdia/minf/stbl/stsd")); } catch (IOException e) { throw new IOException("stsd box could not be found"); } } private void parse(String path, long len) throws IOException { ByteBuffer byteBuffer; long sum = 0, newlen = 0; byte[] buffer = new byte[8]; String name = ""; if(!path.equals("")) mBoxes.put(path, mPos-8); while (sum<len) { mFile.read(buffer,0,8); mPos += 8; sum += 8; if (validBoxName(buffer)) { name = new String(buffer,4,4); if (buffer[3] == 1) { // 64 bits atom size mFile.read(buffer,0,8); mPos += 8; sum += 8; byteBuffer = ByteBuffer.wrap(buffer,0,8); newlen = byteBuffer.getLong()-16; } else { // 32 bits atom size byteBuffer = ByteBuffer.wrap(buffer,0,4); newlen = byteBuffer.getInt()-8; } // 1061109559+8 correspond to "????" in ASCII the HTC Desire S seems to write that sometimes, maybe other phones do // "wide" atom would produce a newlen == 0, and we shouldn't throw an exception because of that if (newlen < 0 || newlen == 1061109559) throw new IOException(); Log.d(TAG, "Atom -> name: "+name+" position: "+mPos+", length: "+newlen); sum += newlen; parse(path+'/'+name,newlen); } else { if( len < 8){ mFile.seek(mFile.getFilePointer() - 8 + len); sum += len-8; } else { int skipped = mFile.skipBytes((int)(len-8)); if (skipped < ((int)(len-8))) { throw new IOException(); } mPos += len-8; sum += len-8; } } } } private boolean validBoxName(byte[] buffer) { for (int i=0;i<4;i++) { // If the next 4 bytes are neither lowercase letters nor numbers if ((buffer[i+4]< 'a' || buffer[i+4]>'z') && (buffer[i+4]<'0'|| buffer[i+4]>'9') ) return false; } return true; } static String toHexString(byte[] buffer,int start, int len) { String c; StringBuilder s = new StringBuilder(); for (int i=start;i<start+len;i++) { c = Integer.toHexString(buffer[i]&0xFF); s.append( c.length()<2 ? "0"+c : c ); } return s.toString(); } } class StsdBox { private RandomAccessFile fis; private byte[] buffer = new byte[4]; private long pos = 0; private byte[] pps; private byte[] sps; private int spsLength, ppsLength; /** Parse the sdsd box in an mp4 file * fis: proper mp4 file * pos: stsd box's position in the file */ public StsdBox (RandomAccessFile fis, long pos) { this.fis = fis; this.pos = pos; findBoxAvcc(); findSPSandPPS(); } public String getProfileLevel() { return MP4Parser.toHexString(sps,1,3); } public String getB64PPS() { return Base64.encodeToString(pps, 0, ppsLength, Base64.NO_WRAP); } public String getB64SPS() { return Base64.encodeToString(sps, 0, spsLength, Base64.NO_WRAP); } private boolean findSPSandPPS() { /* * SPS and PPS parameters are stored in the avcC box * You may find really useful information about this box * in the document ISO-IEC 14496-15, part 5.2.4.1.1 * The box's structure is described there * <pre> * aligned(8) class AVCDecoderConfigurationRecord { * unsigned int(8) configurationVersion = 1; * unsigned int(8) AVCProfileIndication; * unsigned int(8) profile_compatibility; * unsigned int(8) AVCLevelIndication; * bit(6) reserved = 111111b; * unsigned int(2) lengthSizeMinusOne; * bit(3) reserved = 111b; * unsigned int(5) numOfSequenceParameterSets; * for (i=0; i< numOfSequenceParameterSets; i++) { * unsigned int(16) sequenceParameterSetLength ; * bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit; * } * unsigned int(8) numOfPictureParameterSets; * for (i=0; i< numOfPictureParameterSets; i++) { * unsigned int(16) pictureParameterSetLength; * bit(8*pictureParameterSetLength) pictureParameterSetNALUnit; * } * } * </pre> */ try { // TODO: Here we assume that numOfSequenceParameterSets = 1, numOfPictureParameterSets = 1 ! // Here we extract the SPS parameter fis.skipBytes(7); spsLength = 0xFF&fis.readByte(); sps = new byte[spsLength]; fis.read(sps,0,spsLength); // Here we extract the PPS parameter fis.skipBytes(2); ppsLength = 0xFF&fis.readByte(); pps = new byte[ppsLength]; fis.read(pps,0,ppsLength); } catch (IOException e) { return false; } return true; } private boolean findBoxAvcc() { try { fis.seek(pos+8); while (true) { while (fis.read() != 'a'); fis.read(buffer,0,3); if (buffer[0] == 'v' && buffer[1] == 'c' && buffer[2] == 'C') break; } } catch (IOException e) { return false; } return true; } }