IOS 實現錄音PCM轉MP3格式(邊錄音邊轉碼)


最近做的一個項目,項目中有個錄音功能,采用的錄音方法是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中。直到錄音停止。

 
  1. -(void)main  
  2. {  
  3.     //mp3壓縮參數  
  4.     lame = lame_init();  
  5.     lame_set_num_channels(lame, 2);  
  6.     lame_set_in_samplerate(lame, 88200);  
  7.     lame_set_brate(lame, 88);  
  8.     lame_set_mode(lame, 1);  
  9.     lame_set_quality(lame, 2);  
  10.     lame_init_params(lame);  
  11.       
  12.     //這種方式初始化的NSData不需要手動釋放  
  13.     NSMutableData *mp3Data = [[NSMutableData alloc] init];  
  14.       
  15.     NSLog(@"record path: %@",_filePath);  
  16.     NSLog(@"out path: %@", _outFile);  
  17.     FILEFILE *fp;  
  18.     fp = fopen([_filePath cStringUsingEncoding:NSASCIIStringEncoding], "rb");  
  19.           
  20.     long curpos;  
  21.     //if(fp) 這句得補上,但是還不確定是否有問題  
  22.     while (true)  
  23.     {  
  24.         //需要手動釋放  
  25.         NSData *audioData = nil;  
  26.          
  27.         curpos = ftell(fp);  
  28.         long startPos = ftell(fp);//文件當前讀到的位置  
  29.         fseek(fp, 0, SEEK_END);  
  30.         long endPos = ftell(fp);//文件末尾位置  
  31.         long length = endPos - startPos;//剩下未讀入文件長度  
  32.         fseek(fp, curpos, SEEK_SET);//把文件指針重新置回  
  33.           
  34.         charchar *buff[frameSize] = {0};  
  35.         if(length > frameSize)  
  36.         {  
  37.             fread(buff, 1, frameSize, fp);  
  38.             audioData = [NSData dataWithBytes:buff length:frameSize];  
  39.             shortshort *recordingData = (shortshort *)audioData.bytes;  
  40.             int pcmLen = audioData.length;  
  41.             int nsamples = pcmLen / 2;  
  42.               
  43.             unsigned char buffer[pcmLen];  
  44.               
  45.             //執行encode  
  46.             int recvLen = lame_encode_buffer(lame, recordingData, recordingData, nsamples, buffer, pcmLen);  
  47.             [mp3Data appendBytes:buffer length:recvLen];  
  48.       }  
  49.       else  
  50.       {  
  51.         if (_setToStopped)  
  52.         {  
  53.             fread(buff, 1, length, fp);  
  54.             audioData = [NSData dataWithBytes:buff length:length];  
  55.             shortshort *recordingData = (shortshort *)audioData.bytes;  
  56.             int pcmLen = audioData.length;  
  57.             int nsamples = pcmLen / 2;  
  58.               
  59.             unsigned char buffer[pcmLen];  
  60.               
  61.             //執行encode  
  62.             int recvLen = lame_encode_buffer(lame, recordingData, recordingData, nsamples, buffer, pcmLen);  
  63.             [mp3Data appendBytes:buffer length:recvLen];  
  64.             break;  
  65.         }  
  66.         else  
  67.         {  
  68.           [NSThread sleepForTimeInterval:0.05];  
  69.         }  
  70.       }  
  71.     }  
  72.     
  73.     //寫入文件  
  74.     [mp3Data writeToFile:_outFile atomically:YES];  
  75.       
  76.     //釋放lame  
  77.     lame_close(lame);  
  78. }  

現在是一個初步的代碼,好多東西沒有優化,只是實現了功能。相關代碼文件之后補上。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM