javaCV系列文章:java
javaCV開發詳解之2:推流器實現,推本地攝像頭視頻到流媒體服務器以及攝像頭錄製視頻功能實現(基於javaCV-FFMPEG、javaCV-openCV)git
javaCV開發詳解之3:收流器實現,錄製流媒體服務器的rtsp/rtmp視頻文件(基於javaCV-FFMPEG)github
javaCV開發詳解之4:轉流器實現(也可做爲本地收流器、推流器,新增添加圖片及文字水印,視頻圖像幀保存),實現rtsp/rtmp/本地文件轉發到rtmp流媒體服務器(基於javaCV-FFMPEG)api
javaCV開發詳解之5:錄製音頻(錄製麥克風)到本地文件/流媒體服務器(基於javax.sound、javaCV-FFMPEG)數組
javaCV開發詳解之6:本地音頻(話筒設備)和視頻(攝像頭)抓取、混合並推送(錄製)到服務器(本地)服務器
javaCV開發詳解之7:讓音頻轉換更加簡單,實現通用音頻編碼格式轉換、重採樣等音頻參數的轉換功能(以pcm16le編碼的wav轉mp3爲例)ide
補充篇:測試
音視頻編解碼問題:javaCV如何快速進行音頻預處理和解複用編解碼(基於javaCV-FFMPEG)ui
音視頻編解碼問題:16/24/32位位音頻byte[]轉換爲小端序short[],int[],以byte[]轉short[]爲例
本篇文章基於javaCV-FFMPEG,關於javaCV官方是沒有文檔或者api文檔能夠參考的,因此還有不少地方須要研究;
本章對於ffmpeg的須要有必定了解以及對於音頻處理有必定基礎,能夠先了解javaCV是如何進行音頻的解複用和編碼的:http://blog.csdn.net/eguid_1/article/details/52875793
對於依賴的包,本章用到的jar包有javaCV基礎支撐包(即javaCV,javaCPP)和FFMPEG及其相關平臺的jar包
推薦把javaCV.bin的全部包放到項目目錄中
javaCV.bin下載請到javaCV的github下載:https://github.com/bytedeco/javacv
實現錄製本機麥克風音頻到本地文件或者流媒體服務器,
對於錄製音視頻混合的同窗能夠很方便的將本章代碼移植到到錄製視頻的代碼裏
注意:因爲音頻、視頻時兩個不一樣線程同時進行,因此在進行混合錄製的時候須要注意統一幀率,以防止音畫不一樣步現象
/** * 設置音頻編碼器 最好是系統支持的格式,不然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; }
這裏演示錄製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(); } }
支持eguid原創