使用servlet过滤器播放amr音频

前话 


       怎样播放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>

看一下效果:

效果

资源链接:


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用servlet过滤器可以完成具体的业务功能。servlet过滤器servlet规范中的一种特殊组件,用于在请求到达目标servlet之前,对请求进行预处理或后处理。 首先,我们可以通过servlet过滤器来实现用户认证的功能。在用户发送请求时,通过过滤器对用户进行身份验证,比如检查用户的登录状态或者验证用户的身份证明。如果用户认证失败,则可以拦截请求并返回相应的错误信息。 其次,我们可以利用servlet过滤器来处理请求的编码和解码。在网络传输中,数据的编码和解码是必要的,通过过滤器我们可以对请求和响应的数据进行编码和解码操作,确保数据的正确传输。 另外,servlet过滤器还可以用于日志记录和统计功能。我们可以通过过滤器来记录用户的访问日志,包括访问时间、IP地址等信息。通过对这些日志进行分析,可以提供一些统计报表,如最受欢迎的页面、访问量最高的时间段等,有利于进行业务分析和优化。 此外,servlet过滤器还可以用于请求的错误处理。当一个请求发生错误时,过滤器可以捕获异常并对错误进行处理,比如自定义错误页面或提供友好的错误提示信息。 综上所述,通过使用servlet过滤器,我们可以完成具体的业务功能,包括用户认证、请求的编码和解码、日志记录和统计以及错误处理等。通过对请求进行预处理或后处理,可以更好地保护和管理我们的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值