AndroidFFMPEGLocator.java (6736B)
1 package be.tarsos.dsp.io.android; 2 3 import android.content.Context; 4 import android.content.res.AssetManager; 5 import android.os.Build; 6 import android.util.Log; 7 8 import java.io.BufferedReader; 9 import java.io.File; 10 import java.io.FileInputStream; 11 import java.io.FileOutputStream; 12 import java.io.IOException; 13 import java.io.InputStream; 14 import java.io.InputStreamReader; 15 import java.io.OutputStream; 16 17 /** 18 * <p> 19 * The Android FFMPEG locator determines the current CPU architecture of the 20 * running Android device and extracts a statically compiled <a href="http://ffmpeg.org">ffmpeg</a> 21 * binary from the assets folder to the temporary directory of the currently running Android application. 22 * For this to work the assets folder should contain these binaries: 23 * </p> 24 * 25 * <li> 26 * <ul><code>assets/x86_ffmpeg</code> for x86</ul> 27 * <ul><code>assets/armeabi-v7a_ffmpeg</code> for armeabi-v7a</ul> 28 * <ul><code>assets/armeabi-v7a-neon_ffmpeg</code> for armeabi-v7a-neon</ul> 29 * </li> 30 * 31 * 32 * <p> 33 * You can download these binaries 34 * <a href="https://github.com/hiteshsondhi88/ffmpeg-android/releases/download/v0.3.3/prebuilt-binaries.zip">here</a> 35 * and on the <a href="http://0110.be/releases/TarsosDSP/TarsosDSP-static-ffmpeg/Android/">TarsosDSP ffmpeg repository</a>. 36 * Other architectures are currently not supported but could be included in later releases. 37 * </p> 38 * 39 * <p> 40 * If you are a masochist and want to compile ffmpeg for Android yourself you can get your fix <a href="https://github.com/hiteshsondhi88/ffmpeg-android">here</a> 41 * </p> 42 * @author Joren Six 43 */ 44 public class AndroidFFMPEGLocator { 45 46 private static final String TAG = "AndroidFFMPEGLocator"; 47 48 public AndroidFFMPEGLocator(Context context){ 49 CPUArchitecture architecture = getCPUArchitecture(); 50 51 Log.i(TAG,"Detected Native CPU Architecture: " + architecture.name()); 52 53 if(!ffmpegIsCorrectlyInstalled()){ 54 String ffmpegFileName = getFFMPEGFileName(architecture); 55 AssetManager assetManager = context.getAssets(); 56 unpackFFmpeg(assetManager,ffmpegFileName); 57 } 58 File ffmpegTargetLocation = AndroidFFMPEGLocator.ffmpegTargetLocation(); 59 Log.i(TAG, "Ffmpeg binary location: " + ffmpegTargetLocation.getAbsolutePath() + " is executable? " + ffmpegTargetLocation.canExecute() + " size: " + ffmpegTargetLocation.length() + " bytes"); 60 } 61 62 private String getFFMPEGFileName(CPUArchitecture architecture){ 63 final String ffmpegFileName; 64 switch (architecture){ 65 case X86: 66 ffmpegFileName = "x86_ffmpeg"; 67 break; 68 case ARMEABI_V7A: 69 ffmpegFileName = "armeabi-v7a_ffmpeg"; 70 break; 71 case ARMEABI_V7A_NEON: 72 ffmpegFileName = "armeabi-v7a-neon_ffmpeg"; 73 break; 74 default: 75 ffmpegFileName = null; 76 String message= "Could not determine your processor architecture correctly, no ffmpeg binary available."; 77 Log.e(TAG,message); 78 throw new Error(message); 79 } 80 return ffmpegFileName; 81 } 82 83 private boolean ffmpegIsCorrectlyInstalled(){ 84 File ffmpegTargetLocation = AndroidFFMPEGLocator.ffmpegTargetLocation(); 85 //assumed to be correct if existing and executable and larger than 1MB: 86 return ffmpegTargetLocation.exists() && ffmpegTargetLocation.canExecute() && ffmpegTargetLocation.length() > 1000000; 87 } 88 89 private void unpackFFmpeg(AssetManager assetManager,String ffmpegAssetFileName) { 90 InputStream inputStream=null; 91 OutputStream outputStream=null; 92 try{ 93 File ffmpegTargetLocation = AndroidFFMPEGLocator.ffmpegTargetLocation(); 94 inputStream = assetManager.open(ffmpegAssetFileName); 95 outputStream = new FileOutputStream(ffmpegTargetLocation); 96 byte buffer[] = new byte[1024]; 97 int length = 0; 98 while((length=inputStream.read(buffer)) > 0) { 99 outputStream.write(buffer,0,length); 100 } 101 //makes ffmpeg executable 102 ffmpegTargetLocation.setExecutable(true); 103 Log.i(TAG,"Unpacked ffmpeg binary " + ffmpegAssetFileName + " , extracted " + ffmpegTargetLocation.length() + " bytes. Extracted to: " + ffmpegTargetLocation.getAbsolutePath()); 104 }catch (IOException e) { 105 e.printStackTrace(); 106 }finally { 107 //cleanup 108 try { 109 if (inputStream != null) { 110 inputStream.close(); 111 } 112 }catch (IOException e) { 113 e.printStackTrace(); 114 } 115 try { 116 if (outputStream != null) { 117 outputStream.close(); 118 } 119 }catch (IOException e) { 120 e.printStackTrace(); 121 } 122 } 123 } 124 125 private static final File ffmpegTargetLocation(){ 126 String tempDirectory = System.getProperty("java.io.tmpdir"); 127 File ffmpegTargetLocation = new File(tempDirectory,"ffmpeg"); 128 return ffmpegTargetLocation; 129 } 130 131 private enum CPUArchitecture{ 132 X86,ARMEABI_V7A,ARMEABI_V7A_NEON; 133 } 134 135 private boolean isCPUArchitectureSupported(String alias) { 136 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 137 for (String supportedAlias : Build.SUPPORTED_ABIS) { 138 if (supportedAlias.equals(alias)) 139 return true; 140 } 141 142 return false; 143 } else { 144 return Build.CPU_ABI.equals(alias); 145 } 146 } 147 148 private CPUArchitecture getCPUArchitecture() { 149 // check if device is x86 150 if (isCPUArchitectureSupported("x86")) { 151 return CPUArchitecture.X86; 152 } else if (isCPUArchitectureSupported("armeabi-v7a")) { 153 // check if NEON is supported: 154 if (isNeonSupported()) { 155 return CPUArchitecture.ARMEABI_V7A_NEON; 156 } else { 157 return CPUArchitecture.ARMEABI_V7A; 158 } 159 } 160 return null; 161 } 162 163 private boolean isNeonSupported() { 164 try { 165 BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(new File("/proc/cpuinfo")))); 166 String line = null; 167 while ((line = input.readLine()) != null) { 168 Log.d(TAG, "CPUINFO line: " + line); 169 if(line.toLowerCase().contains("neon")) { 170 return true; 171 } 172 } 173 input.close(); 174 } catch (IOException e) { 175 e.printStackTrace(); 176 } 177 return false; 178 } 179 180 }