美文网首页开发iOS 音视频iOS技术专题
iOS音视频学习3——播放音乐(后台播放+拔耳机控制)

iOS音视频学习3——播放音乐(后台播放+拔耳机控制)

作者: Realank | 来源:发表于2016-03-24 16:29 被阅读2208次

    #######要想开启后台播放,需要两步

    1. 开启后台模式:
    开启后台模式
    1. 设置AVAudioSession的类型为AVAudioSessionCategoryPlayback并且调用setActive::方法启动(激活)会话。
    AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
        [audioSession setActive:YES error:nil];
    

    #######为了支持拔耳机以后,自动暂停播放,需要添加远程控制事件

    前两步是后台播放所必须设置的,第三步主要用于接收远程事件,如果这一步不设置虽让也能够在后台播放,但是无法获得音频控制权(如果在使用当前应用之前使用其他播放器播放音乐的话,此时如果按耳机播放键或者控制中心的播放按钮则会播放前一个应用的音频),并且不能使用耳机进行音频控制。
    第一步操作相信大家都很容易理解,如果应用程序要允许运行到后台必须设置,正常情况下应用如果进入后台会被挂起,通过该设置可以让应用程序继续在后台运行。

    拔耳机监听操作

    增加通知:

    //添加通知,拔出耳机后暂停播放
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
    

    通知方法:

    /**
     *  一旦输出改变则执行此方法
     *
     *  @param notification 输出改变通知对象
     */
    -(void)routeChange:(NSNotification *)notification{
        NSDictionary *dic=notification.userInfo;
        int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];
        //等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示旧输出不可用
        if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
            AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];
            AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
            //原设备为耳机则暂停
            if ([portDescription.portType isEqualToString:@"Headphones"]) {
                [self pause];
            }
        }
        
    //    [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    //        NSLog(@"%@:%@",key,obj);
    //    }];
    }
    
    

    记得要取消监听:

    
    -(void)dealloc{
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
    }
    
    接收远程事件

    在appDelegate中的didFinishLaunchingWithOptions方法中添加:

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    

    来接收远程控制事件

    然后在viewController中,实现代理:

    
    
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event {
        if(event.type==UIEventTypeRemoteControl){
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPlay:
                    [self.audioPlayer play];
                    break;
                case UIEventSubtypeRemoteControlPause:
                    [self.audioPlayer pause];
                    break;
                case UIEventSubtypeRemoteControlStop:
                    [self.audioPlayer stop];
                    break;
                case UIEventSubtypeRemoteControlTogglePlayPause:
                    if (self.audioPlayer.isPlaying) {
                        [self.audioPlayer pause];
                    }else{
                        [self.audioPlayer play];
                    }
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    NSLog(@"Next...");
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    NSLog(@"Previous...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingForward:
                    NSLog(@"Begin seek forward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingForward:
                    NSLog(@"End seek forward...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingBackward:
                    NSLog(@"Begin seek backward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingBackward:
                    NSLog(@"End seek backward...");
                    break;
                default:
                    break;
            }
        }
    }
    

    ViewController类实现如下:

    //
    //  ViewController.m
    //  prac
    //
    //  Created by Realank on 16/3/23.
    //  Copyright © 2016年 realank. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <AVFoundation/AVFoundation.h>
    @interface ViewController () <AVAudioPlayerDelegate>
    
    @property (nonatomic, strong) AVAudioPlayer *audioPlayer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        if ([self.audioPlayer isPlaying]) {
            [self.audioPlayer stop];
            AVAudioSession *audioSession=[AVAudioSession sharedInstance];
            [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
            
        }else{
            [self musicPlayback];
        }
        
    }
    
    - (AVAudioPlayer *)audioPlayer {
        if (!_audioPlayer) {
            NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"liang.mp3" ofType:nil];
            NSURL *url=[NSURL fileURLWithPath:urlStr];
            NSError *error=nil;
            //初始化播放器,注意这里的Url参数只能时文件路径,不支持HTTP Url
            _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
            //设置播放器属性
            _audioPlayer.numberOfLoops=0;//设置为0不循环
            _audioPlayer.delegate=self;
            [_audioPlayer prepareToPlay];//加载音频文件到缓存
            if(error){
                NSLog(@"初始化播放器过程发生错误,错误信息:%@",error.localizedDescription);
                return nil;
            }
            AVAudioSession *audioSession=[AVAudioSession sharedInstance];
            [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
            [audioSession setActive:YES error:nil];
            //添加通知,拔出耳机后暂停播放
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
        }
        
        return _audioPlayer;
    }
    
    
    - (void)musicPlayback {
        if (!self.audioPlayer.isPlaying) {
            [self.audioPlayer play];
        }
        
    }
    
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
        NSLog(@"音乐播放完成...");
    }
    
    /**
     *  一旦输出改变则执行此方法
     *
     *  @param notification 输出改变通知对象
     */
    -(void)routeChange:(NSNotification *)notification{
        NSDictionary *dic=notification.userInfo;
        int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];
        //等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示旧输出不可用
        if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
            AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];
            AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
            //原设备为耳机则暂停
            if ([portDescription.portType isEqualToString:@"Headphones"]) {
                [self.audioPlayer pause];
            }
        }
    
    }
    
    -(void)dealloc{
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
    }
    
    
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event {
        if(event.type==UIEventTypeRemoteControl){
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPlay:
                    [self.audioPlayer play];
                    break;
                case UIEventSubtypeRemoteControlPause:
                    [self.audioPlayer pause];
                    break;
                case UIEventSubtypeRemoteControlStop:
                    [self.audioPlayer stop];
                    break;
                case UIEventSubtypeRemoteControlTogglePlayPause:
                    if (self.audioPlayer.isPlaying) {
                        [self.audioPlayer pause];
                    }else{
                        [self.audioPlayer play];
                    }
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    NSLog(@"Next...");
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    NSLog(@"Previous...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingForward:
                    NSLog(@"Begin seek forward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingForward:
                    NSLog(@"End seek forward...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingBackward:
                    NSLog(@"Begin seek backward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingBackward:
                    NSLog(@"End seek backward...");
                    break;
                default:
                    break;
            }
        }
    }
    
    @end
    
    AVAudioSession有必要进行一下详细的说明:

    在iOS中每个应用都有一个音频会话,这个会话就通过AVAudioSession来表示。
    AVAudioSession同样存在于AVFoundation框架中,它是单例模式设计,通过sharedInstance进行访问。
    在使用Apple设备时大家会发现有些应用只要打开其他音频播放就会终止,而有些应用却可以和其他应用同时播放,在多种音频环境中如何去控制播放的方式就是通过音频会话来完成的。
    下面是音频会话的几种会话模式:

    会话模式

    类似的,如果一个应用已经在播放音频,打开我们的应用之后设置了在后台播放的会话类型,此时其他应用的音频会停止而播放我们的音频,如果希望我们的程序音频播放完之后(关闭或退出到后台之后)能够继续播放其他应用的音频的话则可以调用setActive::方法关闭会话:

     AVAudioSession *audioSession=[AVAudioSession sharedInstance];
    [audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
    

    注意,setActive为NO的时候,一定要配上参数,不然的话没有什么卵用。
    播放结束以后,setActive为NO是一个比较负责任的做法

    相关文章

      网友评论

      • 往事随锋:看到有些app,前台播放视频,切到后台可以继续播放音频,这种如何实现?
      • 阿龍飛:你的可以吗,试一下
      • 阿龍飛:老哥:iOS播放音乐被来电打断怎么恢复播放
        阿龍飛:@阿龍飛 你的可以吗,试一下
        阿龍飛:@Realank 代理是iOS8以前的,现在是有通知,但是不执行恢复
        Realank:@阿龍飛 肯定是有代理的,你找找
      • sea7reen:耳机播出 插上以后 怎么处理

      本文标题:iOS音视频学习3——播放音乐(后台播放+拔耳机控制)

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