前言:
前面我用了很多章實現了javaCV的基本操作,包括:音視頻捕捉(攝像頭視頻捕捉和話筒音頻捕捉),推流(本地音視頻或者攝像頭話筒混合推流到服務器),轉流(rtsp->rtmp),收流(錄制)。
序:
我們知道javaCV中編碼需要先取到一幀采樣的音頻(即采樣率x通道數,我們姑且把這個稱為一幀采樣數據)
其實我們在該篇文章http://blog.csdn.net/eguid_1/article/details/52804246中已經對音頻進行轉碼了。
額。。這個真沒看出來(PS:博主也沒看出來 0_0 !)。。。。。。。。。
我們獲取了本地的音頻音頻數據(具體啥編碼博主也不曉得,只知道是16位的, - -! ,不過這不要緊,FFMPEG能我們實現,下面將會講到 );
其中我們做了大小端序的轉換和byte[]轉short[](雙8位轉單16位),音頻編解碼中這個操作我們會經常用;
然后我們使用了recoder.reacordSimples(采樣率,通道數,一份采樣);
對比一下音頻捕獲的文章:http://blog.csdn.net/eguid_1/article/details/52702385
發現了嗎?沒錯,我們給recorder設置了一些屬性:
// 不可變(固定)音頻比特率 recorder.setAudioOption("crf", "0"); // 最高質量 recorder.setAudioQuality(0); // 音頻比特率 recorder.setAudioBitrate(192000); // 音頻采樣率 recorder.setSampleRate(44100); // 雙通道(立體聲) recorder.setAudioChannels(2); // 音頻編/解碼器 recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);看到了嗎?我們其實已經設置了編/解碼格式aac,為什么呢?因為javaCV已經封裝了解復用和編碼這兩個操作。
補充:
補充一下javaCV底層的ffmpeg解復用/編碼流程:
我們在進行recoder.reacordSimples的時候javaCV底層調用ffmpeg的swr_convert()方法(詳見javaCV的FFmpegFrameRecoder類974行)進行了解碼操作,完成了對pcm16le編碼的解復用;
解碼完成之后又調用了recorder.record(Frame frame)(詳見javaCV的FFmpegFrameRecoder類994行),在這個環節完成了調用了FFMPEG的avcodec_encode_audio2()方法(詳見javaCV的FFmpegFrameRecoder類1006行)按照我們已經設定好的的aac格式完成了編
碼操作,所以我們本身是不需要進行解復用/編碼的操作的(視頻也是一樣,以后會講到),因為javaCV已經幫我門做了!
到這里肯定有些小伙伴已經5臉懵bi的狀態了。。。 - -!,最不幸的是,上面一堆的前言和補充知識,我們的主題還沒開始。 0_0 !
eguid唯一技術博客是csdn,博主唯一交流群是群號:371249677 (點擊這里進群),歡迎大家來埋汰群主
1、java音頻預處理
既然javaCV已經幫我門做了解復用和編碼,那么我們只需要將獲得到的音頻數據進行簡單的預處理即可。
注:如果是文件或者服務器直播流,那么連預處理都省了,直接設置編碼格式即可,不需要我們手動做任何處理。
這里講一下特殊的byte[]流,也就是基於socket的IO流形式的音頻數據處理,一般我們使用這種的情況是移動端通過socket推流到中轉服務器,讓中轉服務器進行轉碼推送到流媒體服務器。
1.1、如何從byte[]流中獲取一份完整的音頻幀(即一幀采樣數據)
就拿 8000采樣率,16bit,雙通道(立體聲)的pcm16le編碼來說吧舉例說明吧
我們知道這個音頻采樣率是8000,位數是16bit,2個通道,那么我們就知道這個編碼的一幀就是(8000x2 )個byte
1.2、音頻原始數據轉換
一個byte只能表示8bit數據,我們要表示16位的音頻數據就需要裝換為short,一個short等於2個byte,在轉換的同時進行大小端序轉換(大小端序問題詳見http://blog.csdn.net/eguid_1/article/details/52790848),那么我們最后得到的數據應該是一個長度是8000的short數組(即short[8000])來表示一幀音頻采樣數據。
音頻的預處理到此完畢,接下來該javaCV出場了
2、javaCV音頻解復用及編碼
通過上面一大堆的前言,已經知道:音頻數據直接通過recorder設置音頻編碼參數即可自動進行解復用和編碼
只需要調用recorder.recordSamples(采樣率,通道數量,一份采樣數據)即可。
我的天吶,這真真是用一行代碼解決了C/C++好幾百行的事情!