1. 程式人生 > >AVAudioPlayer播放並實現了後臺播放和遠端控制

AVAudioPlayer播放並實現了後臺播放和遠端控制

//  ViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h> 

@class AVAudioPlayer;

@interface PlayViewController : UIViewController<AVAudioPlayerDelegate>

@property(nonatomic, strong) AVAudioPlayer *player;

+ (PlayViewController *)defaultPlayVC; //將該播放頁面定義成單例

@end
//  ViewController.m
#import "PlayViewController.h"

@interface PlayViewController ()

@property (retain, nonatomic) IBOutlet UIButton *suspendBtn;  //暫停按鈕
@property (retain, nonatomic) IBOutlet UIButton *formerBtn;   //上一首按鈕
@property (retain, nonatomic) IBOutlet UIButton *nextBtn; //下一首按鈕
@property (retain, nonatomic) IBOutlet UISlider *slider;  //滑塊
@property (retain, nonatomic) NSArray *musicUrlArray; //音樂網址陣列
@property (retain, nonatomic) NSArray *musicAllTimeArray; //音樂時間陣列
@property (retain, nonatomic) NSTimer *timer;  //計時器
@property (assign, nonatomic) int currentIndex; //當前音訊下標

@end

@implementation PlayViewController

#pragma mark 單例初始化方法
+ (PlayViewController *)defaultPlayVC {
    static PlayViewController *playVC = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        playVC = [[PlayViewController alloc] init];
    });
    return playVC;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.currentIndex = 0;
    self.musicUrlArray = [NSArray arrayWithObjects:@"http://fdfs.xmcdn.com/group10/M08/D4/2E/wKgDaVZ9EYaQ-j48AFIOaAXL-wM019.mp3",
                          @"http://fdfs.xmcdn.com/group13/M09/D4/17/wKgDXVZ6fR2wR1E8AFuO_VClRZA066.mp3",
                          @"http://fdfs.xmcdn.com/group16/M0A/D5/16/wKgDbFZ89SqyFGWGAHoEKd7yPsw709.mp3",
                          @"http://fdfs.xmcdn.com/group8/M01/D4/34/wKgDYFZ6y1nR6QpZAEDVy7rUTTc247.mp3",
                          @"http://fdfs.xmcdn.com/group9/M00/D4/EF/wKgDZlZ7aLGCQEEDAHMsQsurq_Y170.mp3",
                          @"http://fdfs.xmcdn.com/group12/M07/D4/C4/wKgDW1Z70rWxoDA7ABAp1lwS5nI990.mp3", nil];
    self.musicAllTimeArray = [NSArray arrayWithObjects:@(1344.36), @(1500.04), @(1999.06), @(1062.22), @(1886.96), @(264.76), nil];
    
    //滑塊
    self.slider.minimumValue = 0;
    [self.slider addTarget:self action:@selector(changValue:) forControlEvents:UIControlEventValueChanged];  //新增滑動事件
    [self.slider setThumbImage:[UIImage imageNamed:@"playProcessDot_n"]forState:UIControlStateNormal];

    //播放音訊之前先要設定AVAudioSession模式 是它可以後臺播放
    //並且要在plist檔案中 新增required background modes這個key項,並選擇"App plays audio or streams audio/video using AirPlay"這個value項。
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    
    [self playerCurrentMusic];
}

-(void)playerCurrentMusic {

    //建立序列佇列 (放入執行緒中,免得頁面假死)
    dispatch_queue_t serialQueue = dispatch_queue_create("com.audio.www", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{

        NSString *url = self.musicUrlArray[self.currentIndex];
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
        self.player = [[AVAudioPlayer alloc] initWithData:data error:nil];
        self.player.delegate = self;
        [self.player prepareToPlay];
        [self.player play];

        //滑塊
        CGFloat allTime = [self.musicAllTimeArray[self.currentIndex] floatValue];
        self.slider.maximumValue = allTime;
        
        //計時器只能在主執行緒中
        dispatch_async(dispatch_get_main_queue(), ^{
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(reloadAction) userInfo:nil repeats:YES];
        });
    });
}

#pragma mark -- 每1秒 給進度條賦一次值
-(void)reloadAction {
    if ([self.player isPlaying]) {
        self.slider.value = self.player.currentTime;
    }
}

#pragma mark 點選暫停
- (IBAction)suspendAction:(UIButton *)sender {
    sender.selected = !sender.selected;
    if (sender.selected) {
        [self.player pause]; //暫停
        [sender setTitle:@"播放" forState:UIControlStateSelected];
    } else {
        [self.player play];  //播放
        [sender setTitle:@"暫停" forState:UIControlStateNormal];
    }
}

#pragma mark 點選上一首
- (IBAction)formerAction:(UIButton *)sender {
    [self myPlayFormer]; //播放上一首
}

#pragma mark 點選下一首
- (IBAction)nextAction:(UIButton *)sender {
    [self myPlayNext];  //播放下一首
}

#pragma mark 上一首
-(void)myPlayFormer {
    if (self.currentIndex > 0) {
        [self.player pause];
        self.currentIndex--;
        [self playerCurrentMusic];
    }
}
#pragma mark 下一首
-(void)myPlayNext {
    if (self.currentIndex < self.musicUrlArray.count - 1) {
        [self.player pause];
        self.currentIndex++;
        [self playerCurrentMusic];
    }
}

/*****************   控制檯   *****************/
#pragma mark -- 宣告App接收遠端控制事件
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

#pragma mark -- App結束宣告接收遠端控制事件
- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

#pragma mark -- 接受控制檯的控制事件
- (void)remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
    if (receivedEvent.type == UIEventTypeRemoteControl) {
        //判斷點選按鈕的型別
        switch (receivedEvent.subtype) {
                
            case UIEventSubtypeRemoteControlPlay:
                [self.player play];  //播放
                break;
            case UIEventSubtypeRemoteControlPause:
                [self.player pause]; //暫停
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                [self myPlayFormer]; //播放上一首
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                [self myPlayNext];  //播放下一首
                break;
            default:
                break;
        }
    }
}

#pragma mark 實現後臺播放完一首後, 繼續播放下一首
- (BOOL)canBecomeFirstResponder {
    return YES;
}

/*****************   AVAudioPlayer 代理   *****************/
#pragma mark -- 後臺播放被打斷, 繼續恢復播放 (比如打電話...)
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags {
    [self.player play];
}
#pragma mark -- 播放完當前聲音後,播放下一首
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (self.currentIndex < self.musicUrlArray.count - 1) {
        self.currentIndex++;
        [self playerCurrentMusic];
    }
}

#pragma mark -- 滑塊滑動改變播放進度(代理方法)
-(void)changValue:(UISlider *)slider {
    //拖動滑塊時, 停止計時器
    [self.timer invalidate];
    self.player.currentTime = slider.value;
    //開始計時器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(reloadAction) userInfo:nil repeats:YES];
}

想要實現後臺播放(即程式進入後臺,音樂繼續播放)需要修改plist檔案:

1.新增required background modes這個key項,並選擇"App plays audio or streams audio/video using AirPlay"這個value項。
2.XCode7.1以上獲取網路資料,需要修改plist檔案 Info.plist中新增App Transport Security Settings型別Dictionary App Transport Security Settings下新增Allow Arbitrary Loads型別Boolean,值設為YES

需要在後臺實現播放頁的各種上一首,下一首...各種功能, 還需要在Appdelegate.m的background方法裡:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    
    //程式進入後臺後, 為了讓音樂繼續播放, 在此方法裡建立該代理播放頁, 並指定它播放器的代理
    PlayViewController *player = [PlayViewController defaultPlayVC];
    player.player.delegate = player;
}
當前工程Demo地址: http://download.csdn.net/detail/margaret_mo/9412069