微信上的语音文件拿下来都是amr格式的,好像不分安卓和苹果。
ffmpeg这个工具可以实现音频、视频的格式转换。
java这里,有个现成的包jave,groupId是ws.schild
在maven库里可以看到ws.schild其下有好几个包
jave-core
jave-nativebin-win32
jave-nativebin-win64
jave-nativebin-linux32
jave-nativebin-linux64
jave-nativebin-osx64
jave-all-deps 是最完整的,它的pom.xml引用了所有版本的文件
看名字就能看出,jave-core是核心包,其它几个是windows、linux、maxosx系统下的本地文件
我用的是2.7.3版本,linux下的
<dependency>
<groupId>ws.schild</groupId>
<artifactId>jave-core</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>ws.schild</groupId>
<artifactId>jave-nativebin-linux64</artifactId>
<version>2.7.3</version>
</dependency>
其实nativebin里面就是一个类似ffmpeg.exe的文件
程序在运行时拼接参数,再调用对应的ffmpeg去执行
比如 Encoder的encode方法,其中有一段非常朴素的if/else判断,addArgument方法就是向一个list里追加参数
String codec = videoAttributes.getCodec();
if (codec != null)
{
ffmpeg.addArgument("-vcodec");
ffmpeg.addArgument(codec);
}
String tag = videoAttributes.getTag();
if (tag != null)
{
ffmpeg.addArgument("-vtag");
ffmpeg.addArgument(tag);
}
Integer bitRate = videoAttributes.getBitRate();
if (bitRate != null)
{
ffmpeg.addArgument("-vb");
ffmpeg.addArgument(String.valueOf(bitRate.intValue()));
}
Integer frameRate = videoAttributes.getFrameRate();
if (frameRate != null)
{
ffmpeg.addArgument("-r");
ffmpeg.addArgument(String.valueOf(frameRate.intValue()));
}
VideoSize size = videoAttributes.getSize();
if (size != null)
{
ffmpeg.addArgument("-s");
ffmpeg.addArgument(String.valueOf(size.getWidth()) + "x"
+ String.valueOf(size.getHeight()));
}
if (videoAttributes.isFaststart())
{
ffmpeg.addArgument("-movflags");
ffmpeg.addArgument("faststart");
}
if (videoAttributes.getX264Profile() != null)
{
ffmpeg.addArgument("-profile:v");
ffmpeg.addArgument(videoAttributes.getX264Profile().getModeName());
}
if (videoAttributes.getVideoFilters().size() > 0)
{
for (VideoFilter videoFilter : videoAttributes.getVideoFilters())
{
ffmpeg.addArgument("-vf");
ffmpeg.addArgument(videoFilter.getExpression());
}
}
Integer quality = videoAttributes.getQuality();
if (quality != null)
{
ffmpeg.addArgument("-qscale:v");
ffmpeg.addArgument(String.valueOf(quality.intValue()));
}
打断点可以看到,最后ffmpeg.execute()执行的时候程序用list拼成一个数组,用Runtime去执行
int argsSize = args.size();
String[] cmd = new String[argsSize + 2];
cmd[0] = ffmpegExecutablePath;
for (int i = 0; i < argsSize; i++)
{
cmd[i + 1] = args.get(i);
}
cmd[argsSize + 1] = "-hide_banner"; // Don't show banner
if (LOG.isDebugEnabled())
{
StringBuilder sb = new StringBuilder();
for (String c : cmd)
{
sb.append(c);
sb.append(' ');
}
LOG.debug("About to execute {}", sb.toString());
}
Runtime runtime = Runtime.getRuntime();
ffmpeg = runtime.exec(cmd);
if (destroyOnRuntimeShutdown)
{
ffmpegKiller = new ProcessKiller(ffmpeg);
runtime.addShutdownHook(ffmpegKiller);
}
if (openIOStreams)
{
inputStream = ffmpeg.getInputStream();
outputStream = ffmpeg.getOutputStream();
errorStream = ffmpeg.getErrorStream();
}
大体执行原理是这样。
因为是用java拼参数去调用一个执行文件的方式,所以我没办法用输入输出流做入参。转换代码是用指定源文件和目标文件的字符串路径的方式告诉程序要操作的文件是哪一个。所以,在生产里我用的是先创建临时文件,格式转换后再在finanlly里用Files.deleteIfExists删除的方式。
代码如下
public void putFile(ChatVoice chat) throws Exception {
Path source = null;
File target = null;
try {
source = Files.createTempFile(chat.getMsgid(), ".amr");
target = File.createTempFile(chat.getMsgid(), ".mp3");
Files.write(source, bs);
amrToMp3(source.toFile(), target);
/* target的其它处理逻辑 */
//......
}finally {
Files.deleteIfExists(source);
Files.deleteIfExists(target.toPath());
}
}
public void amrToMp3(File source, File target) throws EncoderException {
//Audio Attributes
AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame"); //lame是一个mp3编码库
// audio.setBitRate(128000);
// audio.setChannels(2);
// audio.setSamplingRate(44100);
EncodingAttributes attrs = new EncodingAttributes();
attrs.setFormat("mp3");
attrs.setAudioAttributes(audio);
//Encode
Encoder encoder = new Encoder();
encoder.encode(new MultimediaObject(source), target, attrs);
}
另外也可以视频格式转换,例子网上到处都有。
代码里audio对象可以设置一些参数,这个我不太懂,大约不特别指定就好,按官方代码指定了采样率和通道数后生成的mp3尺寸大了很多,播放效果还是随着源文件的质量来。
在windows下的cmd窗口运行 ffmpeg-amd64.exe --help 和 fmpeg-amd64.exe --help full
可以看到太多的参数选项,真的很多。
在官方网址里 https://github.com/a-schild/jave2 可以看到一些说明和调用示例
尤其是 More advanced examples 里 https://github.com/a-schild/jave2/blob/master/Examples.md
本文代码基本上是从官网上抄来的,剩下的也是看了网上很多大同小异的文章。