目錄
簡述
Maven依賴
代碼
總結
簡述
將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"));
}
}
執行結果:

可以看到生成一個臨時的pcm檔案,可以自己調整工具類將臨時檔案洗掉,


生成音頻成功,
總結
這里有幾點需要說明,
1、hutools工具主要是為了使用一些方便的組件,可以用自定義的,不一定要引入,
2、關于音頻頭檔案的引數,在另一篇文章我有詳細描述,可以參考:生成自定義時長的靜音音頻 | Java工具類_阿良的博客-CSDN博客
3、里面用到了ffmpeg進行pcm轉換,需要本地環境有ffmpeg環境,具體說明在我另一篇有描述,可以參考:java 音頻轉為wav格式標準音頻 | Java工具類_阿良的博客-CSDN博客
4、該工具類部分引數可以調整為入參模式,如果有疑問的話,可以私信我,
如果本文對你有幫助,請點個贊支持一下吧,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/327968.html
標籤:其他
上一篇:晶晨半導體---軟開
