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中。直到录音停止。

-(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);
    FILE *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);//把文件指针重新置回
        
        char *buff[frameSize] = {0};
        if(length > frameSize)
        {
            fread(buff, 1, frameSize, fp);
            audioData = [NSData dataWithBytes:buff length:frameSize];
            short *recordingData = (short *)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];
            short *recordingData = (short *)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);
}
现在是一个初步的代码,好多东西没有优化,只是实现了功能。相关代码文件之后补上。

阅读更多
文章标签: ios 压缩 mp3
个人分类: IOS开发
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭