美文网首页
iOS 录音 音频 视频 控制中心

iOS 录音 音频 视频 控制中心

作者: S大偉 | 来源:发表于2018-08-20 17:38 被阅读101次

    录音

    最近项目中需要录音功能,为此在写了一个录音的小模块。

    首先需要添加AVFoundation.framework
    lame.h 帮助链接
    下面直接上代码

    #import <Foundation/Foundation.h>
    #import <AVFoundation/AVFoundation.h>
    #import <AudioToolbox/AudioToolbox.h>
    
    typedef void(^AudioModelBlock)(NSInteger duration);
    
    @protocol AudioModelDelegate <NSObject>
    - (void)recordResult:(NSString *)path error:(NSError *)error;
    @end
    
    @interface AudioModel : NSObject <AVAudioRecorderDelegate, AVAudioPlayerDelegate> {
        AVAudioRecorder *_audioRecorder;
        AVAudioPlayer   *_audioPlayer;
        NSTimer *_timer;
        
        NSString *_name;
    }
    
    @property (nonatomic, assign) NSInteger duration;
    @property (nonatomic, copy)   AudioModelBlock block;
    @property (nonatomic, assign) id <AudioModelDelegate> delegate;
    
    #pragma mark - record
    //开始录音 或 恢复录音
    - (void)record;
    
    //暂停录音
    - (void)pauseRecotd;
    
    //停止录音
    - (void)stopRecord;
    
    
    #pragma mark - play
    //播放录音
    - (void)playRecord:(NSString *)path;
    
    #pragma mark - file
    //移除录音
    - (void)removeFile;
    
    #import "AudioModel.h"
    #import "lame.h"
    
    @implementation AudioModel
    - (instancetype)init {
        self = [super init];
        if (self) {
        }
        return self;
    }
    
    #pragma mark - setting
    //获取录音文件设置
    - (NSDictionary *)getAudioSetting {
        NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
        //设置录音格式
        [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
        //设置录音采样率
        [dicM setObject:@(20000) forKey:AVSampleRateKey];
        //设置通道,这里采用单声通道
        [dicM setObject:@(2) forKey:AVNumberOfChannelsKey];
        //    //每个采样点位数,8,16,24,32
        //    [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
        //    [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
        //音频编码质量
        [dicM setObject:@(AVAudioQualityMedium) forKey:AVEncoderAudioQualityKey];
        //其他
        return dicM;
    }
    
    #pragma mark - path
    - (NSString *)getBasePath {
        NSString *urlStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        
        urlStr = [urlStr stringByAppendingPathComponent:[YLSaveObjectToLocal loadObjectForKey:@"userMid"]];
        urlStr = [urlStr stringByAppendingPathComponent:[YLCoreTool getNowWithFormatter:@"yyyyMMdd"]];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if ([fileManager fileExistsAtPath:urlStr]) {
        }
        else {
            [fileManager createDirectoryAtPath:urlStr withIntermediateDirectories:YES attributes:nil error:nil];
        }
        
        return urlStr;
    }
    
    //获取录音文件保存路径
    - (NSString *)getSavePath {
        NSString *urlStr = [self getBasePath];
        urlStr = [urlStr stringByAppendingPathComponent:_name];
        urlStr = [urlStr stringByAppendingString:@".caf"];
    
        return urlStr;
    }
    
    - (NSString *)getMp3FilePath {
        NSString *urlStr = [self getBasePath];
    
        urlStr = [urlStr stringByAppendingPathComponent:_name];
        urlStr = [urlStr stringByAppendingString:@".mp3"];
        
        return urlStr;
    }
    
    - (void)removeFile {
        NSString *urlStr = [self getBasePath];
        urlStr = [urlStr stringByAppendingPathComponent:_name];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSArray *arr = @[@".caf",@".mp3"];
        for (NSString *type in arr) {
            NSString *path = [urlStr stringByAppendingString:type];
            if ([fileManager fileExistsAtPath:path]) {
                [fileManager removeItemAtPath:path error:nil];
            }
        }
    }
    
    #pragma mark - action
    //开始录音 或 恢复录音
    - (void)record {
        _name = [YLCoreTool getNowWithFormatter:@"yyyyMMdd_HHmmss"];
        
        //创建录音文件保存路径
        NSURL *url = [NSURL URLWithString: [self getSavePath]];
        //创建录音格式设置
        NSDictionary *setting = [self getAudioSetting];
        //设置音频会话 不然不录制
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        //设置为播放和录音状态,以便可以在录制完之后播放录音
        [audioSession setCategory:AVAudioSessionCategoryRecord error:nil];
        [audioSession setActive:YES error:nil];
    
        //创建录音机
        NSError *error = nil;
        _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
        _audioRecorder.delegate = self;
        if (error) {
            NSLog(@"creat audioRecorder error:%@", error.localizedDescription);
        } else {
            if (![_audioRecorder isRecording]) {
                if ([_audioRecorder prepareToRecord]) {
                    if ([_audioRecorder record]) {
                        self.timer.fireDate = [NSDate distantPast];
                    }
                }
            }
        }
    }
    
    //暂停录音
    - (void)pauseRecotd {
        if ([_audioRecorder isRecording]) {
            [_audioRecorder pause];
            self.timer.fireDate = [NSDate distantFuture];
        }
    }
    
    //停止录音
    - (void)stopRecord {
        [_audioRecorder stop];
        self.timer.fireDate = [NSDate distantFuture];
    }
    
    //计时器计时
    - (void)handleTimerAction {
        self.duration = _audioRecorder.currentTime;
    }
    
    //播放录音
    - (void)playRecord:(NSString *)path {
        //播放声音小时可以设置
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory :AVAudioSessionCategoryPlayback error:nil];
        [audioSession setActive:YES error:nil];
        
        NSURL *url = [NSURL URLWithString: path];
        NSError *error = nil;
        _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
        _audioPlayer.numberOfLoops = 0;
        _audioPlayer.delegate = self;
        [_audioPlayer prepareToPlay];
        if (error) {
            NSLog(@"creat audioPlayer error: %@", error.localizedDescription);
        }
        else {
            [_audioPlayer play];
        }
    }
    
    - (void)playAVItem {
        
    }
    
    #pragma mark - AVAudioRecorderDelegate
    - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error {
        if (error) {
            NSLog(@"%@",error.localizedDescription);
        }
    }
    
    
    - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
        NSLog(@"录制完成");
        [self convertToMp3];
        _audioRecorder = nil;
    }
    
    
    #pragma mark - AVAudioPlayerDelegate
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
        _audioPlayer = nil;
    }
    
    #pragma mark - tool
    - (void)convertToMp3
    {
        NSString *filePath = [self getMp3FilePath];
        
        @try {
            int read,write;
            //只读方式打开被转换音频文件
            FILE *pcm = fopen([[self getSavePath] cStringUsingEncoding:1], "rb");
            fseek(pcm, 4 * 1024, SEEK_CUR);//删除头,否则在前一秒钟会有杂音
            //只写方式打开生成的MP3文件
            FILE *mp3 = fopen([filePath cStringUsingEncoding:1], "wb");
            
            const int PCM_SIZE = 8192;
            const int MP3_SIZE = 8192;
            short int pcm_buffer[PCM_SIZE * 2];
            unsigned char mp3_buffer[MP3_SIZE];
            //这里要注意,lame的配置要跟AVAudioRecorder的配置一致,否则会造成转换不成功
            lame_t lame = lame_init();
            lame_set_VBR(lame, vbr_default);
            
            lame_set_num_channels(lame,2);//默认为2双通道
            lame_set_in_samplerate(lame, 20000);//11025.0
            lame_set_brate(lame,8);
            lame_set_mode(lame,3);
            lame_set_quality(lame,5); /* 2=high 5 = medium 7=low 音质*/
            
            lame_init_params(lame);
            
            do {
                
                //以二进制形式读取文件中的数据
                read = (int)fread(pcm_buffer, 2 * sizeof(short int), 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);
                
                //二进制形式写数据到文件中  mp3_buffer:数据输出到文件的缓冲区首地址  write:一个数据块的字节数  1:指定一次输出数据块的个数   mp3:文件指针
                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 ([self.delegate respondsToSelector:@selector(recordResult:error:)]) {
                [self.delegate recordResult:[self getMp3FilePath] error:nil];
            }
        }
    }
    
    #pragma mark - set and get
    - (void)setDuration:(NSInteger)duration {
        _duration = duration;
        if (self.block) {
            self.block(_duration);
        }
    }
    
    -(NSTimer *)timer{
        if (!_timer) {
            _timer=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(handleTimerAction) userInfo:nil repeats:YES];
        }
        return _timer;
    }
    
    /**
     *  录音声波状态设置
     */
    //        _audioRecorder.meteringEnabled = YES;//如果需要监控声波则必须设置为YES
    
    
    //-(void)audioPowerChange{
    //    [self.audioRecorder updateMeters];//更新测量值
    //    float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
    //    CGFloat progress=(1.0/160.0)*(power+160.0);
    //    [self.audioPower setProgress:progress];
    //}
    
    
    // 获取音频时长
    //AVURLAsset* audioAsset =[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:self.savePath] options:nil];
    //CMTime audioDuration = audioAsset.duration;
    //float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
    

    音频和视频

    音频和视频播放使用的ZFPlayer,有兴趣的话可以自己研究下

    控制中心 后台播放

    需要打开相应的Background Models


    屏幕快照 2018-08-20 下午5.23.51.png

    AppDelegate 中

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      //处理中断事件的通知 播放
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
        //开启后台处理多媒体事件
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        return YES;
    }
    
    //处理中断事件
    -(void)handleInterreption:(NSNotification *)sender
    {
        ZFPlayerView *player = [ZFPlayerView sharedPlayerView];
        if(player.state == ZFPlayerStatePause)
        {
           [player play];
        }
        else
        {
            [player pause];
        }
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application { 
        //ZFPlayerView会监听UIApplicationWillResignActiveNotification,自动关闭后台播放
        //这里是重新打开
        [self handleInterreption:nil];
    }
    
    //控制中心操作
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event {
        if (event.type == UIEventTypeRemoteControl) {
            NSInteger order = -1;
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPause:
                    order = UIEventSubtypeRemoteControlPause;
                    break;
                case UIEventSubtypeRemoteControlPlay:
                    order = UIEventSubtypeRemoteControlPlay;
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    order = UIEventSubtypeRemoteControlNextTrack;
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    order = UIEventSubtypeRemoteControlPreviousTrack;
                    break;
                case UIEventSubtypeRemoteControlTogglePlayPause:
                    order = UIEventSubtypeRemoteControlTogglePlayPause;
                    break;
    
                default:
                    order = -1;
                    break;
            }
            NSDictionary *dict = @{@"order":@(order)};
            [[NSNotificationCenter defaultCenter] postNotificationName:@"kAppDidReceiveRemoteControlNotification" object:nil userInfo:dict];
        }
    }
    

    控制文件中,如ViewController等
    这里需要添加MediaPlayer.framework

    #import <MediaPlayer/MediaPlayer.h>
    //监听控制中心事件
    - (void)viewDidLoad {
        [super viewDidLoad];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listeningRemoteControl:) name:@"kAppDidReceiveRemoteControlNotification" object:nil];
    }
    
    //model 为自己定义
    //播放时调用此函数更新控制中心
    - (void)setNowPlayingInfo:(Model *)model {
        NSMutableDictionary *songDict = [NSMutableDictionary dictionary];
        //歌名
        [songDict setObject:model.title forKey:MPMediaItemPropertyTitle];
        //歌手名
        [songDict setObject:AppName forKey:MPMediaItemPropertyArtist];
        //总时长
        [songDict setObject:[NSNumber numberWithInt:[model.time intValue] * 60] forKey:MPMediaItemPropertyPlaybackDuration];
        //图片
        MPMediaItemArtwork *imageItem = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"%@Logo", PREFIX_NAME]]];
        [songDict setObject:imageItem forKey:MPMediaItemPropertyArtwork];
        
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songDict];
    }
    
    //控制中心事件操作
    -(void)listeningRemoteControl:(NSNotification *)sender
    {
        NSDictionary *dict=sender.userInfo;
        NSInteger order = [[dict objectForKey:@"order"] integerValue];
        switch (order) {
                //暂停
            case UIEventSubtypeRemoteControlPause:
                [self.playerView pause];
                break;
                //播放
            case UIEventSubtypeRemoteControlPlay:
                [self.playerView play];
                break;
                //暂停播放切换
            case UIEventSubtypeRemoteControlTogglePlayPause:
            {
                if(self.playerView.state == ZFPlayerStatePause)
                {
                    [self.playerView play];
                }
                else
                {
                    [self.playerView pause];
                }
                break;
            }
                //下一首
            case UIEventSubtypeRemoteControlNextTrack:
            {
                NSInteger index = [self.models indexOfObject:self.currentModel];
                index = index + 1;
                if (index < self.models.count) {
                    //这里是用于等待界面渲染
                    [NSTimer scheduledTimerWithTimeInterval:0.02 repeats:NO block:^(NSTimer * timer) {
                        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
                        [self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
                    }];
                }
                break;
            }
                //上一首
            case UIEventSubtypeRemoteControlPreviousTrack:
            {
                NSInteger index = [self.models indexOfObject:self.currentModel];
                index = index - 1;
                if (index >= 0) {
                    if (index < self.models.count) {
                        [NSTimer scheduledTimerWithTimeInterval:0.02 repeats:NO block:^(NSTimer * timer) {
                            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
                            [self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
                        }];
                    }
                }
                
                break;
            }
            default:
                break;
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS 录音 音频 视频 控制中心

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