最近做的一個項目,項目中有個錄音功能,采用的錄音方法是IOS下的AVAudioRecorder。錄音效果不錯,但是錄制的原生.pcm文件太大,每分鍾大約10M左右。
找了下相關的音頻壓縮方法,用speex的比較多。按照speex的示例文檔折騰了半天,實現了轉碼壓縮。speex壓縮率還蠻高的,但是壓縮之后的pcm文件不能播放,
需要解碼回來,可是按照示例代碼解碼之后的pcm文件依舊不能播放。百思不得解,遂google之,未果。得到只言片語,說是沒有添加wav頭雲雲。個人感覺錄音得
到的pcm文件是有wav頭的,因為錄制好的文件由AVAudioPlayer播放的時候並不受文件后綴名影響。由於不了解speex的編碼原理,也沒時間繼續深究,只好尋求其他辦法。
下了一個比較靠譜的speex轉碼demo,仔細研究了一下。感覺蠻復雜的(對於我這個音頻編解碼門外漢來說),大致是先要跳過pcm里的wav頭,獲取到單純的
pcm文件之后再行編解碼,中間需要分析pcm禎什么的,反正我是弄不來這個,看着頭都大了。
繼續尋找更簡單的解決辦法。。。看到一篇介紹使用lame來轉碼到mp3文件的博客(http://ikinglai.blog.51cto.com/6220785/1228309),感覺比較靠譜,從lame官網下了庫文
件,在命令行里打包了.o文件,(如果不知道怎么打包,試試http://download.csdn.net/download/ixfly/4440512)導入到工程里,按照demo試了下,果然好使!編碼實現非常簡
單,得到的mp3文件大小大概是pcm文件的1/10左右。
接下來就是實現邊錄邊轉碼了。上面博客里提供的demo實際上就是采用了邊錄邊轉碼的實現。但是有兩個問題,一個問題是編譯通不過(把工程里lame相關的庫文件都移除,
導入剛才打包的.o文件,在.o文件同目錄下要有lame.h和lame.c文件,但是不要導入這兩個文件,否則編譯錯誤,具體原因還得再查一下)。另一個問題是這個demo的錄音
功能不是基於AVAudioRecorder實現的,用的是AVAudioQueue,基於數據緩沖實現的,可以實時獲取錄音的buffer數據,做邊錄邊轉碼很方便。而AVAudioRecorder則不能
實時獲得錄音數據。
於是只能是對AVAudioRecorder生成的pcm文件讀取來實現實時轉碼了。主要的思路是:錄音開始后開啟轉碼線程,轉碼線程讀取pcm文件,設定每次轉碼的frame大小,當
讀入的文件小於frame大小,就等待,當文件大於這個值時,則讀取frame大小的文件,並轉碼,添加至data中。直到錄音停止。
- -(void)main
- {
- //mp3壓縮參數
- lame = lame_init();
- lame_set_num_channels(lame, 2);
- lame_set_in_samplerate(lame, 88200);
- lame_set_brate(lame, 88);
- lame_set_mode(lame, 1);
- lame_set_quality(lame, 2);
- lame_init_params(lame);
- //這種方式初始化的NSData不需要手動釋放
- NSMutableData *mp3Data = [[NSMutableData alloc] init];
- NSLog(@"record path: %@",_filePath);
- NSLog(@"out path: %@", _outFile);
- FILEFILE *fp;
- fp = fopen([_filePath cStringUsingEncoding:NSASCIIStringEncoding], "rb");
- long curpos;
- //if(fp) 這句得補上,但是還不確定是否有問題
- while (true)
- {
- //需要手動釋放
- NSData *audioData = nil;
- curpos = ftell(fp);
- long startPos = ftell(fp);//文件當前讀到的位置
- fseek(fp, 0, SEEK_END);
- long endPos = ftell(fp);//文件末尾位置
- long length = endPos - startPos;//剩下未讀入文件長度
- fseek(fp, curpos, SEEK_SET);//把文件指針重新置回
- charchar *buff[frameSize] = {0};
- if(length > frameSize)
- {
- fread(buff, 1, frameSize, fp);
- audioData = [NSData dataWithBytes:buff length:frameSize];
- shortshort *recordingData = (shortshort *)audioData.bytes;
- int pcmLen = audioData.length;
- int nsamples = pcmLen / 2;
- unsigned char buffer[pcmLen];
- //執行encode
- int recvLen = lame_encode_buffer(lame, recordingData, recordingData, nsamples, buffer, pcmLen);
- [mp3Data appendBytes:buffer length:recvLen];
- }
- else
- {
- if (_setToStopped)
- {
- fread(buff, 1, length, fp);
- audioData = [NSData dataWithBytes:buff length:length];
- shortshort *recordingData = (shortshort *)audioData.bytes;
- int pcmLen = audioData.length;
- int nsamples = pcmLen / 2;
- unsigned char buffer[pcmLen];
- //執行encode
- int recvLen = lame_encode_buffer(lame, recordingData, recordingData, nsamples, buffer, pcmLen);
- [mp3Data appendBytes:buffer length:recvLen];
- break;
- }
- else
- {
- [NSThread sleepForTimeInterval:0.05];
- }
- }
- }
- //寫入文件
- [mp3Data writeToFile:_outFile atomically:YES];
- //釋放lame
- lame_close(lame);
- }
現在是一個初步的代碼,好多東西沒有優化,只是實現了功能。相關代碼文件之后補上。