借助三方工具ffmpeg.exe实现音频文件和背景音乐合成
/**
* 借助三方工具ffmpeg.exe实现音频文件和背景音乐合成
*
* @author tums
* @2019年5月11日14:29:57
*/
public class FFmpegBuilder {
private static Logger logger = LoggerFactory.getLogger(FFmpegBuilder.class);
/**
* 音频文件最长不能超过10分钟
*/
private static final int MAX_SECONDS = 600;
/**
* 执行命令 循环+混音(音量以前面的为准)
* ffmpeg -i /usr/local/ffmpeg/永恒瞬间.m4a -i /usr/local/ffmpeg/灵动线条.m4a -filter_complex '[1:a]aloop=loop=-1:size=2e+09[out];[out][0:a]amix' -ss 0 -t 126 -y /usr/local/ffmpeg/x.m4a
*/
// private static final String COMMAND_STR = "%s -i %s -i %s -filter_complex '[1:a]aloop=loop=-1:size=2e+09[bg];[0:a][bg]amix' -ss 0 -t %s -y %s";
private static final String COMMAND_STR = "%s -i %s -i %s -filter_complex '[1:a]aloop=loop=-1:size=2e+09[bg];[0:a][bg]amix[out];[out]volume=volume=1.5' -ar 44100 -ss 0 -t %s -y %s";
private static final String COMMAND_MP32M4A_STR = "%s -i %s %s";
private static final String COMMAND_AUDIO_LENGTH_STR = "ffprobe %s";
/**
* mp3 to m4a
*
* @param ffmpeg
* @param input
* @param output
* @throws RuntimeException
*/
public static void mp32m4a(String ffmpeg, String input, String output) throws RuntimeException {
execute(String.format(COMMAND_MP32M4A_STR, ffmpeg, input, output));
}
private static void execute(String cmd) throws RuntimeException {
logger.info("cmd start {}", cmd);
try {
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd});
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
errorGobbler.start();
StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
outGobbler.start();
process.waitFor();
logger.info("cmd completed");
} catch (Exception e) {
logger.error("cmd fail ", e);
throw new RuntimeException("音频处理失败");
}
}
/**
* Duration: 00:00:11.55, start: 0.050113, bitrate: 68 kb/s
*
* @param filename
* @return
* @throws RuntimeException
*/
public static long getAudioLength(String filename) throws RuntimeException {
String info = getExecuteInfo(String.format(COMMAND_AUDIO_LENGTH_STR, filename));
String strDuration = "Duration:";
int startIndex = info.indexOf(strDuration);
if (startIndex <= 0) {
return 0;
}
int endIndex = info.indexOf(",", startIndex);
String durationInfo = info.substring(startIndex + strDuration.length(), endIndex).trim();
String[] arrs = durationInfo.split("[:.]");
int length = arrs.length;
if (length >= 3) {
int h = Integer.parseInt(arrs[0]) * 3600;
int m = Integer.parseInt(arrs[1]) * 60;
int s = Integer.parseInt(arrs[2]);
return h + m + s;
}
return 0;
}
private static String getExecuteInfo(String cmd) throws RuntimeException {
logger.info("cmd start {}", cmd);
Runtime runtime = Runtime.getRuntime();
InputStream errorStream = null;
try {
Process process = runtime.exec(cmd);
errorStream = process.getErrorStream();
process.waitFor();
String respBody = IOUtils.toString(errorStream, "utf-8");
System.out.println(respBody);
return respBody;
} catch (Exception e) {
logger.error("cmd fail ", e);
throw new RuntimeException("音频处理失败");
} finally {
logger.info("cmd completed");
try {
if (errorStream != null) {
errorStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param inputA 输入音频
* @param inputB 背景音乐
* @param seconds 限制时长-单位s
* @param outputC 输出音频
* @throws RuntimeException
*/
public static void process(String ffmpeg, String inputA, String inputB, int seconds, String outputC) throws RuntimeException {
if (seconds <= 0) {
throw new RuntimeException("音频时长不能小于0");
} else if (seconds > MAX_SECONDS) {
seconds = MAX_SECONDS;
}
execute(String.format(COMMAND_STR, ffmpeg, inputA, inputB, seconds, outputC));
}
/**
* @param inputA 输入音频
* @param inputB 背景音乐
* @param seconds 限制时长-单位s
* @param outputC 输出音频
* @throws RuntimeException
*/
public static void processWindows(String ffmpeg, String inputA, String inputB, int seconds, String outputC) throws RuntimeException {
long start = System.currentTimeMillis();
if (seconds <= 0) {
throw new RuntimeException("音频时长不能小于0");
} else if (seconds > MAX_SECONDS) {
seconds = MAX_SECONDS;
}
List<String> command = new ArrayList<>();
command.add(ffmpeg);
command.add("-i");
command.add(inputA);
command.add("-i");
command.add(inputB);
command.add("-filter_complex");
//linux 必须加双引号,windows 可以不加
command.add("'[1:a]aloop=loop=-1:size=2e+09[out];[out][0:a]amix'");
command.add("-ss");
command.add("0");
command.add("-t");
command.add(String.valueOf(seconds));
command.add("-y");
command.add(outputC);
logger.info("cmd {}", Arrays.toString(command.toArray()));
try {
new ProcessBuilder(command).start();
long use = System.currentTimeMillis() - start;
logger.info("use {}ms", use);
} catch (IOException e) {
logger.error("音频合成失败", e);
throw new RuntimeException("音频合成失败");
}
}
public static void main(String[] args) {
String cmd = "%s %s";
String exe = "ffprobe";
String file = "D:\\tmp\\a.mp3";
try {
long audioLength = FFmpegBuilder.getAudioLength(String.format(cmd, exe, file));
System.out.println(audioLength);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class StreamGobbler extends Thread {
private InputStream is;
private String type;
private OutputStream os;
public StreamGobbler(InputStream is, String type) {
this(is, type, null);
}
public StreamGobbler(InputStream is, String type, OutputStream redirect) {
this.is = is;
this.type = type;
this.os = redirect;
}
@Override
public void run() {
try {
PrintWriter pw = null;
if (os != null) {
pw = new PrintWriter(os);
}
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
if (pw != null) {
pw.println(line);
}
}
if (pw != null)
pw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}