java 视频提取音频 | Java工具类

简介: java 视频提取音频 | Java工具类

简述

将MP4视频内的音频提取出来,该工具类的解决思路如下:

1、提取视频音频pcm数据

2、将pcm数据封装音频头保存。

Maven依赖

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.5</version>
        </dependency>

说明一下,这里用到了hutools工具包,因为可以大幅简化代码。如果有自己的读取文件流的工具类,可以不使用。

代码

上代码

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.Loader;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
/** @Author huyi @Date 2021/10/20 12:11 @Description: mp4提取音频工具类 */
@Slf4j
public class Mp4ExtractAudioUtils {
  /**
   * 获取视频的文件pcm文件地址
   *
   * @param url MP4
   * @return
   * @throws Exception
   */
  public static String getMp4Pcm(String url, String tmpDir) throws Exception {
    Optional<String> pcmPath = Optional.empty();
    try {
      pcmPath = convertMP4toPCM(Paths.get(url), Paths.get(tmpDir));
    } catch (Exception exception) {
      exception.printStackTrace();
      throw new Exception("转换pcm异常:" + exception.getMessage());
    }
    if (pcmPath.isPresent()) {
      return pcmPath.get();
    } else {
      throw new Exception("视频转换音频失败");
    }
  }
  /**
   * 将单个PM4文件进行片头和片尾歌曲删除后,转换为PCM文件
   *
   * @param mp4Path
   * @param pcmDir
   * @return 转换完成后的pcm文件路径
   */
  public static Optional<String> convertMP4toPCM(Path mp4Path, Path pcmDir) {
    String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
    // 基于ffmpeg进行pcm转换
    // 基于输入路径的md5值来命名,也可以基于系统时间戳来命名
    String pcmFile = pcmDir.resolve(UUID.randomUUID() + ".pcm").toString();
    ProcessBuilder pcmBuilder =
        new ProcessBuilder(
            ffmpeg,
            "-y",
            "-i",
            mp4Path.toAbsolutePath().toString(),
            "-vn",
            "-acodec",
            "pcm_s16le",
            "-f",
            "s16le",
            "-ac",
            "1",
            "-ar",
            "16000",
            pcmFile);
    try {
      // inheritIO是指将 子流程的IO与当前java流程的IO设置为相同
      pcmBuilder.inheritIO().start().waitFor();
    } catch (InterruptedException | IOException e) {
      log.error("ffmpeg将mp4转换为pcm时出错", e);
      return Optional.empty();
    }
    // 返回pcm文件路径
    return Optional.of(pcmFile);
  }
  /**
   * 根据PCM文件构建wav的header字段
   *
   * @param srate Sample rate - 8000, 16000, etc.
   * @param channel Number of channels - Mono = 1, Stereo = 2, etc..
   * @param format Number of bits per sample (16 here)
   * @throws IOException
   */
  public static byte[] buildWavHeader(int dataLength, int srate, int channel, int format)
      throws IOException {
    byte[] header = new byte[44];
    long totalDataLen = dataLength + 36;
    long bitrate = srate * channel * format;
    header[0] = 'R';
    header[1] = 'I';
    header[2] = 'F';
    header[3] = 'F';
    header[4] = (byte) (totalDataLen & 0xff);
    header[5] = (byte) ((totalDataLen >> 8) & 0xff);
    header[6] = (byte) ((totalDataLen >> 16) & 0xff);
    header[7] = (byte) ((totalDataLen >> 24) & 0xff);
    header[8] = 'W';
    header[9] = 'A';
    header[10] = 'V';
    header[11] = 'E';
    header[12] = 'f';
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = (byte) format;
    header[17] = 0;
    header[18] = 0;
    header[19] = 0;
    header[20] = 1;
    header[21] = 0;
    header[22] = (byte) channel;
    header[23] = 0;
    header[24] = (byte) (srate & 0xff);
    header[25] = (byte) ((srate >> 8) & 0xff);
    header[26] = (byte) ((srate >> 16) & 0xff);
    header[27] = (byte) ((srate >> 24) & 0xff);
    header[28] = (byte) ((bitrate / 8) & 0xff);
    header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
    header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
    header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
    header[32] = (byte) ((channel * format) / 8);
    header[33] = 0;
    header[34] = 16;
    header[35] = 0;
    header[36] = 'd';
    header[37] = 'a';
    header[38] = 't';
    header[39] = 'a';
    header[40] = (byte) (dataLength & 0xff);
    header[41] = (byte) ((dataLength >> 8) & 0xff);
    header[42] = (byte) ((dataLength >> 16) & 0xff);
    header[43] = (byte) ((dataLength >> 24) & 0xff);
    return header;
  }
  /**
   * 默认写入的pcm数据是16000采样率,16bit,可以按照需要修改
   *
   * @param filePath
   * @param pcmData
   */
  public static String writeToFile(String filePath, byte[] pcmData) {
    BufferedOutputStream bos = null;
    try {
      bos = new BufferedOutputStream(new FileOutputStream(filePath));
      byte[] header = buildWavHeader(pcmData.length, 16000, 1, 16);
      bos.write(header, 0, 44);
      bos.write(pcmData);
      bos.close();
      return filePath;
    } catch (Exception e) {
      e.printStackTrace();
      return filePath;
    } finally {
      if (bos != null) {
        try {
          bos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
  /**
   * 提取音频
   * @param mp4Path MP4地址
   * @param tmpDir 临时文件夹
   * @param resultPath 最终结果音频地址
   * @return 音频地址
   * @throws Exception 异常
   */
  public static String extractAudio(String mp4Path, String tmpDir, String resultPath)
      throws Exception {
    String pcmPath = getMp4Pcm(mp4Path, tmpDir);
    return writeToFile(resultPath, FileUtil.readBytes(pcmPath));
  }
  public static void main(String[] args) throws Exception {
    System.out.println(
        extractAudio(
            "C:\\Users\\huyi\\Desktop\\测试.mp4",
            "C:\\Users\\huyi\\Desktop",
            "C:\\Users\\huyi\\Desktop\\测试.wav"));
  }
}

执行结果:

image.png



可以看到生成一个临时的pcm文件,可以自己调整工具类将临时文件删除。


image.png


image.png


生成音频成功。


总结

这里有几点需要说明。


1、hutools工具主要是为了使用一些方便的组件,可以用自定义的,不一定要引入。


2、关于音频头文件的参数,在另一篇文章我有详细描述,可以参考:生成自定义时长的静音音频 | Java工具类_阿良的博客-CSDN博客


3、里面用到了ffmpeg进行pcm转换,需要本地环境有ffmpeg环境,具体说明在我另一篇有描述,可以参考:java 音频转为wav格式标准音频 | Java工具类_阿良的博客-CSDN博客


4、该工具类部分参数可以调整为入参模式,如果有疑问的话,可以私信我。


如果本文对你有帮助,请点个赞支持一下吧。


相关文章
|
3天前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
|
1月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的反诈视频宣传系统
基于Java+Springboot+Vue开发的反诈视频宣传系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的反诈视频宣传管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
60 4
基于Java+Springboot+Vue开发的反诈视频宣传系统
|
10天前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
30 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
15天前
|
Java
Java 些许公共工具类
Java 些许公共工具类
12 1
|
2月前
|
缓存 前端开发 Java
【前端学java】java基础巩固复习巩固语法练习-工具类的封装(14)
【8月更文挑战第10天】java基础巩固,工具类的封装
24 1
【前端学java】java基础巩固复习巩固语法练习-工具类的封装(14)
|
2月前
|
Java
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
|
2月前
|
存储 设计模式 安全
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
38 0
|
3月前
|
数据采集 Web App开发 Java
Java爬虫安全策略:防止TikTok音频抓取过程中的请求被拦截
Java爬虫安全策略:防止TikTok音频抓取过程中的请求被拦截
|
3月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
57 1
|
3月前
|
Java 程序员 语音技术
怎么用Java 把多个音频拼接成一个?
**Java音频拼接指南** 在Java中,利用音频处理库`cn.juwatech.*`可合并音频文件。步骤包括导入库,创建`AudioFile`对象,将它们添加到列表,然后用`AudioConcatenator.concat()`拼接成一个文件。注意确保音频格式一致,处理异常,并考虑性能优化。此技术提升用户体验,适用于音频编辑和合成场景。[来源:稀土掘金](https://juejin.cn/post/7387701265797840932)