All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.tarsos.dsp.io.PipeDecoder Maven / Gradle / Ivy

There is a newer version: 2.4-1
Show newest version
/*
*      _______                       _____   _____ _____  
*     |__   __|                     |  __ \ / ____|  __ \ 
*        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
*        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
*        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
*        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
*                                                         
* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*  
* -------------------------------------------------------------
*
*  Info: http://0110.be/tag/TarsosDSP
*  Github: https://github.com/JorenSix/TarsosDSP
*  Releases: http://0110.be/releases/TarsosDSP/
*  
*  TarsosDSP includes modified source code by various authors,
*  for credits and info, see README.
* 
*/

package be.tarsos.dsp.io;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteOrder;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import be.tarsos.dsp.util.FFMPEGDownloader;

/**
 * 

* Decode audio files to PCM, mono, 16bits per sample, at any sample rate using * an external program. By default ffmpeg is used. Other * command Line programs that are able to decode audio and pipe binary PCM * samples to STDOUT are possible as well (avconv, mplayer). * To install ffmpeg on Debian: apt-get install ffmpeg. *

*

* This adds support for a lot of audio formats and video container formats with * relatively little effort. Depending on the program used also http streams, * rtpm streams, ... are supported as well. *

*

* To see which audio decoders are supported, check *

*
ffmpeg -decoders | grep -E "^A" | sort
avconv version 9.8, Copyright (c) 2000-2013 the Libav developers
  built on Aug 26 2013 09:52:20 with gcc 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)
A... 8svx_exp             8SVX exponential
A... 8svx_fib             8SVX fibonacci
A... aac                  AAC (Advanced Audio Coding)
A... aac_latm             AAC LATM (Advanced Audio Coding LATM syntax)
A... ac3                  ATSC A/52A (AC-3)
A... adpcm_4xm            ADPCM 4X Movie
...
* * @author Joren Six */ public class PipeDecoder { private final static Logger LOG = Logger.getLogger(PipeDecoder.class.getName()); private final String pipeEnvironment; private final String pipeArgument; private final String pipeCommand; private final int pipeBuffer; private boolean printErrorstream = false; private String decoderBinaryAbsolutePath; public PipeDecoder(){ pipeBuffer = 10000; //Use sensible defaults depending on the platform if(System.getProperty("os.name").indexOf("indows") > 0 ){ pipeEnvironment = "cmd.exe"; pipeArgument = "/C"; }else if(new File("/bin/bash").exists()){ pipeEnvironment = "/bin/bash"; pipeArgument = "-c"; }else if (new File("/system/bin/sh").exists()){ //probably we are on android here pipeEnvironment = "/system/bin/sh"; pipeArgument = "-c"; }else{ LOG.severe("Coud not find a command line environment (cmd.exe or /bin/bash)"); throw new Error("Decoding via a pipe will not work: Coud not find a command line environment (cmd.exe or /bin/bash)"); } String path = System.getenv("PATH"); String arguments = " -ss %input_seeking% %number_of_seconds% -i \"%resource%\" -vn -ar %sample_rate% -ac %channels% -sample_fmt s16 -f s16le pipe:1"; if(isAvailable("ffmpeg")){ LOG.info("found ffmpeg on the path (" + path + "). Will use ffmpeg for decoding media files."); pipeCommand = "ffmpeg" + arguments; }else if (isAvailable("avconv")){ LOG.info("found avconv on your path(" + path + "). Will use avconv for decoding media files."); pipeCommand = "avconv" + arguments; }else { if(isAndroid()) { String tempDirectory = System.getProperty("java.io.tmpdir"); printErrorstream=true; File f = new File(tempDirectory, "ffmpeg"); if (f.exists() && f.length() > 1000000 && f.canExecute()) { decoderBinaryAbsolutePath = f.getAbsolutePath(); } else { LOG.severe("Could not find an ffmpeg binary for your Android system. Did you forget calling: 'new AndroidFFMPEGLocator(this);' ?"); LOG.severe("Tried to unpack a statically compiled ffmpeg binary for your architecture to: " + f.getAbsolutePath()); } }else{ LOG.warning("Dit not find ffmpeg or avconv on your path(" + path + "), will try to download it automatically."); FFMPEGDownloader downloader = new FFMPEGDownloader(); decoderBinaryAbsolutePath = downloader.ffmpegBinary(); if(decoderBinaryAbsolutePath==null){ LOG.severe("Could not download an ffmpeg binary automatically for your system."); } } if(decoderBinaryAbsolutePath == null){ pipeCommand = "false"; throw new Error("Decoding via a pipe will not work: Could not find an ffmpeg binary for your system"); }else{ pipeCommand = '"' + decoderBinaryAbsolutePath + '"' + arguments; } } } private boolean isAvailable(String command){ try{ Runtime.getRuntime().exec(command + " -version"); return true; }catch (Exception e){ return false; } } public PipeDecoder(String pipeEnvironment,String pipeArgument,String pipeCommand,String pipeLogFile,int pipeBuffer){ this.pipeEnvironment = pipeEnvironment; this.pipeArgument = pipeArgument; this.pipeCommand = pipeCommand; this.pipeBuffer = pipeBuffer; } public InputStream getDecodedStream(final String resource,final int targetSampleRate,final double timeOffset, double numberOfSeconds) { try { String command = pipeCommand; command = command.replace("%input_seeking%",String.valueOf(timeOffset)); //defines the number of seconds to process // -t 10.000 e.g. specifies to process ten seconds // from the specified time offset (which is often zero). if(numberOfSeconds>0){ command = command.replace("%number_of_seconds%","-t " + String.valueOf(numberOfSeconds)); } else { command = command.replace("%number_of_seconds%",""); } command = command.replace("%resource%", resource); command = command.replace("%sample_rate%", String.valueOf(targetSampleRate)); command = command.replace("%channels%","1"); ProcessBuilder pb; pb= new ProcessBuilder(pipeEnvironment, pipeArgument , command); LOG.info("Starting piped decoding process for " + resource); LOG.info(" with command: " + command); final Process process = pb.start(); final InputStream stdOut = new BufferedInputStream(process.getInputStream(), pipeBuffer){ @Override public void close() throws IOException{ super.close(); // try to destroy the ffmpeg command after close process.destroy(); } }; //print std error if requested if(printErrorstream) { new ErrorStreamGobbler(process.getErrorStream(),LOG).start(); } new Thread(new Runnable(){ @Override public void run() { try { process.waitFor(); LOG.info("Finished piped decoding process"); } catch (InterruptedException e) { LOG.severe("Interrupted while waiting for decoding sub process exit."); e.printStackTrace(); } }},"Decoding Pipe").start(); return stdOut; } catch (IOException e) { LOG.warning("IO exception while decoding audio via sub process." + e.getMessage() ); e.printStackTrace(); } return null; } public double getDuration(final String resource) { double duration = -1; try { //use " for windows compatibility! String command = "ffmpeg -i \"%resource%\""; command = command.replace("%resource%", resource); ProcessBuilder pb; pb= new ProcessBuilder(pipeEnvironment, pipeArgument , command); LOG.info("Starting duration command for " + resource); LOG.fine(" with command: " + command); final Process process = pb.start(); final InputStream stdOut = new BufferedInputStream(process.getInputStream(), pipeBuffer){ @Override public void close() throws IOException{ super.close(); // try to destroy the ffmpeg command after close process.destroy(); } }; ErrorStreamStringGlobber essg = new ErrorStreamStringGlobber(process.getErrorStream()); essg.start(); new Thread(new Runnable(){ @Override public void run() { try { process.waitFor(); LOG.info("Finished piped decoding process"); } catch (InterruptedException e) { LOG.severe("Interrupted while waiting for decoding sub process exit."); e.printStackTrace(); } }},"Decoding Pipe").run(); String stdError = essg.getErrorStreamAsString(); Pattern regex = Pattern.compile(".*\\s.*Duration:\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\.(\\d\\d), .*", Pattern.DOTALL | Pattern.MULTILINE); Matcher regexMatcher = regex.matcher(stdError); if (regexMatcher.find()) { duration = Integer.valueOf(regexMatcher.group(1)) * 3600+ Integer.valueOf(regexMatcher.group(2)) * 60+ Integer.valueOf(regexMatcher.group(3)) * 1 + Double.valueOf("." + regexMatcher.group(4) ); } } catch (IOException e) { LOG.warning("IO exception while decoding audio via sub process." + e.getMessage() ); e.printStackTrace(); } return duration; } public void printBinaryInfo(){ try { Process p = Runtime.getRuntime().exec(decoderBinaryAbsolutePath); BufferedReader input = new BufferedReader(new InputStreamReader(p.getErrorStream())); String line = null; while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); //int exitVal = p.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * Constructs the target audio format. The audio format is one channel * signed PCM of a given sample rate. * * @param targetSampleRate * The sample rate to convert to. * @return The audio format after conversion. */ public static TarsosDSPAudioFormat getTargetAudioFormat(int targetSampleRate) { TarsosDSPAudioFormat audioFormat = new TarsosDSPAudioFormat(TarsosDSPAudioFormat.Encoding.PCM_SIGNED, targetSampleRate, 2 * 8, 1, 2 * 1, targetSampleRate, ByteOrder.BIG_ENDIAN.equals(ByteOrder.nativeOrder())); return audioFormat; } private boolean isAndroid(){ try { // This class is only available on android Class.forName("android.app.Activity"); System.out.println("Running on Android!"); return true; } catch(ClassNotFoundException e) { //the class is not found when running JVM return false; } } private class ErrorStreamGobbler extends Thread { private final InputStream is; private final Logger logger; private ErrorStreamGobbler(InputStream is, Logger logger) { this.is = is; this.logger = logger; } @Override public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { logger.info(line); } } catch (IOException ioe) { ioe.printStackTrace(); } } } private class ErrorStreamStringGlobber extends Thread { private final InputStream is; private final StringBuilder sb; private ErrorStreamStringGlobber(InputStream is) { this.is = is; this.sb = new StringBuilder(); } @Override public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException ioe) { ioe.printStackTrace(); } } public String getErrorStreamAsString(){ return sb.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy