[轉]javaCV開發詳解之5:錄製音訊(錄製麥克風)到本地檔案/流媒體伺服器(基於javax.sound、javaCV-FFMPEG)
本文轉載自部落格:https://blog.csdn.net/eguid_1/article/details/52702385
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
javaCV系列文章:
javaCV開發詳解之2:推流器實現,推本地攝像頭視訊到流媒體伺服器以及攝像頭錄製視訊功能實現(基於javaCV-FFMPEG、javaCV-openCV)
javaCV開發詳解之3:收流器實現,錄製流媒體伺服器的rtsp/rtmp視訊檔案(基於javaCV-FFMPEG)
javaCV開發詳解之4:轉流器實現(也可作為本地收流器、推流器,新增新增圖片及文字水印,視訊影象幀儲存),實現rtsp/rtmp/本地檔案轉發到rtmp流媒體伺服器(基於javaCV-FFMPEG)
javaCV開發詳解之5:錄製音訊(錄製麥克風)到本地檔案/流媒體伺服器(基於javax.sound、javaCV-FFMPEG)
javaCV開發詳解之6:本地音訊(話筒裝置)和視訊(攝像頭)抓取、混合並推送(錄製)到伺服器(本地)
javaCV開發詳解之7:讓音訊轉換更加簡單,實現通用音訊編碼格式轉換、重取樣等音訊引數的轉換功能(以pcm16le編碼的wav轉mp3為例)
補充篇:
音視訊編解碼問題:javaCV如何快速進行音訊預處理和解複用編解碼(基於javaCV-FFMPEG)
音視訊編解碼問題:16/24/32位位音訊byte[]轉換為小端序short[],int[],以byte[]轉short[]為例
- 歡迎大家積極開心的加入討論群
群號:371249677 (點選這裡進群)
javacpp-ffmpeg:
javacpp-FFmpeg拉流解碼成YUVJ420P,並儲存為jpg圖片
前言:
本篇文章基於javaCV-FFMPEG,關於javaCV官方是沒有文件或者api文件可以參考的,所以還有很多地方需要研究;
本章對於ffmpeg的需要有一定了解以及對於音訊處理有一定基礎,可以先了解javaCV是如何進行音訊的解複用和編碼的:http://blog.csdn.net/eguid_1/article/details/52875793
1、依賴的包
對於依賴的包,本章用到的jar包有javaCV基礎支撐包(即javaCV,javaCPP)和FFMPEG及其相關平臺的jar包
推薦把javaCV.bin的所有包放到專案目錄中
javaCV.bin下載請到javaCV的github下載:https://github.com/bytedeco/javacv
2、程式碼實現
實現錄製本機麥克風音訊到本地檔案或者流媒體伺服器,
對於錄製音視訊混合的同學可以很方便的將本章程式碼移植到到錄製視訊的程式碼裡
注意:由於音訊、視訊時兩個不同執行緒同時進行,所以在進行混合錄製的時候需要注意統一幀率,以防止音畫不同步現象
-
/**
-
* 設定音訊編碼器 最好是系統支援的格式,否則getLine() 會發生錯誤
-
* 取樣率:44.1k;取樣率位數:16位;立體聲(stereo);是否簽名;true:
-
* big-endian位元組順序,false:little-endian位元組順序(詳見:ByteOrder類)
-
*/
-
AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
-
System.out.println("準備開啟音訊!");
-
// 通過AudioSystem獲取本地音訊混合器資訊
-
Mixer.Info[] minfoSet = AudioSystem.getMixerInfo();
-
// 通過AudioSystem獲取本地音訊混合器
-
Mixer mixer = AudioSystem.getMixer(minfoSet[AUDIO_DEVICE_INDEX]);
-
// 通過設定好的音訊編解碼器獲取資料線資訊
-
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
-
// 開啟並開始捕獲音訊
-
// 通過line可以獲得更多控制權
-
// 獲取裝置:TargetDataLine line
-
// =(TargetDataLine)mixer.getLine(dataLineInfo);
-
Line dataline = null;
-
try {
-
dataline = AudioSystem.getLine(dataLineInfo);
-
} catch (LineUnavailableException e2) {
-
System.err.println("開啟失敗...");
-
return null;
-
}
-
TargetDataLine line = (TargetDataLine) dataline;
-
try {
-
line.open(audioFormat);
-
} catch (LineUnavailableException e1) {
-
line.stop();
-
try {
-
line.open(audioFormat);
-
} catch (LineUnavailableException e) {
-
System.err.println("按照指定音訊編碼器開啟失敗...");
-
return null;
-
}
-
}
-
line.start();
-
System.out.println("已經開啟音訊!");
-
// 獲得當前音訊取樣率
-
int sampleRate = (int) audioFormat.getSampleRate();
-
// 獲取當前音訊通道數量
-
int numChannels = audioFormat.getChannels();
-
// 初始化音訊緩衝區(size是音訊取樣率*通道數)
-
int audioBufferSize = sampleRate * numChannels;
-
byte[] audioBytes = new byte[audioBufferSize];
-
Runnable crabAudio = new Runnable() {
-
ShortBuffer sBuff = null;
-
int nBytesRead;
-
int nSamplesRead;
-
@Override
-
public void run() {
-
System.out.println("讀取音訊資料...");
-
// 非阻塞方式讀取
-
nBytesRead = line.read(audioBytes, 0, line.available());
-
// 因為我們設定的是16位音訊格式,所以需要將byte[]轉成short[]
-
nSamplesRead = nBytesRead / 2;
-
short[] samples = new short[nSamplesRead];
-
/**
-
* ByteBuffer.wrap(audioBytes)-將byte[]陣列包裝到緩衝區
-
* ByteBuffer.order(ByteOrder)-按little-endian修改位元組順序,解碼器定義的
-
* ByteBuffer.asShortBuffer()-建立一個新的short[]緩衝區
-
* ShortBuffer.get(samples)-將緩衝區裡short資料傳輸到short[]
-
*/
-
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
-
// 將short[]包裝到ShortBuffer
-
sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);
-
// 按通道錄製shortBuffer
-
try {
-
System.out.println("錄製音訊資料...");
-
recorder.recordSamples(sampleRate, numChannels, sBuff);
-
} catch (org.bytedeco.javacv.FrameRecorder.Exception e) {
-
// do nothing
-
}
-
}
-
@Override
-
protected void finalize() throws Throwable {
-
sBuff.clear();
-
sBuff = null;
-
super.finalize();
-
}
-
};
-
return crabAudio;
-
}
3、測試錄製麥克風音訊
這裡演示錄製flv
注意:對於想要推送音訊到fms,red5,nginx-rtmp等流媒體伺服器的同學務必請使用flv進行封裝,不管是音訊還是視訊
-
public static void test2() throws InterruptedException, LineUnavailableException {
-
int FRAME_RATE = 25;
-
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
-
Runnable crabAudio = recordMicroPhone(4, "localAudio.flv",FRAME_RATE);//對應上面的方法體
-
ScheduledFuture tasker = exec.scheduleAtFixedRate(crabAudio, 0, (long) 1000 / FRAME_RATE,
-
TimeUnit.MILLISECONDS);
-
Thread.sleep(20 * 1000);
-
tasker.cancel(true);
-
if (!exec.isShutdown()) {
-
exec.shutdownNow();
-
}
-
}