使用servlet过滤器播放amr音频

版权声明:本文为博主原创文章,欢迎大家各种转载,转载请注明出处! https://blog.csdn.net/qq791967024/article/details/49254605

前话 


       怎样播放amr音频?这个问题让我好烦恼,在网上找了一些资料,quicktime插件虽然可以播放amr格式的音频,但是不满足项目的要求,html5也不能播放amr格式的音频。后来想到将amr音频转成其他HTML5支持的格式不久行了,后来在网上找到JAVE能转换音频和视频,但是我在转换的过程中老是报如下的异常:

it.sauronsoftware.jave.EncoderException.EncoderException:Duration: N/A, bitrate: N/A
上面报的异常让我摸不着头脑,不知道是什么意思,后来经过研究JAVE的源代码发现JAVAE内部其实是使用FFMPEG来进行转换,其实就是用java来调用ffmpeg.exe来进行转换(windows下是ffmpeg.exe文件,linux下是ffmpeg文件),然后通过解析转换过程中的输出语句来获取一些信息。后来我自己在window8下通过命令行来进行转换,能转换成功,而且支持的格式也很多。通过仔细的研究转换过程中输出的语句,我终于找到了产生上面异常的原因:在音频或视频的转换过程中,JAVAE有一段通过正则表达式来获取时长,开始时间和比特率的代码,而该正则表达式不能匹配到。
Duration: N/A, bitrate: N/A,而JAVE的这段代码(在it.sauronsoftware.jave.Encoder#public void encode(File source, File target, EncodingAttributes attributes, EncoderProgressListener listener) 中):
if (step == 0) {
    if (line.startsWith("WARNING: ")) {
        if (listener != null) {
            listener.message(line);
        }
    } else if (!line.startsWith("Output #0")) {
        throw new EncoderException(line);
    } else {
        step++;
    }
}
从上面的代码可以看出如果是第0步解析到的某行输出不是以Output #0开头,那么就抛出异常,实际上此时这行的值为Duration: N/A, bitrate: N/A,所以就抛出了如上的异常,从这里也可以看出JAVE是有BUG的:如果通过FFMPEG获取不到时长、开始时间和比特率,那么就会抛出异常,修改上面的配置正则表达式就能修复上面的BUG。实际上JAVE已经很久没维护了,下面进行amr音频格式转换就不使用JAVE,我自己简单的封装一下,可以根据实际的需求进行处理。

实现过程


本文是将amr文件转成mp3文件,然后输出到浏览器,思路:通过过滤器拦截以amr结尾的请求,对请求的路径进行处理,获取到文件所在的真实位置,如果文件不存在则让请求通过,如果存在则找同名的mp3文件,如果同名的mp3文件不存在则将amr转成mp3文件,并以相同的名字以mp3为后缀存储。设置相应的类型为MP3的MIME类型,读取mp3文件并输出。

package cn.zq.amrplay.web.filter;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.zq.amrplay.util.AudioUtils;

/**
 * <p>此过滤器用来拦截所有以amr后缀结尾的请求,并转换成mp3流输出,输出<strong>MIME</strong>类型为audio/mpeg。</p>
 * @author Riccio Zhang
 *
 */
public class Amr2Mp3Filter implements Filter{
	
	/**
	 * mp3扩展名对应的MIME类型,值为"audio/mpeg"
	 */
	public final static String MP3_MIME_TYPE = "audio/mpeg";
	
	public void init(FilterConfig filterConfig) throws ServletException {}
	public void destroy() {}

	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest  request;
	    HttpServletResponse response;
        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) resp;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        
		String requstURI = request.getRequestURI();
		String contextPath = request.getContextPath();
		String resPath = requstURI;
		//去掉requstURI中contextPath部分和参数部分
		if(contextPath.length() > 0) {
			resPath = resPath.substring(contextPath.length());
		}
		int index = 0;
		if((index = resPath.lastIndexOf("?")) != -1) {
			resPath = resPath.substring(0, index);
		}
		
		String resRealPath = req.getServletContext().getRealPath(resPath);
		String mp3ResRealPath = resRealPath.replaceFirst(".amr$", ".mp3");
		File mp3File = new File(mp3ResRealPath);
		if(!mp3File.exists()) {
			File amrFile = new File(resRealPath);
			if(!amrFile.exists()) {
				filterChain.doFilter(request, response);
				return;
			}
			AudioUtils.amr2mp3(amrFile.getAbsolutePath(), mp3File.getAbsolutePath());
		}
		response.setContentLength((int)mp3File.length());
		response.setContentType(MP3_MIME_TYPE);
		InputStream in = new FileInputStream(mp3File);
		OutputStream out = response.getOutputStream();
		try {
			byte[] buf = new byte[1024];
			int len = -1;
			while((len = in.read(buf)) != -1) {
				out.write(buf, 0, len);
			}
		} finally {
			try {
				in.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			out.flush();
		}
	}

}
上面过滤器的实现与上面的思路是吻合的。

音频转换的工具类:

package cn.zq.amrplay.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;


public class AudioUtils {
    /**
     * ffmpeg.exe文件所在的路径
     */
    private final static String FFMPEG_PATH;
    static {
        FFMPEG_PATH = AudioUtils.class.getResource("ffmpeg.exe").getFile();
    }
    /**
     * 将一个amr文件转换成mp3文件
     * @param amrFile 
     * @param mp3File 
     * @throws IOException 
     */
    public static void amr2mp3(String amrFileName, String mp3FileName) throws IOException {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec(FFMPEG_PATH + " -i "+amrFileName+" -ar 8000 -ac 1 -y -ab 12.4k " + mp3FileName);
        InputStream in = process.getErrorStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        try {
            String line = null;
            while((line = br.readLine())!=null) {
                System.out.println(line);
            }
            if(process.exitValue() != 0 ) {
                //如果转换失败,这里需要删除这个文件(因为有时转换失败后的文件大小为0)
                new File(mp3FileName).delete();
                throw new RuntimeException("转换失败!");
            }
        } finally {
            //为了避免这里抛出的异常会覆盖上面抛出的异常,这里需要用捕获异常。
            try {
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
上面的工具类amr2mp3方法,通过java.lang.Runtime类来执行ffmpeg.exe文件,在其后加上一系列的参数了(这个命令类似:ffmpeg -i f:\2.mp3 -ar 8000 -ac 1 -ab 12.2k f:\2.amr),并通过process.getErrorStream()(注意:process.getInputStream()并不能读取到任何输出,这有点奇怪,却要通过错误流才能读取到输出)方法通过流来读取转换过程中的输出,将其包装成了BufferedReader以便每次读取一行,上面只是简单的讲输出打印到了控制台,最后通过判断程序退出值来判断是否转换成功,如果以退出值等于0则表示转换成功,否则抛出异常,删除mp3文件,最后关闭流。

下面简单说明下ffmpeg的几个命令参数:

  • -i :指定输入文件
  • -ar : 指定sampling rate(采样率),它的单位是HZ
  • -ac:指定声道,1表示双声道,0表示单声道
  • -ab:指定转换后的比特率

PS: 写到这里我才发现,这个工具类有点问题,由于项目代码是提前上传的,请将项目代码里的工具类替换为上面的代码。

过滤器配置:

  <filter>
  	<filter-name>Amr2mp3Filter</filter-name>
  	<filter-class>cn.zq.amrplay.web.filter.Amr2Mp3Filter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>Amr2mp3Filter</filter-name>
  	<url-pattern>*.amr</url-pattern>
  </filter-mapping>

看一下效果:

效果

资源链接:


没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试

关闭