plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

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 }