美文网首页iOS开发之笔记摘录
录音并将caf转换格式为mp3之iOS笔记摘录

录音并将caf转换格式为mp3之iOS笔记摘录

作者: 平安喜乐698 | 来源:发表于2020-04-02 18:42 被阅读0次
    目录
        
      
    

    长风破浪会有时,直挂云帆济沧海。

    项目中遇到过需要录制一段声音,然后上传到7牛(格式需要是mp3格式)。

    格外注意一下:AVAudioRecorder的配置参数和lame_set配置参数。
    配置不正确会导致转换的mp3文件长度不正确。
    

    前言

    音频-文件格式(后缀)
    
    1. WAV:
        音质最好(对应PCM编码)
        适用:多媒体开发,保存音乐和音效素材。
    
    2. MP3:
        音质好,压缩比比较高,被广泛支持(iOS、Android都支持)。
        适用:比较高要求的音乐欣赏。
    
    3. caf:
        适用: iOS中所有的编码格式。
    
    音频-编码格式
    
    1. PCM
        脉冲编码调制,是一种非压缩音频数字化技术(未压缩的原音重现)。
        数字模式下,音频的初始化信号是PCM。
    
    2. MP3
    
    3. AAC
        advanced audio coding高级音频编码,被设计来取代MPC格式。
    
    4. HE-AAC
        HE-AAC是AAC的一个超集,专门为低比特率所优化的一种音频编码格式。
    
    5. AMR
        Adaptive Multi-Rate,另一个专门为低比特率所优化的一种音频编码格式。
    
    2.6 ALAC
        Apple Lossless,无损压缩的音频编码方式。
    
    2.7 IMA4
        在16-bit音频文件下按照4:1的压缩比来进行压缩的格式。
    

    1.

    1. 录音工具类

    FQVideoRecordTool.h

    @interface FQVideoRecordTool : NSObject
    // 必须设置(用于没权限时弹窗)
    @property (nonatomic,weak) UIViewController *currentVC;
    @property (nonatomic,copy) NSString *videoRecordPath;
    // 最大录制/播放时间
    @property (nonatomic,assign) int maxVideoRecordTime;
    // 最小录制/播放时间
    @property (nonatomic,assign) int minVideoRecordTime;
    // 录制时间
    @property (nonatomic,assign) int videoRecordTime;
    
    +(instancetype)fq_shared;
    
    // 录音
    -(void)fq_recordVideo;
    // 暂停录音
    -(void)fq_pauseRecord;
    // 停止录音
    -(void)fq_stopRecord;
    // 取消/清除 录音
    -(void)fq_cancelOrCleanRecord;
    
    // 播放录音
    -(void)fq_playVideo;
    // 暂停播放录音
    -(void)fq_pauseVideo;
    // 继续播放录音
    -(void)fq_replayVideo;
    @end
    

    FQVideoRecordTool.m

    #import "FQVideoRecordTool.h"
    
    @interface FQVideoRecordTool()
    @property (nonatomic,strong) AVAudioRecorder *videoRecorder;
    @property (nonatomic,strong) AVAudioPlayer *videoplayer;
    @end
    
    
    @implementation FQVideoRecordTool
    
    static FQVideoRecordTool *_shared;
    +(instancetype)fq_shared{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if(!_shared){
                _shared=[FQVideoRecordTool new];
            }
        });
        return _shared;
    }
    
    
    #pragma mark 录音
    // 录音
    -(void)fq_recordVideo{
        AVAudioSessionRecordPermission permission = AVAudioSession.sharedInstance.recordPermission;
        //在此添加新的判定 undetermined,否则新安装后的第一次询问会出错。新安装后的第一次询问为 undetermined,而非 denied。
        if (permission == AVAudioSessionRecordPermissionDenied || permission == AVAudioSessionRecordPermissionUndetermined) {
            [AVAudioSession.sharedInstance requestRecordPermission:^(BOOL granted) {
                if (!granted) {
                    UIAlertController *ac = [UIAlertController alertControllerWithTitle:@"无法访问麦克风" message:@"开启麦克风权限才能发送语音消息" preferredStyle:UIAlertControllerStyleAlert];
                    [ac addAction:[UIAlertAction actionWithTitle:@"以后再说" style:UIAlertActionStyleCancel handler:nil]];
                    [ac addAction:[UIAlertAction actionWithTitle:@"去开启" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                        UIApplication *app = [UIApplication sharedApplication];
                        NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                        if ([app canOpenURL:settingsURL]) {
                            [app openURL:settingsURL];
                        }
                    }]];
                    [self.currentVC presentViewController:ac animated:YES completion:nil];
                }
            }];
            return;
        }
        if(permission == AVAudioSessionRecordPermissionGranted){
            [self fq_startRecord];
        }
    }
    -(void)fq_startRecord{
        if([[NSFileManager defaultManager] fileExistsAtPath:self.videoRecordPath]){
    //        [self.videoRecorder record];
            [self fq_cancelOrCleanRecord];
        }
        
        //
        AVAudioSession *session = [AVAudioSession sharedInstance];
        NSError *error = nil;
        [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
        [session setActive:YES error:&error];
        
        if(error){
            [SQGToast showWithText:@"录制出错"];
            NSLog(@"%@",error.userInfo);
        }else{
            [self.videoRecorder prepareToRecord];
            [self.videoRecorder record];
            [self.videoRecorder updateMeters];
        }
    }
    // 暂停录音
    -(void)fq_pauseRecord{   //
        if([self.videoRecorder isRecording]){
            [self.videoRecorder pause];
        }
    }
    // 停止录音
    -(void)fq_stopRecord{   //
        if([self.videoRecorder isRecording]){
            [self.videoRecorder stop];
        }
    }
    // 取消/清除 录音
    -(void)fq_cancelOrCleanRecord{
        //
        if([self.videoRecorder isRecording]){
            [self.videoRecorder stop];
        }
        self.videoRecorder=nil;
        //
        if([[NSFileManager defaultManager] fileExistsAtPath:self.videoRecordPath]){
            [[NSFileManager defaultManager] removeItemAtPath:self.videoRecordPath error:nil];
        }
        
        //
        self.videoplayer=nil;
    }
    
    #pragma mark 播放录音
    // 播放录音
    -(void)fq_playVideo{
        if([self.videoRecorder isRecording]){
            [self fq_stopRecord];
        }
        if ([self.videoplayer isPlaying])return;
         
        //
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];
        [self.videoplayer play];
    }
    // 暂停播放录音
    -(void)fq_pauseVideo{
        if ([self.videoplayer isPlaying]){
            [self.videoplayer pause];
        }
    }
    // 继续播放录音
    -(void)fq_replayVideo{
        if ([self.videoplayer isPlaying]){
            return;
        }
        
        [self.videoplayer play];
    }
    // 没权限
    -(BOOL)fq_NoPermission{
        return AVAudioSession.sharedInstance.recordPermission == AVAudioSessionRecordPermissionDenied;
    }
    
    
    
    #pragma mark GET
    -(AVAudioRecorder *)videoRecorder{
        if(!_videoRecorder){
            NSDictionary *recordSetting=[[NSDictionary alloc] initWithObjectsAndKeys:
                                           // 采样率  8000/11025/22050/44100/96000(影响音频的质量)
                                           [NSNumber numberWithFloat: 8000.0],AVSampleRateKey,
                                           // 音频格式()
                                           [NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
                                           // 采样位数  8、16、24、32 默认为16
                                           [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                                           // 音频通道数 1 或 2
                                           [NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
                                           //
                                           [NSNumber numberWithInt: 16],AVLinearPCMBitDepthKey,
                                           // 录音质量
                                           [NSNumber numberWithInt:AVAudioQualityHigh],AVEncoderAudioQualityKey,
                                           nil];
            NSError *error;
            _videoRecorder=[[AVAudioRecorder alloc]initWithURL:[NSURL URLWithString:self.videoRecordPath]settings:recordSetting error:&error];
            [_videoRecorder setMeteringEnabled:true];
        }
        return _videoRecorder;
    }
    -(NSString *)videoRecordPath{
        if(!_videoRecordPath){
            _videoRecordPath=[NSString stringWithFormat:@"%@/VideoRecord.caf",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]];
        }
        return _videoRecordPath;
        
    }
    -(AVAudioPlayer *)videoplayer{
        if(!_videoplayer){
            _videoplayer=[[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL URLWithString:self.videoRecordPath] error:nil];
        }
        return _videoplayer;
    }
    -(int)maxVideoRecordTime{
        if(!_maxVideoRecordTime){
            _maxVideoRecordTime=20;
        }
        return _maxVideoRecordTime;
    }
    -(int)minVideoRecordTime{
        if(!_minVideoRecordTime){
            _minVideoRecordTime=5;
        }
        return _minVideoRecordTime;
    }
    
    
    
    -(void)dealloc{
        //
        [self fq_cancelOrCleanRecord];
    }
    @end
    
    1. 打包lame静态库(用来将caf转换为mp3格式)

    1. 下载lame
    2. 下载build脚本

    1. 创建一个新文件夹(存放1和2下载的内容)
    2. 编译(在终端输入./build-lame.sh)
    3. 将fat-lame中编译好的.a和.h文件拖入项目
    文件目录1 文件目录2
    1. lame使用工具类

    FQLibMp3Tool.h

    @interface FQLibMp3Tool : NSObject
    
    +(NSString *)FQTransferToMP3:(NSString *)sourcePath isDeleteSourchFile:(BOOL)isDelete;
    @end
    

    FQLibMp3Tool.m

    #import "FQLibMp3Tool.h"
    #import "lame.h"
    
    @implementation FQLibMp3Tool
    
    +(NSString *)FQTransferToMP3: (NSString *)sourcePath isDeleteSourchFile:(BOOL)isDelete {
        
        // 输入路径
        NSString *inPath = sourcePath;
        // 判断输入路径是否存在
        NSFileManager *fm = [NSFileManager defaultManager];
        if (![fm fileExistsAtPath:sourcePath]) {
            NSLog(@"文件不存在");
        }
        // 输出路径
        NSString *outPath = [[sourcePath stringByDeletingPathExtension] stringByAppendingString:@".mp3"];
        @try {
            int read, write;
            
            FILE *pcm = fopen([inPath cStringUsingEncoding:1], "rb");  //source 被转换的音频文件位置
            fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
            FILE *mp3 = fopen([outPath cStringUsingEncoding:1], "wb+");  //output 输出生成的Mp3文件位置
            
            const int PCM_SIZE = 8192;
            const int MP3_SIZE = 8192;
            short int pcm_buffer[PCM_SIZE*2];
            unsigned char mp3_buffer[MP3_SIZE];
            
            // 配置不正确可能造成mp3转换时间长短出现错误。
            lame_t lame = lame_init();
            lame_set_num_channels(lame,2);  // 设置1为单通道,默认为2双通道
            lame_set_in_samplerate(lame, 8000.0);
    //        lame_set_VBR(lame, vbr_default);
            lame_set_brate(lame, 16);
            lame_set_mode(lame, 3);
            lame_set_quality(lame, 2); /* 2=high 5 = medium 7=low 音质*/
            lame_init_params(lame);
            
            do {
                size_t size = (size_t)(2 * sizeof(short int));
                read = fread(pcm_buffer, size, PCM_SIZE, pcm);
                if (read == 0)
                    write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
                else
                    write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
                
                fwrite(mp3_buffer, write, 1, mp3);
                
            } while (read != 0);
            
            lame_close(lame);
            fclose(mp3);
            fclose(pcm);
        }
        @catch (NSException *exception) {
            NSLog(@"%@",[exception description]);
        }
        @finally {
            NSLog(@"MP3生成成功:");
            if (isDelete) {
                
                NSError *error;
                [fm removeItemAtPath:sourcePath error:&error];
                if (error == nil) {
                    NSLog(@"删除源文件成功");
                }
            }
            return outPath;
        }
    }
    
    @end
    
    1. 转换为mp3格式
    // var/..../test.mp3
    NSString *mp3Path=[FQLibMp3Tool FQTransferToMP3:@"var/..../test.caf" isDeleteSourchFile:false];
    

    相关文章

      网友评论

        本文标题:录音并将caf转换格式为mp3之iOS笔记摘录

        本文链接:https://www.haomeiwen.com/subject/hyuxphtx.html