微信語音分享

你的使用者名稱發表於2019-01-30
微信是大家平時使用最多的聊天軟體,其中可以通過打字、語音、視訊等方式進行溝通。但是大家有沒有發現一個問題,那就是微信裡的語音是分享不了的,就算你是收藏了,然後再分享也會被提示語音格式不支援分享。

微信語音分享


我們想到是否可以以傳送檔案的方式傳送過去呢?我們首先找到微信儲存在本地的語音,在tencent/MicroMsg下面,我們發現微信的語音檔案是.silk字尾名的未知格式檔案,並且手機上沒有自帶app可以開啟這種格式的語音。那麼如果我們直接找到該檔案傳送給好友,微信是否能自動識別然後讓好友聽到呢?答案是不可以的,當我們將該語音以檔案形式發過去的時候好友並不能播放,說明微信也是做過限制的。那麼有沒有辦法可以將微信的語音分享出去讓別人聽到呢?答案當然是可以的,這就是作者今天要分享的內容,如何開啟微信儲存在本地的語音檔案。想要實現該功能就必須瞭解音視訊基礎,接下來作者來講解一下。

音視訊基礎

微信語音分享


音視訊檔案如何生成的?

如上圖,影象的原始格式是YUV/RGB。由於原始資料過大,需要對原始影象進行壓縮成H-264/VP8,壓縮過程也叫做編碼。

同理音訊的過程也大致類似,音訊的原始格式是PCM,同時也需要進行壓縮成AAC/AMR。

編碼以後將音訊和視訊封裝成一個MP4/FLV檔案,即我們常見的音視訊檔案。


音視訊檔案時如何播放的?

播放其實就是和生成相反的步驟,入上圖可看到,首先要解封裝,然後音訊和視訊分別進行解壓縮,解碼成系統能夠識別的格式,進而呼叫系統硬體進行工作。


其實安卓本身提供了錄製和播放PCM的操作,只是由於MediaRecorder使用起來更簡單易懂,所以可能不是很瞭解。經過對音視訊編解碼的瞭解,那麼思路就有了,只要我們能將微信的.silk檔案裡的PCM採集出來,那是不是就可以通過Android自帶的PCM播放器AudioTrack播放出來了?下面作者就帶領大家一起學習一下AudioTrack播放PCM的byte資料。


AudioTrack其實使用起來非常簡單,只需要4步就搞定了


步驟一   建立AudioTrack物件

int mBufferSizeInBytes = AudioTrack.getMinBufferSize(

sampleRate, // 取樣率,每秒採集資料次數

AudioFormat.CHANNEL_OUT_MONO, // 聲道

AudioFormat.ENCODING_PCM_16BIT); // 位數,一個資料16位


mAudioTrack = new AudioTrack(

AudioManager.STREAM_MUSIC, // 音樂流 (告訴系統將該資料看成音樂來播放)

sampleRate, // 取樣率 (每秒取樣資料的次數,次數越大聲音越真實,但是耗效能)

AudioFormat.CHANNEL_OUT_MONO, // 聲道 (雙聲道、單聲道等選擇好,現在是單聲道)

AudioFormat.ENCODING_PCM_16BIT, //採集的資料每個資料所佔的位數

mBufferSizeInBytes, // 緩衝區大小,上面設定好的

AudioTrack.MODE_STREAM);  //設定以流的形式進行播放


步驟二    啟動播放器

mAudioTrack.play();


步驟三    開始播放(MODE_STREAM)

mAudioTrack.write(mOutputBuffer, 0, len);


步驟四    停止與釋放

mAudioTrack.stop();
mAudioTrack.release();


以上播放的準備已經做好了,只需要拿到PCM往上面的AudioTrack中傳入就好了,那麼今天重點來了,應該怎麼做才能將silk檔案中的PCM採集出來呢?


如何從微信語音檔案中獲取PCM資料

想獲取其中的資料就必須知道silk是什麼格式其實Silk是Skype開發的開源的音訊壓縮格式和音訊編解碼器。它是為在Skype中使用而開發的。實時頻寬6-40Kbps即可工作,即使丟包水平達到10%依然可以穩定維持24KHz取樣的通話音質,這個是該技術行業非常領先的。

那麼Skype又是什麼產品呢,Skype是一款即時通訊軟體,其具備IM所需的功能,比如視訊聊天、多人語音會議、多人聊天、傳送檔案、文字聊天等功能,類似於微信QQ。

總結一下,silk就是就是一個C語言開發的開源的音訊編解碼庫。既然微信是使用的第三方的編碼方式,所以思路來了,我們只需要讓開源的silk庫對微信語音檔案進行解碼,再播放即可。

前面說過,silk是一個C語言的庫,如果想在Android中使用到C語言的庫,就必須進行NDK的開發了。


什麼是JNI和NDK?

JNI:

Java Native Interface,JDK提供的一套API,實現了Java和其他語言的通訊(主要是C&C++),它允許Java程式碼和其他語言寫的程式碼進行互動。

NDK:

Native Development Kit,本地開發工具包

微信語音分享

微信語音分享

NDK和SDK很類似。在SDK中java檔案是通過javac編譯成class,然後再通過dx打包成classes.dex包。在NDK中.c/.cpp檔案時通過gcc等編譯成.o檔案,然後通過ar打包成.so包。


如何使用ndk開發將silk檔案解碼成PCM?

前面已經研究過了,只需要呼叫silk庫進行解碼即可,由於silk的解碼sdk是C語言庫,所以需要使用ndk開發。接下來我要做的思路大致如下:使用者從本地選擇一個檔案,然後程式碼中通過獲取該檔案的path,迴圈讀取byte陣列傳入silk庫中進行解碼成PCM,然後傳入AudioTrack中進行播放。這裡我們定義3個需要在c檔案中書寫的native方法,其中nativeInit方法用來呼叫silk的初始化方法來建立解碼器,nativeDecode方法是用來呼叫silk的解碼方法,實時傳入從語音文字中獲取到的byte陣列給silk庫,nativeClean是用來善後處理,比如講解碼器關閉,回收c的記憶體等。

private native void nativeInit();
private native int nativeDecode(byte[] silkData, short byteSize, short[] outBuffer);
private native void nativeClean();複製程式碼


如何判斷某檔案是silk格式的?

如果根據檔案字尾名來判斷是不嚴謹的,這裡就要用到魔數知識了,一般在檔案的位元組碼開頭或者結尾會有。可以通過010Editor這款16進位制檢視軟體進行檢視位元組碼資料我們開啟所有的silk檔案,其字首都是固定的。不光是silk,其他的檔案格式也是如此,大部分都會在開頭或者結尾有一串固定的位元組碼。這個固定的位元組碼就叫做魔數,所以我們只需要判斷檔案的魔數是否是silk擁有的魔數即可。

微信語音分享

接下來我們具體在程式碼中實現一下,如下圖這個dis就是本地的微信語音檔案儲存路徑,我們將它讀入進輸入流中,然後獲取前10個位元組,接下來和silk的魔數字節字首HEADER陣列進行對比,如果完全一樣就說明該檔案是silk檔案,接下來才能進行解碼以及播放的操作。

微信語音分享

微信語音分享


每次應該給silk傳多大的byte陣列?

其實在魔數後的2個位元組是short資料,比如前面那個截圖魔數後面跟的是13 00,那麼應該如何將這個13 00轉化成音訊資料的長度呢?這裡還有大小端之分,需要將16進位制的13 00 交換位置,變成00 13,然後再轉換成10進位制,變成19,也就是說接下來一段音訊會有19個位元組碼

微信語音分享

微信語音分享

至此,其中的坑點都已經明確了,我們只需要按照思路走下去就可以了,在java層呼叫c層的解碼程式碼如上,迴圈依次獲取到每段音訊的2位16進位制位元組碼,然後由於大小端位元組碼順序進行替換,接下來獲取到實際的長度,然後傳遞計算出來的長度到nativeDecode方法中,其中初始化了一個outBuffer用來接收從silk傳遞回來的解碼後的資料,最後呼叫AudioTrack播放器進行播放已解碼的PCM資料,最後成功的播放了微信檔案。只需要讓好友安裝我們的app,就可以聽到我們給TA分享的微信語音啦!

微信語音分享

核心的silk解碼程式碼如上圖,我們可以看到其實就是呼叫了核心的silk庫進行解碼,然後迴圈接收所有的silk庫返回的byte陣列,最後寫入到java層傳入的接收引數的outBuffer陣列中。其餘2個native方法分別用於初始化silk解碼器以及記憶體回收,使用malloc申請了記憶體就需要free來釋放,這裡程式碼就不貼了,大家感興趣的話可以進一步瞭解silk庫以及C語言的基本使用。

坑點:最後需要注意,有的6.0以上手機需要動態申請獲取本地檔案許可權,然後注意那個path在不同手機上可能會有相容性問題,主要體現在7.0以後必須使用fileprovider來共享檔案,所以要通過contentresover來解析Uri,而不能直接getPath,不然會報fileNotFoundException。

總結:今天我們主線是嘗試使用silk庫來解碼微信的語音檔案,並且使用Android系統自帶的AudioTrack播放器對silk解碼後的PCM資料進行播放。其中講解了音視訊的基礎知識、JNI和NDK開發的基礎知識、silk庫以及AudioTrack播放器的使用、檢視檔案的16進位制位元組碼方式010Editor、檔案的魔數區分方法等知識,希望能對大家有所幫助!


相關文章