package com.cagneymoreau.videoencoding; import android.util.Base64; import android.util.Log; 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 java.util.Map; /** * * This class is for retrieving the avc1 box thats savde after the movie and returning a string array * with the data needed to start your streaming session. It should be pulled from a short video saved to the * device with the same parameters as the stream will have * */ public class SDPMaker { private final static String TAG = "SDPMaker"; private SDPMaker() { } //pass th file here and get you sps and pps back public static byte[][] retreiveSPSPPS(File file) throws IOException, FileNotFoundException { byte[] sps = new byte[0]; //we will find the bytes and read into these arrays then convert to the string values byte[] pps = new byte[0]; byte[] prfix = new byte[6]; byte[][] spspps; String[] spsppsString = new String[2]; RandomAccessFile randomAccessFile; //file type to allow searching file byte by byte long fileLength = 0; long position = 0; long moovPos = 0; byte[] holder = new byte[8]; //get the file we saved our little video too randomAccessFile = new RandomAccessFile(file, "r"); fileLength = randomAccessFile.length(); // here we find the moov box within the mp4 file while(position < fileLength) { //read our current position and then advance to next position randomAccessFile.read(holder, 0, 8); position += 8; if (checkForBox(holder)) { String name = new String(holder, 4, 4); if (name.equals("moov")) { moovPos = position; Log.d(TAG, "retreiveSPSPPS: found moov box = " + name); break; } } } holder = new byte[4]; boolean searching = true; //were close. now we search for avcC while(searching && position < fileLength){ if (randomAccessFile.read() == 'a') { //Log.d(TAG, "retreiveSPSPPS: found an a"); randomAccessFile.read(holder, 0, 3); //String s = new String(holder); //Log.d(TAG, "retreiveSPSPPS: holder val " + s); if (holder[0] == 'v' && holder[1] == 'c' && holder[2] == 'C'){ Log.d(TAG, "retreiveSPSPPS: found avcC"); searching = false; } position += 4; }else{ position++; } } // here im extracting the prefix. not sure if needed but better to have boolean prefixNeeded = true; byte[] onebyte = new byte[1]; byte[] temp = new byte[5]; while (prefixNeeded && position < fileLength){ onebyte[0] = (byte) randomAccessFile.read(); if (onebyte[0] == 0x01) //we have the data { randomAccessFile.read(temp, 0 , 5); System.arraycopy(onebyte, 0, prfix,0, 1); System.arraycopy(temp, 0, prfix, 1, 5); prefixNeeded = false; position+= 6; Log.d(TAG, "retreiveSPSPPS: prefix found"); }else{ position++; } } //extract the sps and pps byte bLength; byte[] read = new byte[1] ; byte[] remaining; boolean buildng = true; while(buildng && position < fileLength){ bLength = read[0]; //save last byte in case next byte is start randomAccessFile.read(read, 0,1); //String s = new String(read); // Log.d(TAG, "retreiveSPSPPS: searching " + s); if (read[0] == 'g') { //ascii 'g' = hex 67 <- we found the sps int length = bLength & 0xff; //blength is the length of the sps remaining = new byte[length]; randomAccessFile.read(remaining, 0, length-1); //minus 1 because we already read the g //scoot everything down and add our g at the begining for (int i = length-1; i > 0 ; i--) { remaining[i] = remaining[i-1]; } remaining[0] = read[0]; sps = remaining; String s = bytesToHex(remaining); Log.d(TAG, "retreiveSPSPPS: found sps: " + s + " length used: " + String.valueOf(length)); } if (read[0] == 'h'){ Log.d(TAG, "retreiveSPSPPS: found pps"); int length = bLength & 0xff; remaining = new byte[length]; randomAccessFile.read(remaining, 0 , length-1); for (int i = length-1; i> 0 ; i--) { remaining[i] = remaining[i-1]; } remaining[0] = read[0]; pps = remaining; String s = bytesToHex(remaining); Log.d(TAG, "retreiveSPSPPS: found sps: " + s + " length used: " + String.valueOf(length)); buildng = false; } } spspps = new byte[3][]; spspps[0] = sps; spspps[1] = pps; spspps[2] = prfix; spsppsString[0] = Base64.encodeToString(sps, Base64.NO_WRAP); spsppsString[1] = Base64.encodeToString(pps, Base64.NO_WRAP); Log.d(TAG, "retreiveSPSPPS: done -> sps: " + spspps[0] + " pps: " + spspps[1]); return spspps; } //check for boxes in an 8 byte array private static boolean checkForBox(byte[] tocheck) { for (int i=0;i<4;i++) { if ((tocheck[i+4]< 'a' || tocheck[i+4]>'z') && (tocheck[i+4]<'0'|| tocheck[i+4]>'9') ) return false; } return true; } private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } // TODO: 9/13/2018 this goes bye bye //experimental code public static void countNalu(File file) throws IOException, FileNotFoundException { Log.d(TAG, "countNalu: started"); int position = 0; //scroll through until mdat found RandomAccessFile rando = new RandomAccessFile(file, "r"); byte[] holder = new byte[8]; boolean mdatMissing = true; int size = 0; //this will hold our mdat size while(mdatMissing && position < rando.length()) { //read our current position and then advance to next position rando.read(holder, 0, 8); position += 8; if (checkForBox(holder)) { String name = new String(holder, 4, 4); if (name.equals("mdat")) { size = ByteBuffer.wrap(new byte[]{holder[0], holder[1], holder[2], holder[3]}).getInt(); size -= 8; //to account for already viewed header Log.d(TAG, "countNalu: mdat found size = " + String.valueOf(size)); mdatMissing = false; } } } naluSearch(rando, position, size + position); } // TODO: 9/13/2018 we need to check for type 7 and 8 nalus in the stream...just in case private static void naluSearch(RandomAccessFile rando, int currPOs, int maxPos) throws IOException { Log.d(TAG, "naluSearch: started"); int type; int naluLength; byte[] header = new byte[5]; int found = 0; while (currPOs < maxPos){ header[0] = header[1]; header[1] = header[2]; header[2] = header[3]; header[3] = header[4]; header[4] = (byte) rando.read(); currPOs++; type = header[4]&0x1F; if (type == 5 || type == 1) { naluLength = header[3]&0xFF | (header[2]&0xFF)<<8 | (header[1]&0xFF)<<16 | (header[0]&0xFF)<<24; if (naluLength > 0 && naluLength < 100000) { found++; Log.d(TAG, "naluSearch: " + String.valueOf(naluLength)); } if (naluLength==0) { Log.d(TAG, "naluSearch: null nalu"); } } } Log.d(TAG, "countNalu: " + String.valueOf(found)); } }