使用Java代码实现----根据文件地址获取音视频时长---问题解决:No audio header found withina.mp3错误以及读取时长为0

一、引言

因为工作需要,在网上找了许多方法来实现,七拼八凑总算是把接口搞通了,肯定还有更好的方法,但下面这个方法亲测有效!

二、添加依赖

在pom.xml配置文件中添加如下依赖:

<!-- mp3文件支持-->
<dependency>
	<groupId>org</groupId>
	<artifactId>jaudiotagger</artifactId>
	<version>2.0.1</version>
</dependency>
<!-- mp4文件支持-->
<dependency>
	<groupId>com.googlecode.mp4parser</groupId>
	<artifactId>isoparser</artifactId>
	<version>1.1.22</version>
</dependency>

三、添加工具类

以工具类包装更为方便
注意:这里会出现1秒的文件获取时长为0。
可参考六、问题解决----1秒的时长在未出错的情况下读出来是0秒,因为是我自己的解决方案,可能有更适合的,所以没有在这里直接加上!

3.1 视频文件工具类

package com.blcup.cledu.teach.utils;
import com.coremedia.iso.IsoFile;
import java.io.IOException;
public class VideoUtil {
    /**
     * 获取视频文件的播放长度(mp4、mov格式)
     * @param videoPath
     * @return 单位为毫秒
     */
    public static long getMp4Duration(String videoPath) throws IOException {
        IsoFile isoFile = new IsoFile(videoPath);
        long lengthInSeconds =
                isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
                isoFile.getMovieBox().getMovieHeaderBox().getTimescale();
        return lengthInSeconds;
    }
    
    /**
     * 得到语音或视频文件时长,单位秒
     * @param filePath
     * @return
     * @throws IOException
     */
    public static long getDuration(String filePath) throws IOException {
        String format = getVideoFormat(filePath);
        long result = 0;
        if("wav".equals(format)){
            result = AudioUtil.getDuration(filePath).intValue();
        }else if("mp3".equals(format)){
            result = AudioUtil.getMp3Duration(filePath).intValue();
        }else if("m4a".equals(format)) {
            result = VideoUtil.getMp4Duration(filePath);
        }else if("mov".equals(format)){
            result = VideoUtil.getMp4Duration(filePath);
        }else if("mp4".equals(format)){
            result = VideoUtil.getMp4Duration(filePath);
        }
        return result;
    }
 
    /**
     * 得到语音或视频文件时长,单位秒
     * @param filePath
     * @return
     * @throws IOException
     */
    public static long getDuration(String filePath,String format) throws IOException {
        long result = 0;
        if("wav".equals(format)){
            result = AudioUtil.getDuration(filePath).intValue();
        }else if("mp3".equals(format)){
            result = AudioUtil.getMp3Duration(filePath).intValue();
        }else if("m4a".equals(format)) {
            result = VideoUtil.getMp4Duration(filePath);
        }else if("mov".equals(format)){
            result = VideoUtil.getMp4Duration(filePath);
        }else if("mp4".equals(format)){
            result = VideoUtil.getMp4Duration(filePath);
        }
 
        return result;
    }
 
    /**
     * 得到文件格式
     * @param path
     * @return
     */
    public static String getVideoFormat(String path){
        return  path.toLowerCase().substring(path.toLowerCase().lastIndexOf(".") + 1);
    } 
} 

3.2 音频文件工具类

package com.blcup.cledu.teach.utils;
import com.coremedia.iso.IsoFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import java.io.File;
import java.io.IOException;
/**
 * @Author xxx
 **/
public class AudioUtil {

    /**
     * 获取mp3语音文件播放时长(秒) mp3
     * @param filePath
     * @return
     */
    public static Float getMp3Duration(String filePath){

        try {
            File mp3File = new File(filePath);
            MP3File f = (MP3File) AudioFileIO.read(mp3File);
            MP3AudioHeader audioHeader = (MP3AudioHeader)f.getAudioHeader();
            return Float.parseFloat(audioHeader.getTrackLength()+"");
        } catch(Exception e) {
            e.printStackTrace();
            return 0f;
        }
    }
    
    /**
     * 获取mp3语音文件播放时长(秒)
     * @param mp3File
     * @return
     */
    public static Float getMp3Duration(File mp3File){
        try {
            //File mp3File = new File(filePath);
            MP3File f = (MP3File) AudioFileIO.read(mp3File);
            MP3AudioHeader audioHeader = (MP3AudioHeader)f.getAudioHeader();
            return Float.parseFloat(audioHeader.getTrackLength()+"");
        } catch(Exception e) {
            e.printStackTrace();
            return 0f;
        }
    }

    /**
     * 得到pcm文件的毫秒数
     *
     * pcm文件音频时长计算
     * 同图像bmp文件一样,pcm文件保存的是未压缩的音频信息。 16bits 编码是指,每次采样的音频信息用2个字节保存。可以对比下bmp文件用分别用2个字节保存RGB颜色的信息。 16000采样率 是指 1秒钟采样 16000次。常见的音频是44100HZ,即一秒采样44100次。 单声道: 只有一个声道。
     *
     * 根据这些信息,我们可以计算: 1秒的16000采样率音频文件大小是 2*16000 = 32000字节 ,约为32K 1秒的8000采样率音频文件大小是 2*8000 = 16000字节 ,约为 16K
     *
     * 如果已知录音时长,可以根据文件的大小计算采样率是否正常。
     * @param filePath
     * @return
     */
    public static long getPCMDurationMilliSecond(String filePath) {
        File file = new File(filePath);
        //得到多少秒
        long second = file.length() / 32000 ;
        long milliSecond = Math.round((file.length() % 32000)   / 32000.0  * 1000 ) ;
        return second * 1000 + milliSecond;
    }
}

四、亲测成功

我这里是通过资源id获取资源文件的详细信息的实现类,其中包含对音频时长的处理,注释里有详细标注

@Override
    public TeachExerciseFileVO getById(Long id) {
    //通过id获取资源详情
     TeachExerciseFile teachExerciseFile = baseMapper.selectById(id);
     TeachExerciseFileVO teachExerciseFileVO = new TeachExerciseFileVO();
      //因为我这里在数据库表中并没有时长字段,所以先把实体类其他信息复制到VO中,再单独插入时长
     BeanUtils.copyProperties(teachExerciseFile,teachExerciseFileVO);
     
     // --------------------------------------------------------------------------------------------------开始处理音频时长
     //这里的path是一个文件的远程地址,我先获取到文件再存到本地进行操作---如果没有从远程获取文件的前提,本身就在本地的话可以略过
     String path = fileProperties.getOss().getEndpoint() + fileProperties.getBucketName()+"/"+teachExerciseFileVO.getPath();
        try {
        	//通过远程url地址建立连接
            URL url = new URL(path);
            URLConnection connection = url.openConnection();
            InputStream inputStream = connection.getInputStream();
            //此处的   System.getProperty("java.io.tmpdir"   指的是本地临时文件路径,因为正常开发环境下要适应Windows和Linux等不同环境,所以一般不写死,这里是提前配置过的临时文件路径
            FileOutputStream outputStream = new FileOutputStream(System.getProperty("java.io.tmpdir")+File.separator+"a.mp3");
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.close();
            inputStream.close();
            //-------------------------------------如果文件直接就在本地的话,直接跳到这里,不需要上面的从远程读写操作
            //上面的音频工具类中有不同的获取音频时长的方法,此处使用的是通过本地文件路径获取
            Float duration = AudioUtil.getMp3Duration(System.getProperty("java.io.tmpdir")+File.separator+"a.mp3");

	//----------------------------------------------------------------------------------------------------处理音频时长结束
            //将时长插入到VO中
            teachExerciseFileVO.setDuration(duration.longValue());
            //用完文件后把本地的文件删除掉,正常生产环境需要删,自己研究可以不删
            File file = new File(System.getProperty("java.io.tmpdir")+File.separator+"a.mp3");
            file.delete();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //返回VO
        return teachExerciseFileVO;
    }

五、No audio header found withina.mp3 错误解决-----可能未报错但是时长为0

1)场景:我有一个接口是通过传Base64编码的方式上传的,我直接把Base64编码解密后使用文件流上传了。
2)原因:Base64 编码是一种将二进制数据转换成文本格式的编码方式,它仅仅是对原始数据的编码,并不包含音频文件的元数据,例如音频头信息、采样率和播放时长等。创文件的不包含读的时候自然读不到!!!
3)解决方案:在不是通过文件形式(MultipartFile类型或者File类型)上传的地方,先写入到本地文件,再以文件形式上传就好了,用完文件删除本地文件即可。
在这里插入图片描述
4)修改前代码:
在这里插入图片描述
5)修改后代码:
在这里插入图片描述
代码版:

FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(System.getProperty("java.io.tmpdir") + File.separator + name + "." + suffix);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1){
                fileOutputStream.write(buffer,0,bytesRead);
            }
            fileOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

六、问题解决----1秒的时长在未出错的情况下读出来是0秒

本人猜测:因为测试了其他文件读取时间都是对的,只有1秒时被读成了0秒,猜测是获取文件时长所需的时间超过1秒,所以还未获取到时长文件就结束了,所以为0秒,这里我是手动设置成了1秒。代码如下:

public static Float getMp3Duration(String filePath){
        try {
            File mp3File = new File(filePath);
            MP3File f = (MP3File) AudioFileIO.read(mp3File);
            MP3AudioHeader audioHeader = (MP3AudioHeader)f.getAudioHeader();
            int trackLength = audioHeader.getTrackLength();
            //原代码是直接获取后返回,我这里手动增加了判断,如果读取没问题却读成了0,就手动设置为1返回
            if (trackLength==0){
                trackLength = 1;
            }
            return Float.parseFloat(trackLength+"");
        } catch(Exception e) {
            e.printStackTrace();
            return 0f;
        }
    }

七、结束

路漫漫其修远兮,吾将上下而求索!
任何疑问欢迎私信指教!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

karry0130

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

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

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

打赏作者

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

抵扣说明:

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

余额充值