美文网首页
OC 微信语音、网易云音乐、APP音频播放互放session

OC 微信语音、网易云音乐、APP音频播放互放session

作者: 喵喵粉 | 来源:发表于2021-05-06 19:08 被阅读0次

    AVAudioPlayerAVAudioSession

    功能点:

    1. 后台播放音频
    2. 音频信息能显示在锁屏界面、控制中心界面
    3. app在后台播放音频
      微信播放语音时app暂停播放音频,
      微信播放语音完成,app继续播放音频,
      微信播放语音未完成,app收不到AVAudioSessionInterruptionNotification通知,AVAudioPlayer不能继续播放
    4. 网易云后台播放音乐
      app播放音频时会暂停网易云后台播放音乐
      app播放音频完成,网易云继续播放音乐
    1. 后台播放音频
      .plist文件UIBackgroundModes下添加audio
      设置sessioncategory,能在锁屏下可以播放的AVAudioSessionCategoryPlaybackAVAudioSessionCategoryPlayAndRecord

    2. 音频信息显示在锁屏界面、控制中心界面

    AppDelegate.m

    #import <MediaPlayer/MediaPlayer.h>
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions {
        
        //https://www.jishudog.com/3906/html
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:CGSizeMake(50, 50) requestHandler:^UIImage * _Nonnull(CGSize size) {
            return [[UIImage imageNamed:@"icon"] originImageScaleToSize:size];
        }];
    
        //锁屏界面显示的图文
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:@{
            MPMediaItemPropertyTitle:@"ciluo",
            MPMediaItemPropertyAlbumTitle:@"好音乐网",
            MPMediaItemPropertyArtwork:artwork
        }];
        
        ...
    }
    
    - (BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    ///接收远端控制事件
    - (void)applicationDidBecomeActive:(UIApplication *)application {
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        
        [self resignFirstResponder];
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
        
        [self becomeFirstResponder];
    }
    

    响应远端控制事件:控制中心、锁屏界面的播放暂停
    ViewController.m

    - (void)remoteControlReceivedWithEvent:(UIEvent *)event {
        if (UIEventTypeRemoteControl == event.type) {
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPlay:
                    [_player play];
                    break;
                case UIEventSubtypeRemoteControlPause:
                    [_player pause];
                    break;
                case UIEventSubtypeRemoteControlStop:
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingBackward:
                    break;
                case UIEventSubtypeRemoteControlEndSeekingBackward:
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingForward:
                    break;
                case UIEventSubtypeRemoteControlEndSeekingForward:
                    break;
    
                default:
                    break;
            }
        }
        NSLog(@"%s %@", __func__, event);
    }
    
    1. app后台播放音频时,微信播放语音
      会收到中断通知
    //3.添加session的通知:中断、线路切换
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interupAction:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChangeAction:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherAppHintAction:) name:AVAudioSessionSilenceSecondaryAudioHintNotification object:[AVAudioSession sharedInstance]];
    
    1. 网易云后台播放音乐
    • app播放音频
    - (void)playAmr {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback
                 withOptions:AVAudioSessionCategoryOptionAllowBluetooth
                       error:nil];
        [session setActive:YES error:nil];
        
        //初始化播放器
        self.player = [[AVAudioPlayer alloc] initWithData:_musicData error:nil];
        self.player.delegate = self;
        [self.player prepareToPlay];
        [self.player play];
    }
    
    • app播放音频完成
    #pragma mark - AVAudioPlayerDelegate
    
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
        NSLog(@"%s", __func__);
        
        [self resignSession];
    }
    
    /* if an error occurs while decoding it will be reported to the delegate. */
    - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error {
        NSLog(@"%s", __func__);
    }
    
    • app停止播放后,继续恢复后台其他APP背景音乐的播放(取消激活当前应用的audio session
    - (void)resignSession {
        [[AVAudioSession sharedInstance] setActive:NO
                                       withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
                                             error:nil];
    }
    
    1. vc.m
    @interface ViewController ()<AVAudioPlayerDelegate>
    @property (nonatomic, strong) AVAudioPlayer *player;
    
    @property (weak, nonatomic) IBOutlet UILabel *lbTip;
    
    @property (nonatomic, strong) NSData *musicData;
    @end
    
    @implementation ViewController
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [_player pause];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self readyData];
        [self audioAuth];
    }
    
    - (void)readyData {
        NSArray *amrs = @[@"ciluo", @"listen"];
        NSString *path = [[NSBundle mainBundle] pathForResource:amrs.firstObject ofType:@"amr"];
        NSData * data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:path]];
        
        NSString *tmpAmrPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AudioTempFile"];
        NSString *tmpWavPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AudioTempConvertFile"];
        
        //把data写入文件中,取名AudioTempFile
        [data writeToFile:tmpAmrPath atomically:YES];
        //将amr格式的数据转成wav
        [EMVoiceConverter amrToWav:tmpAmrPath wavSavePath:tmpWavPath];
        //读取新的wav格式音频文件
        self.musicData = [NSData dataWithContentsOfFile:tmpWavPath];
        
        //第二种转换data
        NSData *wavData = DecodeAMRToWAVE(data);
        NSLog(@"%ld, %ld", self.musicData.length, wavData.length);
    }
    
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event {
        if (UIEventTypeRemoteControl == event.type) {
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPlay:
                    [_player play];
                    break;
                case UIEventSubtypeRemoteControlPause:
                    [_player pause];
                    break;
                case UIEventSubtypeRemoteControlStop:
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingBackward:
                    break;
                case UIEventSubtypeRemoteControlEndSeekingBackward:
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingForward:
                    break;
                case UIEventSubtypeRemoteControlEndSeekingForward:
                    break;
    
                default:
                    break;
            }
        }
        NSLog(@"%s %@", __func__, event);
    }
    
    #pragma mark - auth
    
    - (void)audioAuth {
        //Info.plist must contain an NSMicrophoneUsageDescription key with a string value
        [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
            if (granted) {
                NSLog(@"auth ok");
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self setup];
                });
            } else {
                NSLog(@"auth not ok");
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self notPermissionTip];
                });
            }
        }];
    }
    
    - (void)notPermissionTip {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"麦克风访问权限受限" preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
        [alert addAction:[UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [UIApplication.sharedApplication openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
        }]];
        [self presentViewController:alert animated:YES completion:nil];
    }
    
    - (void)setup {
        
        //3.添加session的通知:中断、线路切换
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interupAction:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChangeAction:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherAppHintAction:) name:AVAudioSessionSilenceSecondaryAudioHintNotification object:[AVAudioSession sharedInstance]];
        
        //4.设置终端的rate、channels、duration等
    
        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop target:self action:@selector(stopPlay)];
        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(playAmr)];
    }
    
    #pragma mark - action
    
    - (void)playAmr {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback
                 withOptions:AVAudioSessionCategoryOptionAllowBluetooth
                       error:nil];
        [session setActive:YES error:nil];
        
        //初始化播放器
        self.player = [[AVAudioPlayer alloc] initWithData:_musicData error:nil];
        self.player.delegate = self;
        [self.player prepareToPlay];
        [self.player play];
    }
    
    - (void)stopPlay {
        [_player stop];
        
        [self resignSession];
    }
    
    ///自己APP停止播放后,继续恢复后台其他APP背景音乐的播放(取消激活当前应用的audio session)
    - (void)resignSession {
        [[AVAudioSession sharedInstance] setActive:NO
                                       withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
                                             error:nil];
    }
    
    ///继续播放⏸️的音乐
    - (void)resumePlay {
            
        NSError *error = nil;
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback
                 withOptions:AVAudioSessionCategoryOptionAllowBluetooth
                       error:&error];
    
        //Activate audio session in current app
        //Deactivate audio session in others' app
        
        //https://baiduhidevios.github.io/2017/03/26/团队原创之AV%20Foundation-AVAudioPlayer/
        [session setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
    //    [session setActive:YES error:nil];
        
        if (error) {
            NSLog(@"setActive:YES error:%@", error.description);
        } else {
            NSLog(@"setActive:YES no error");
        }
        
        [self.player play];
    }
    
    - (void)audioMethod {
        
    }
    
    #pragma mark - NSNotification
    
    - (void)interupAction:(NSNotification *)notification {
        NSLog(@"%s :%@", __func__, notification.userInfo);
        NSDictionary *userInfo = notification.userInfo;
        
        //
        AVAudioSessionInterruptionType type = [userInfo[AVAudioSessionInterruptionTypeKey] integerValue];
        switch (type) {
            case AVAudioSessionInterruptionTypeBegan:
            {
                NSLog(@"开始暂停 ⏸️");
                [self.player pause];
            }
                break;
            case AVAudioSessionInterruptionTypeEnded:
            {
                NSUInteger opts = [userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
                if (AVAudioSessionInterruptionOptionShouldResume == opts) {
                    NSLog(@"app恢复播放");
                    
                    [self resumePlay];
                } else {
                    NSLog(@"app不恢复播放 %lu", opts);
                }
            }
                break;
            default:
                break;
        }
        
        BOOL suspended = userInfo[AVAudioSessionInterruptionWasSuspendedKey];
        if (suspended) {
            NSLog(@"app Suspended");
        } else {
            NSLog(@"app not Suspended");
        }
        
    }
    
    - (void)routeChangeAction:(NSNotification *)notification {
        NSLog(@"%s :%@", __func__, notification.userInfo);
        AVAudioSessionRouteChangeReason type = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] integerValue];
        switch (type) {
            case AVAudioSessionRouteChangeReasonUnknown:
                break;
            case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            {
                AVAudioSessionRouteDescription *desc = notification.userInfo[AVAudioSessionRouteChangePreviousRouteKey];
                NSArray<AVAudioSessionPortDescription *> *inputs = desc.inputs;
            }
                break;
            case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            {
                AVAudioSessionRouteDescription *roteDesc = notification.userInfo[AVAudioSessionRouteChangePreviousRouteKey];
                NSArray<AVAudioSessionPortDescription *> *outputs = roteDesc.outputs;
                
                AVAudioSessionPortDescription *portDesc = outputs.firstObject;
                if ([AVAudioSessionPortHeadphones isEqualToString:portDesc.portType]) {
                    
                } else if ([AVAudioSessionPortHeadphones isEqualToString:portDesc.portType]) {
                    
                }
                
            }
                break;
            default:
                break;
        }
    }
    
    - (void)otherAppHintAction:(NSNotification *)notification {
        NSLog(@"%s :%@", __func__, notification.userInfo);
        AVAudioSessionSilenceSecondaryAudioHintType type = [notification.userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] integerValue];
        switch (type) {
            case AVAudioSessionSilenceSecondaryAudioHintTypeBegin:
                break;
            case AVAudioSessionSilenceSecondaryAudioHintTypeEnd:
                break;
            default:
                break;
        }
    }
    
    #pragma mark - AVAudioPlayerDelegate <NSObject>
    
    /* audioPlayerDidFinishPlaying:successfully: is called when a sound has finished playing. This method is NOT called if the player is stopped due to an interruption. */
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
        NSLog(@"%s", __func__);
        
        [self resignSession];
    }
    
    /* if an error occurs while decoding it will be reported to the delegate. */
    - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error {
        NSLog(@"%s", __func__);
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:OC 微信语音、网易云音乐、APP音频播放互放session

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