java获取MP3音频播放时长(总结)

睿洛医疗

方法一

        使用jaudiotagger。此方法简单,但取较大MP3的文件头信息有错误,具体为比特率减半、时长翻倍,不建议使用。

<!-- https://mvnrepository.com/artifact/org/jaudiotagger -->
<dependency>
    <groupId>org</groupId>
    <artifactId>jaudiotagger</artifactId>
    <version>2.0.3</version>
</dependency>
public double getMp3Size(String filename) throws Exception{
    double size = 0.0f;
    File file = new File(filename);
    MP3File f=(MP3File)AudioFileIO.read(file);
    MP3AudioHeader audioHeader=(MP3AudioHeader)f.getAudioHeader();
    size = audioHeader.getTrackLength();
    return size;
}
public static void main(String[] args) {
    Mp3Util mu = new Mp3Util();
    String filename = "D:\\MP3\\11.mp3";
    double size = 0.0f;
    try {
        size = mu.getMp3Size(filename);
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println(size);
}

方法二

        使用基于ffmpeg的封装库sauronsoftware。此方法依赖ffmpeg可执行文件,在windows平台和Linux平台要分别安装对应平台的ffmpeg,可移植性差,对视频的处理比较稳定,对音频处理功能不强。不建议使用。

        这个包不在maven服务源,需要先下载,安装到本地源。

下载地址http://www.sauronsoftware.it/projects/jave/jave-1.0.2.zip  下载完成后,命令行安装:

mvn install:install-file -Dfile=E:\jave-1.0.2.jar -DgroupId=it.sauronsoftware -DartifactId=jave -Dversion=1.0.2 -Dpackaging=jar

在这里插入图片描述

 安装完成后在项目中使用

<dependency>
    <groupId>it.sauronsoftware</groupId>
    <artifactId>jave</artifactId>
    <version>1.0.2</version>
</dependency>

        可能因操作系统不同会报 ffmpeg不可执行的错误。找个可以运行的,替换下C:\Windows\Temp\jave-1\ffmpeg.exe。

ffmpeg下载地址https://ffmpeg.org/download.html

public double getDuration(String filename) {
		double size = 0.0f;
		File source = new File(filename);
        Encoder encoder = new Encoder();
        try {
            MultimediaInfo m = encoder.getInfo(source);
            long ls = m.getDuration();
            size = ls/1000;
            System.out.println("时长为:" + size + "秒!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
        
    }

方法三

        使用ffmpeg java扩展库。准确、可跨平台、不依赖ffmpeg可执行文件。(推荐使用)

参考:test-ffmpeg: java对ffmpeg的使用。无需引入ffmpeg.exe文件https://gitee.com/hsjjsh123/test-ffmpeg

		<!-- https://mvnrepository.com/artifact/ws.schild/jave-all-deps -->
		<dependency>
		    <groupId>ws.schild</groupId>
		    <artifactId>jave-all-deps</artifactId>
		    <version>3.2.0</version>
		</dependency>
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FfmpegCmdDurationTest {
	private final static String DURATION_START = "Duration:";
	
	private final static String KEY_FOR_HOUR = "hour";
	private final static String KEY_FOR_MINUTE = "minute";
	private final static String KEY_FOR_SECONED = "seconed";
	private final static String KEY_FOR_MILLSECONED = "millseconed";
	
	//小时 * 60 = 分
	//分 * 60 = 秒
	private final static BigDecimal GAP_60 = new BigDecimal("60");
	//秒* 1000 = 毫秒
	private final static BigDecimal GAP_1000 = new BigDecimal("1000");
	
	/**
	 *  TYPE_0:小时
	 *  TYPE_1:分钟
	 *  TYPE_2:秒钟
	 *  TYPE_3:毫秒 
	 */
	public final static int TYPE_0 = 0;
	public final static int TYPE_1 = 1;
	public final static int TYPE_2 = 2;
	public final static int TYPE_3 = 3;
	
	//ffmpeg执行命令
	private final static String cmd_4_info = "-i D:\\MP3\\22.mp3";

	public static void main(String[] args) {
	  try {
		System.out.println(String.format("获取时长:%s 小时", duration(cmd_4_info,TYPE_0).doubleValue()));
		System.out.println(String.format("获取时长:%s 分钟", duration(cmd_4_info,TYPE_1).doubleValue()));
		System.out.println(String.format("获取时长:%s 秒钟", duration(cmd_4_info,TYPE_2).doubleValue()));
		System.out.println(String.format("获取时长:%s 毫秒", duration(cmd_4_info,TYPE_3).doubleValue()));
		System.out.println("");
		System.out.println(String.format("获取时长(格式化):%s", durationFormat(cmd_4_info)));
	} catch (Exception e) {
		e.printStackTrace();
	}
	}
	/**
	 * 
	 * @Description: (获取格式化的时间duration:such as:00:01:03.32)   
	 * @param: @param cmd_4_info
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: BigDecimal      
	 * @throws
	 */
	public static String durationFormat(final String cmd_4_info) throws Exception {
		String duration = null;
		Map<String,String> map = null;
		try {
			CompletableFuture<String> completableFutureTask = CompletableFuture.supplyAsync(() ->{	
				                String durationStr = null;
				                //执行ffmpeg命令
								StringBuffer sText = getErrorStreamText(cmd_4_info);
								if(null != sText && sText.indexOf(DURATION_START) > -1) {
									String stextOriginal = sText.toString();
									//正则解析时间
									Matcher matcher = patternDuration().matcher(stextOriginal);
							        //正则提取字符串
									while(matcher.find()){
										//such as:00:01:03.32
										durationStr = matcher.group(1);
										break;
									}
								}
								return durationStr;
				   }, ThreadPoolExecutorUtils.pool);

			duration = completableFutureTask.get();

		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return duration;
	}
	/**
	 * 
	 * @Description: (获取时长)   
	 * @param: @param cmd_4_info ffmpeg命令,如:-i I:\\荣耀视频测试.mp4
	 * @param: @param type 类型:
	 *                        TYPE_0:小时
	 *                        TYPE_1:分钟
	 *                        TYPE_2:秒钟
	 *                        TYPE_3:毫秒
	 * @param: @return      
	 * @return: BigDecimal      
	 * @throws Exception 
	 * @throws
	 */
	public static BigDecimal duration(final String cmd_4_info, int type) throws Exception {
		BigDecimal duration = new BigDecimal("00");
		Map<String,String> map = null;
		try {
			CompletableFuture<Map<String,String>> completableFutureTask = CompletableFuture.supplyAsync(() ->{		  	  
				                Map<String,String> mapTmp = null;
				                //执行ffmpeg命令
								StringBuffer sText = getErrorStreamText(cmd_4_info);
								if(null != sText && sText.indexOf(DURATION_START) > -1) {
									String stextOriginal = sText.toString();
									//正则解析时间
									Matcher matcher = patternDuration().matcher(stextOriginal);
							        //正则提取字符串
									while(matcher.find()){
										//such as:00:01:03.32
										String durationStr = matcher.group(1);
										mapTmp = getHourMinuteSeconedMillseconed(durationStr);
										break;
									}
								}
								return mapTmp;
				   }, ThreadPoolExecutorUtils.pool);

			map = completableFutureTask.get();
			if(null != map && map.size() > 0) {
				switch (type) {
				case TYPE_0:
					//小时
					duration = duration.add(new BigDecimal(map.get(KEY_FOR_HOUR)));
					break;
				case TYPE_1:
					//分钟
					duration = duration.add(new BigDecimal(map.get(KEY_FOR_HOUR)).multiply(GAP_60))
					                   .add(new BigDecimal(map.get(KEY_FOR_MINUTE)));
					break;
				case TYPE_2:
					//秒
					duration = duration.add(new BigDecimal(map.get(KEY_FOR_HOUR)).multiply(GAP_60).multiply(GAP_60))
	                                   .add(new BigDecimal(map.get(KEY_FOR_MINUTE)).multiply(GAP_60))
	                                   .add(new BigDecimal(map.get(KEY_FOR_SECONED)));
					break;
				case TYPE_3:
					//毫秒
					duration = duration.add(new BigDecimal(map.get(KEY_FOR_HOUR)).multiply(GAP_60).multiply(GAP_60).multiply(GAP_1000))
	                                   .add(new BigDecimal(map.get(KEY_FOR_MINUTE)).multiply(GAP_60).multiply(GAP_1000))
	                                   .add(new BigDecimal(map.get(KEY_FOR_SECONED)).multiply(GAP_1000))
	                                   .add(new BigDecimal(map.get(KEY_FOR_MILLSECONED)));
					break;
				default:
					throw new Exception("未知的类型!");
				} 
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return duration;
	}
	
	//模式
	public static Pattern patternDuration() {
		//"(?i)duration:\\s*([0-9\\:\\.]+)"-->匹配到时分秒即可,毫秒不需要
		return Pattern.compile("(?i)duration:\\s*([0-9\\:\\.]+)");
	}
	/**
	 * 
	 * @Description: (获取错误流)   
	 * @param: @param cmd_4_info
	 * @param: @return      
	 * @return: StringBuffer      
	 * @throws
	 */
	private static StringBuffer getErrorStreamText(String cmdStr) {
		  //返回的text
		  StringBuffer sText = new StringBuffer();
		  FfmpegCmd ffmpegCmd = new FfmpegCmd();			  			  		
		  try {
;	
				 //错误流
				 InputStream errorStream = null;
				//destroyOnRuntimeShutdown表示是否立即关闭Runtime
				//如果ffmpeg命令需要长时间执行,destroyOnRuntimeShutdown = false
			
				//openIOStreams表示是不是需要打开输入输出流:
				//	       inputStream = processWrapper.getInputStream();
				//	       outputStream = processWrapper.getOutputStream();
				//	       errorStream = processWrapper.getErrorStream();
			   ffmpegCmd.execute(false, true, cmdStr);
			   errorStream = ffmpegCmd.getErrorStream();		   
			   
			    //打印过程
			    int len = 0;
		        while ((len=errorStream.read())!=-1){
		        	char t = (char)len;
//		            System.out.print(t);
		            sText.append(t);
		        }
			   
			   //code=0表示正常
		       ffmpegCmd.getProcessExitCode();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				//关闭资源
				ffmpegCmd.close();
			}
		  //返回
		  return sText;
	}
	
	/**
	 * 
	 * @Description: (获取duration的时分秒毫秒)   
	 * @param: durationStr  such as:00:01:03.32
	 * @return: Map      
	 * @throws
	 */
	private static Map<String,String> getHourMinuteSeconedMillseconed(String durationStr){
		HashMap<String,String> map = new HashMap<>(4);
		if(null != durationStr && durationStr.length() > 0) {
			String[] durationStrArr = durationStr.split("\\:");
			String hour = durationStrArr[0];
			String minute = durationStrArr[1];
			//特殊
			String seconed = "00";
			String millseconed = "00";
			String seconedTmp = durationStrArr[2];
			if(seconedTmp.contains("\\.")) {
				String[] seconedTmpArr = seconedTmp.split("\\.");
				seconed = seconedTmpArr[0];
				millseconed = seconedTmpArr[1];
			}
			else {
				seconed = seconedTmp;
			}
			map.put(KEY_FOR_HOUR, hour);
			map.put(KEY_FOR_MINUTE, minute);
			map.put(KEY_FOR_SECONED, seconed);
			map.put(KEY_FOR_MILLSECONED, millseconed);
		}
		return map;
	}

}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorUtils {

	//多线程
	public static int core = Runtime.getRuntime().availableProcessors();
	public static ExecutorService pool = new ThreadPoolExecutor(core,//核心
			core * 2,//最大
            0L,//空闲立即退出
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024),//无边界阻塞队列
            new ThreadPoolExecutor.AbortPolicy());

}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ws.schild.jave.process.ProcessKiller;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

public class FfmpegCmd {
	
	      private static final Logger LOG = LoggerFactory.getLogger(ProcessWrapper.class);

		  /** The process representing the ffmpeg execution. */
		  private Process ffmpeg = null;
	
		  /**
		   * A process killer to kill the ffmpeg process with a shutdown hook, useful if the jvm execution
		   * is shutted down during an ongoing encoding process.
		   */
		  private ProcessKiller ffmpegKiller = null;
	
		  /** A stream reading from the ffmpeg process standard output channel. */
		  private InputStream inputStream = null;
	
		  /** A stream writing in the ffmpeg process standard input channel. */
		  private OutputStream outputStream = null;
	
		  /** A stream reading from the ffmpeg process standard error channel. */
		  private InputStream errorStream = null;
			 
		  /**
		   * Executes the ffmpeg process with the previous given arguments.
		   *
		   * @param destroyOnRuntimeShutdown destroy process if the runtime VM is shutdown
		   * @param openIOStreams Open IO streams for input/output and errorout, should be false when
		   *     destroyOnRuntimeShutdown is false too
		   * @param ffmpegCmd  windows such as (mp4 transform to mov): 
		   *     " -i C:\\Users\\hsj\\AppData\\Local\\Temp\\jave\\honer.mp4 -c copy C:\\Users\\hsj\\AppData\\Local\\Temp\\jave\\honer_test.mov "
		   * @throws IOException If the process call fails.
		   */
			public void execute(boolean destroyOnRuntimeShutdown, boolean openIOStreams, String ffmpegCmd) throws IOException {
		    	DefaultFFMPEGLocator defaultFFMPEGLocator = new DefaultFFMPEGLocator();
		  	  
		  	    StringBuffer cmd = new StringBuffer(defaultFFMPEGLocator.getExecutablePath());
		  	    //insert blank for delimiter
		  	    cmd.append(" ");
		  	    cmd.append(ffmpegCmd);
		  	    String cmdStr = String.format("ffmpegCmd final is :%s", cmd.toString());
		  	    System.out.println(cmdStr);
		  	    LOG.info(cmdStr);
		  	    
		    	Runtime runtime = Runtime.getRuntime();
			    try {			    	   
			    	    ffmpeg = runtime.exec(cmd.toString());
			    	
			    	    if (destroyOnRuntimeShutdown) {
			    	      ffmpegKiller = new ProcessKiller(ffmpeg);
			    	      runtime.addShutdownHook(ffmpegKiller);
			    	    }
	
			    	    if (openIOStreams) {
			    	      inputStream = ffmpeg.getInputStream();
			    	      outputStream = ffmpeg.getOutputStream();
			    	      errorStream = ffmpeg.getErrorStream();
			    	    }
				} catch (Exception e) {
					e.printStackTrace();
				}
			}   
		    
		    /**
		     * Returns a stream reading from the ffmpeg process standard output channel.
		     *
		     * @return A stream reading from the ffmpeg process standard output channel.
		     */
		    public InputStream getInputStream() {
		      return inputStream;
		    }

		    /**
		     * Returns a stream writing in the ffmpeg process standard input channel.
		     *
		     * @return A stream writing in the ffmpeg process standard input channel.
		     */
		    public OutputStream getOutputStream() {
		      return outputStream;
		    }

		    /**
		     * Returns a stream reading from the ffmpeg process standard error channel.
		     *
		     * @return A stream reading from the ffmpeg process standard error channel.
		     */
		    public InputStream getErrorStream() {
		      return errorStream;
		    }

		    /** If there's a ffmpeg execution in progress, it kills it. */
		    public void destroy() {
		      if (inputStream != null) {
		        try {
		          inputStream.close();
		        } catch (Throwable t) {
		          LOG.warn("Error closing input stream", t);
		        }
		        inputStream = null;
		      }

		      if (outputStream != null) {
		        try {
		          outputStream.close();
		        } catch (Throwable t) {
		          LOG.warn("Error closing output stream", t);
		        }
		        outputStream = null;
		      }

		      if (errorStream != null) {
		        try {
		          errorStream.close();
		        } catch (Throwable t) {
		          LOG.warn("Error closing error stream", t);
		        }
		        errorStream = null;
		      }

		      if (ffmpeg != null) {
		        ffmpeg.destroy();
		        ffmpeg = null;
		      }

		      if (ffmpegKiller != null) {
		        Runtime runtime = Runtime.getRuntime();
		        runtime.removeShutdownHook(ffmpegKiller);
		        ffmpegKiller = null;
		      }
		    }

		    /**
		     * Return the exit code of the ffmpeg process If the process is not yet terminated, it waits for
		     * the termination of the process
		     *
		     * @return process exit code
		     */
		    public int getProcessExitCode() {
		      // Make sure it's terminated
		      try {
		        ffmpeg.waitFor();
		      } catch (InterruptedException ex) {
		        LOG.warn("Interrupted during waiting on process, forced shutdown?", ex);
		      }
		      return ffmpeg.exitValue();
		    }

		    /**close**/
		    public void close() {
		      destroy();
		    }    
		
}

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

外码斯迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值