問題背景
最近項(xiàng)目可能需要對(duì)聲音進(jìn)行加密,解密祷蝌,進(jìn)行識(shí)別認(rèn)證茅撞,在網(wǎng)上有一篇文章介紹了如何使用AES算法在java中對(duì)音頻進(jìn)行加密: Android 加密/解密音頻文件(AES);
其具體的做法就是將加密文件和解密文件全部保存到存儲(chǔ)器上巨朦,在未來項(xiàng)目中需要頻繁的讀寫硬盤可不是一個(gè)好的建議米丘,所以尋找能夠保存的是加密的音頻,然后直接對(duì)加密音頻進(jìn)行解密罪郊,之后直接在內(nèi)存中進(jìn)行播放蠕蚜。
于是又找到了一篇關(guān)于實(shí)時(shí)播放wav,但是我們需要的mp3悔橄,最好還是解決實(shí)時(shí)播放wav文章的最初原作者同樣給出了實(shí)時(shí)解碼mp3的解決方法:Android Audio: Play an MP3 file on an AudioTrack靶累;
下面的代碼就是混合了他們所有人的產(chǎn)物腺毫,部分內(nèi)容也是直接翻譯過來。
項(xiàng)目已上傳
Github-使用TrackAudio播放AES加密的mp3
這里的邏輯
為什么加密和TrackAudio能夠混在一起用挣柬,講下這里的邏輯:因?yàn)椴シ舖p3使用TrackAudio潮酒,它使用的是音頻流,即輸入的是byte[],而AES解密后輸出也是byte[],所以AES解密后直接輸出byte的邪蛔,再傳遞給TrackAudio進(jìn)行音頻播放也就順理成章了急黎。
而具體的流程就是:使用AES機(jī)密mp3文件,并將解密后的文件newByte_track(byte[]類型)侧到,直接交給ByteArrayInputStream變成InputStream in勃教;
InputStream in = new ByteArrayInputStream(newByte_track);
Bitstream bitstream = new Bitstream(in);
變?yōu)锽itstream bitstream ,而Jlayer正好可以將bitstream 作為輸入匠抗,然后輸出SampleBuffer
SampleBuffer sampleBuffer = (SampleBuffer) mDecoder.decodeFrame(header, bitstream);
最后把SampleBuffer 轉(zhuǎn)換為byte[],我們的TrackAudio是可以直接播放byte[]故源。
首先不談加密,直接播放mp3
以下來自翻譯
在Android中能夠播放mp3的接口只有MediaPlayer汞贸,沉重绳军,慢,并且僅提供高級(jí)接口矢腻,如果你需要修改或者混合音頻流的話门驾,你就得自己動(dòng)手,AudioTrack就能幫到你多柑,我建議先閱讀(<a >the article about playing a WAV</a>) 這篇文章奶是,它包含了一些關(guān)于PCM基本常識(shí)。
因?yàn)閃AV文件基本可以算無損顷蟆,而MP3就不同了诫隅,它已經(jīng)經(jīng)過復(fù)雜的算法解碼過腐魂,所以我們需要借助第三方代碼帐偎,允許我們將MP3數(shù)據(jù)轉(zhuǎn)換為raw PCM數(shù)據(jù),之后就可以按部就班的讓AudioTrack播放了蛔屹。
經(jīng)過漫長的搜索削樊,終于找到了一款mp3解碼器<a >Jlayer</a>。它可以輕松的解碼MP3(當(dāng)然從Jlayer的界面也能看出兔毒,它是為Java SE 平臺(tái)專設(shè)的漫贞,時(shí)間也是許久之前,不過Android也基于java育叁,所以索性就試試)迅脐,Jlayer的授權(quán)協(xié)議類LGPL 協(xié)議,對(duì)商用app也非常友好豪嗽。
在使用Jlayer之前谴蔑,首先把它導(dǎo)入進(jìn)來到你的項(xiàng)目中來豌骏。
下邊是調(diào)用Jlayer代碼
InputStream in = new ByteArrayInputStream(newByte_track);
Bitstream bitstream = new Bitstream(in);
final int READ_THRESHOLD = 2147483647;
int framesReaded = READ_THRESHOLD;
Header header;
for(; framesReaded-- > 0 && (header = bitstream.readFrame()) != null;) {
SampleBuffer sampleBuffer = (SampleBuffer) mDecoder.decodeFrame(header, bitstream);
Log.e("header",String.valueOf(header.framesize ));
short[] buffer = sampleBuffer.getBuffer();
for (short s : buffer) {
outStream.write(s & 0xff);
outStream.write((s >> 8 ) & 0xff);
}
bitstream.closeFrame();
}
byte[] Byte_JLayer=outStream.toByteArray();
這里需要說明下:
short[] buffer = sampleBuffer.getBuffer();
for (short s : buffer) {
outStream.write(s & 0xff);
outStream.write((s >> 8 ) & 0xff);
}
上邊的代碼表示的是將short類型的值寫入到“數(shù)據(jù)輸出流”中,見[DataOutputStream(數(shù)據(jù)輸出流)的認(rèn)知隐锭、源碼和示例;計(jì)算機(jī)內(nèi)的存儲(chǔ)都是利用二進(jìn)制的補(bǔ)碼進(jìn)行存儲(chǔ)的窃躲,byte類型的數(shù)字要&0xff再賦值給int類型,其本質(zhì)原因就是想保持二進(jìn)制補(bǔ)碼的一致性钦睡,見byte為什么要與上0xff蒂窒?。
這里參數(shù)修改如下時(shí)荞怒,播放的音樂的完整性和播放開始的速度都是不一樣的洒琢。
//大約需要14s,但是歌曲可以完整保存下來
// final int READ_THRESHOLD = 2147483647;//我試著改動(dòng)了褐桌,沒有變化;
//需要3s纬凤,但是音樂沒有播放完就結(jié)束了
final int READ_THRESHOLD = 1024;//我試著改動(dòng)了,沒有變化;
int framesReaded = READ_THRESHOLD;
其中
mDecoder = new Decoder();
這里Decoder類的完整類名javazoom.jl.decoder.Decoder.Decoder()撩嚼,當(dāng)然代碼時(shí)死的停士,不需要記憶,但是你要知道它的原理完丽,然后實(shí)現(xiàn)它恋技。
當(dāng)然也可以走原生路線,找一個(gè)用C寫的MP3解碼器(這里也有一個(gè)連接逻族,但是我沒有實(shí)現(xiàn)蜻底,需要JNI和NDK的知識(shí)),在此不提聘鳞,
如果你需要在你的app中解碼mp3薄辅,這篇文章可以幫助到你。
在使用Jlayer成功獲得到數(shù)據(jù) byte[] Byte_JLayer抠璃,剩下的工作就交給AudioTrack了站楚,當(dāng)然在將byte[] Byte_JLayer存入AudioTrack,首先是配置AudioTrack:
final int sampleRate = 44100;
final int minBufferSize = AudioTrack.getMinBufferSize(sampleRate,
//MI3:CHANNEL_OUT_STEREO //[]AudioFormat.CHANNEL_OUT_STEREO
//CHANNEL_OUT_MONO影響不大搏嗡,只要是new AudioTrack構(gòu)建時(shí)選擇AudioFormat.CHANNEL_OUT_STEREO即可
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
//這里的關(guān)鍵詞就是復(fù)制窿春,粘貼,調(diào)參數(shù)剛剛拿了一個(gè)舊的mp2采盒,試過旧乞,當(dāng)然是錯(cuò)誤的
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate,
AudioFormat.CHANNEL_OUT_STEREO, // CHANNEL_OUT_STEREO 聲音嘈雜 ,CHANNEL_OUT_DEFAULT磅氨,CHANNEL_IN_DEFAULT尺栖,也是有噪音
AudioFormat.ENCODING_PCM_16BIT,//AudioFormat.CHANNEL_CONFIGURATION_DEFAULT也是有聲音
2*minBufferSize,
AudioTrack.MODE_STREAM);
AES加密解密具體流程
高級(jí)加密標(biāo)準(zhǔn)(英語:Advanced Encryption Standard,縮寫:AES)烦租,在密碼學(xué)中又稱Rijndael加密法延赌,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)货徙。這個(gè)標(biāo)準(zhǔn)用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用皮胡。(來自百度百科)
加密的過程
我直接使用Android 加密/解密音頻文件(AES)中的加密方法痴颊。加密的過程簡單來說就是讀取文件轉(zhuǎn)換為byte[],解密,他的思路就是保存為音樂文件屡贺,然后播放蠢棱,而我的思路是直接交給AudioTrack和Jlayer去運(yùn)行byte,這樣每次就不用保存到存儲(chǔ)空間上甩栈,而對(duì)外泻仙,mp3一直處于加密狀態(tài),在PC端也同樣無法播放量没。
備注
我在加密過程中注意玉转,使用mp3(通常下載的歌曲沒有問題),但是注意如果使用Adobe Audition時(shí)就要注意如果輸出的格式進(jìn)行了設(shè)置殴蹄,有時(shí)Jlayer和AudioTrack也是無法滿足的究抓,有時(shí)會(huì)有噪音的輸出,有時(shí)就是沒有聲音袭灯,例如我在使用Adobe Audition導(dǎo)出mp2后刺下,修改后綴為mp3,依然還是無法播放稽荧,因?yàn)楸窘坛痰慕獯a就是面向mp3而存在橘茉。
下邊的這些參數(shù)實(shí)際也是跟隨android設(shè)備進(jìn)行設(shè)置的,可以通過調(diào)整細(xì)微的參數(shù)姨丈,看AudioTrack播放音樂時(shí)會(huì)有不同的表現(xiàn)畅卓。
效果圖
將代碼保重的54wall拷貝到手機(jī)的內(nèi)部存儲(chǔ)器或者SD卡上,54wall文件夾中有一個(gè)q.mp3,是導(dǎo)盲犬小Q的一個(gè)音樂蟋恬,然后運(yùn)行項(xiàng)目后就是下邊的效果翁潘,此時(shí)的q.mp3是沒有加密過的,直接點(diǎn)擊play筋现,就可以播放唐础,點(diǎn)擊encryp是進(jìn)行加密保存到存儲(chǔ)器上,再進(jìn)行play是不能播放的矾飞,這時(shí)點(diǎn)擊toJLayer則Activity進(jìn)行跳轉(zhuǎn),使用Jlayer進(jìn)行播放呀邢,注意洒沦,這時(shí)需要消耗一定的時(shí)間,進(jìn)行解碼价淌,開始是沒有聲音的申眼。
廣告
鯉魚日語一個(gè)簡單背日語的APP,歡迎下載,期待Star舒憾。