这个demo功能很简单,用AVPlayer实现了音频播放功能,
支持后台播放及锁屏状态下控制播放

1.AppDelegate的分类,这里写控制事件

#import "AppDelegate.h"

NS_ASSUME_NONNULL_BEGIN

@interface AppDelegate (audio)
- (void)setLockPlay;
@end

NS_ASSUME_NONNULL_END
#import "AppDelegate+audio.h"
#import "AudioPlayUtil.h"
@implementation AppDelegate (audio)

- (void)setLockPlay{
    [[UIApplication  sharedApplication] beginReceivingRemoteControlEvents];
    [self  becomeFirstResponder];
}

- (void)beginReceivingRemoteControlEvents {
    [self becomeFirstResponder];
}
-  (BOOL)canBecomeFirstResponder
{
    return  YES;
}
-  (void)remoteControlReceivedWithEvent:(UIEvent  *)receivedEvent  {

    if  (receivedEvent.type  ==  UIEventTypeRemoteControl)  {

        switch  (receivedEvent.subtype)  {

            case UIEventSubtypeRemoteControlTogglePlayPause:
                [[AudioPlayUtil sharedInstance] audioPause];
                break;

            case UIEventSubtypeRemoteControlNextTrack:

                if ([AudioPlayUtil sharedInstance].playNext) {
                    [AudioPlayUtil sharedInstance].playNext();
                }
                break;

            case UIEventSubtypeRemoteControlPreviousTrack:
                if ([AudioPlayUtil sharedInstance].playUper) {
                    [AudioPlayUtil sharedInstance].playUper();
                }
                break;
                
            case UIEventSubtypeRemoteControlPlay:
                [[AudioPlayUtil sharedInstance] audioPlay];
                break;
                
            case UIEventSubtypeRemoteControlPause:
                [[AudioPlayUtil sharedInstance] audioPause];
                break;
            default:
                break;

        }

    }
    
}
@end

2.AudioPlayUtil,这里主要是播放的一些方法和监听事件

#import <Foundation/Foundation.h>
#import "playConfig.h"
NS_ASSUME_NONNULL_BEGIN

@interface AudioPlayUtil : NSObject

+ (AudioPlayUtil*)sharedInstance;

- (void)initPlayerWithAudioInfo:(playConfig *)audioInfo;


@property (copy, nonatomic) void (^audioProgressUpdate)(NSMutableDictionary * diction);
@property (copy, nonatomic) void (^playerCompletion)(void);
@property (copy, nonatomic) void (^playNext)(void);
@property (copy, nonatomic) void (^playUper)(void);

//暂停播放
- (void)audioPause;
//开始播放
- (void)audioPlay;
//回退(15)秒
- (void)QuickretreatWithTime:(NSInteger )miao;
//快进(15)秒
- (void)FastforwardWithTime:(NSInteger)miao;

//设置播放进度
- (void)setToTime:(NSInteger)time;

//设置播放倍速
- (void)settingPlayRate:(float)rate;
@end

//播放主要文件


#import "AudioPlayUtil.h"
#import "AVFoundation/AVPlayer.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>

static AudioPlayUtil *audioPlayUtil = nil;

@interface AudioPlayUtil ()

@property(nonatomic,assign) id timeObserve;
@property(nonatomic,strong) AVPlayer *player;
@property (nonatomic,assign) BOOL isPlay;
@property (strong, nonatomic) playConfig *config;
//倍速
@property (nonatomic,assign) float rate;
@end

@implementation AudioPlayUtil

+ (AudioPlayUtil*)sharedInstance {
    @synchronized(self) {
        if(!audioPlayUtil) {
            audioPlayUtil = [[self alloc] init];
            audioPlayUtil.rate = 1;
        }
    }
    return audioPlayUtil;
}

- (void)initPlayerWithAudioInfo:(playConfig *)audioInfo{
    if (!audioInfo) {
        NSLog(@"数据错误");
        return;
    }
    self.config = audioInfo;
    if (self.timeObserve) {
        [self.player removeTimeObserver:self.timeObserve];
        self.timeObserve = nil;
    }
    NSURL * url  = [NSURL URLWithString:audioInfo.url];
    AVPlayerItem * songItem = [[AVPlayerItem alloc] initWithURL:url];
    [songItem seekToTime:CMTimeMake(self.config.current, 1)];
    [self.player replaceCurrentItemWithPlayerItem:songItem];
    [self setPlayingCenter];
    if (self.config.isAutoPlay) {
        [self audioPlay];
    }else{
        [self audioPause];
    }
    __block AudioPlayUtil *wself = self;
    self.timeObserve  = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        float current = CMTimeGetSeconds(time);
        float total = (CMTimeGetSeconds(wself.player.currentItem.duration));
        if (total > 0) {
            wself.config.current = current;
            [wself setLockProgress:current];
            float progress = current / total;
            NSMutableDictionary *diction = [NSMutableDictionary dictionary];
            diction[@"total"] = @(total);
            diction[@"current"] = @(current);
            if (wself.audioProgressUpdate) {
                wself.audioProgressUpdate(diction);
            }
            if (progress == 1){
                if (wself.playerCompletion) {
                    wself.playerCompletion();
                }
            }
            
        }
    }];
    UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
    UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
    AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
    AudioSessionSetActive(true);
    
    
}


- (void)audioPause{
    [self.player pause];
    self.isPlay = NO;
}
- (void)audioPlay{
    [self.player play];
    self.isPlay = YES;
}
- (void)setLockProgress:(NSInteger)current{
    MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
    NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
    [nowPlayingInfo setObject:[NSNumber numberWithDouble:current] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
    center.nowPlayingInfo = nowPlayingInfo;
}
//退回多少秒
- (void)QuickretreatWithTime:(NSInteger )miao{
    float time = self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale - miao >= 0 ? self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale - miao:0;
    [self.player.currentItem seekToTime:CMTimeMakeWithSeconds(time, self.player.currentItem.currentTime.timescale)];
    [self setLockProgress:time];
    
}
//快进多少秒
- (void)FastforwardWithTime:(NSInteger)miao{
    float time = self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale + miao <= self.player.currentItem.currentTime.timescale ? self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale + miao:self.player.currentItem.currentTime.timescale;
    [self.player.currentItem seekToTime:CMTimeMakeWithSeconds(time, self.player.currentItem.currentTime.timescale) toleranceBefore:CMTimeMake(1, self.player.currentItem.currentTime.timescale) toleranceAfter:CMTimeMake(1, self.player.currentItem.currentTime.timescale)];
    [self setLockProgress:time];

}
//设置播放进度
- (void)setToTime:(NSInteger)time{
    [self.player.currentItem seekToTime:CMTimeMakeWithSeconds(time, 1)];
    [self setLockProgress:time];
}
//设置播放倍速
- (void)settingPlayRate:(float)rate{
    self.player.rate = rate;
    self.rate = rate;
    if (!self.isPlay) {
        [self.player pause];
    }
    MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
    NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
    [nowPlayingInfo setObject:[NSNumber numberWithFloat:self.rate] forKey:MPNowPlayingInfoPropertyPlaybackRate];
    center.nowPlayingInfo = nowPlayingInfo;
}




                   
- (void)setPlayingCenter{
    if ([[[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo objectForKey:MPMediaItemPropertyTitle]  isEqualToString:self.config.albumItemTitle]) {
        return;
    }
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.config.albumCoverImage]]]];
        NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary dictionary];
        [nowPlayingInfo setObject:self.config.albumItemTitle forKey:MPMediaItemPropertyTitle];
        [nowPlayingInfo setObject:self.config.albumArtist forKey:MPMediaItemPropertyArtist];
        [nowPlayingInfo setObject:self.config.albumTitle forKey:MPMediaItemPropertyAlbumTitle];
        [nowPlayingInfo setObject:artwork forKey:MPMediaItemPropertyArtwork];
        [nowPlayingInfo setObject:[NSNumber numberWithFloat:self.rate] forKey:MPNowPlayingInfoPropertyPlaybackRate];
        [nowPlayingInfo setObject:[NSNumber numberWithDouble:self.config.current] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
        [nowPlayingInfo setObject:[NSNumber numberWithDouble:self.config.duration] forKey:MPMediaItemPropertyPlaybackDuration];
        dispatch_async(dispatch_get_main_queue(), ^{
            center.nowPlayingInfo = nowPlayingInfo;
        });
    });
    

}

- (AVPlayer *)player{
    if (!_player) {
        _player = [[AVPlayer alloc] init];
    }
    return _player;
}

3.在AppDelegate 调用[self setLockPlay]

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [self setLockPlay];
    return YES;
}

4.ViewController.h 里包含使用方法(自己随便写的控件,用到自己整理)


#import "ViewController.h"
#import "AudioPlayUtil.h"
@interface ViewController ()
@property (strong, nonatomic) NSMutableArray *array;
@property (nonatomic,assign) NSInteger index;
@property (strong, nonatomic) AudioPlayUtil *audioUtil;
@property (strong, nonatomic) UIButton *bfBtn;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
   
    ///一下代码随便写的,
    UIButton *b1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 60, 30)];
    [self.view addSubview:b1];
    [b1 setTitle:@"上一首" forState:0];
    b1.backgroundColor = UIColor.redColor;
    [b1 addTarget:self action:@selector(shangyishou) forControlEvents:UIControlEventTouchUpInside];
    
    UIButton *b2 = [[UIButton alloc] initWithFrame:CGRectMake(200, 100, 60, 30)];
    [self.view addSubview:b2];
    [b2 setTitle:@"播放" forState:0];
    [b2 setTitle:@"暂停" forState:UIControlStateSelected];
    b2.backgroundColor = UIColor.redColor;
    [b2 addTarget:self action:@selector(bofang:) forControlEvents:UIControlEventTouchUpInside];
    self.bfBtn = b2;
    
    UIButton *b3 = [[UIButton alloc] initWithFrame:CGRectMake(300, 100, 60, 30)];
    [self.view addSubview:b3];
    [b3 setTitle:@"下一首" forState:0];
    b3.backgroundColor = UIColor.redColor;
    [b3 addTarget:self action:@selector(xiayishou) forControlEvents:UIControlEventTouchUpInside];
    
    
    
    UIButton *b4 = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 60, 30)];
    [self.view addSubview:b4];
    [b4 setTitle:@"退15秒" forState:0];
    b4.backgroundColor = UIColor.redColor;
    [b4 addTarget:self action:@selector(houtui15miao) forControlEvents:UIControlEventTouchUpInside];
    
    
    UIButton *b5 = [[UIButton alloc] initWithFrame:CGRectMake(300, 200, 60, 30)];
    [self.view addSubview:b5];
    [b5 setTitle:@"进15秒" forState:0];
    b5.backgroundColor = UIColor.redColor;
    [b5 addTarget:self action:@selector(kuaijin15miao) forControlEvents:UIControlEventTouchUpInside];
    
   
    [self initData];
    
    AudioPlayUtil *util = [AudioPlayUtil sharedInstance];
    
    util.audioProgressUpdate = ^(NSMutableDictionary * _Nonnull diction) {
        NSLog(@"播放进度%@---总时长%@",diction[@"current"],diction[@"total"]);
    };
    util.playerCompletion = ^{
        //播放完成
        [self xiayishou];
    };
    util.playNext = ^{
        [self xiayishou];
    };
    util.playUper = ^{
        [self shangyishou];
    };
    self.audioUtil = util;
    
    //默认播放第一个
    [self bofangAudio];
}

- (void)houtui15miao{
    [[AudioPlayUtil sharedInstance] QuickretreatWithTime:15];
}
- (void)kuaijin15miao{
    [[AudioPlayUtil sharedInstance] FastforwardWithTime:15];
}
- (void)bofang:(UIButton *)btn{
    btn.selected = !btn.selected;
    if (btn.selected) {
        [self.audioUtil audioPlay];
    }else{
        [self.audioUtil audioPause];
    }
    
}

- (void)shangyishou{
    self.index --;
    if (self.index < 0) {
        self.index = 0;
    }
    [self bofangAudio];
}

- (void)xiayishou{
    self.index++;
    if (self.index >= self.array.count) {
        self.index = 0;
    }
    [self bofangAudio];
}

- (void)bofangAudio{
    self.bfBtn.selected = YES;
    playConfig *config = self.array[self.index];
    [[AudioPlayUtil sharedInstance] initPlayerWithAudioInfo:config];
}

- (NSMutableArray *)array{
    if (!_array) {
        _array = [NSMutableArray array];
    }
    return _array;
}

- (void)initData{
    playConfig *config = [[playConfig alloc] init];
    config.isAutoPlay = YES;
    config.url = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/audio/2023-08/2023-08-02/6c83e3fb3d944ef4be68d08f1debe89a.mp3";
    config.albumTitle = @"水浒发刊词 听108位英雄的豪情侠义!";
    config.albumItemTitle = @"水浒发刊词 听108位英雄的豪情侠义!";
    config.albumCoverImage = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/image/2023-08/2023-08-07/e3c2d4971ceb48719d9ad50a14dbed4b.jpg";
    config.albumArtist = @"作者:狮子老爸";
    config.current = 0;
    config.duration = 647.0;
    [self.array addObject:config];
    
    playConfig *config2 = [[playConfig alloc] init];
    config2.isAutoPlay = YES;
    config2.url = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/audio/2023-07/2023-07-30/3d987aef38fc496cae8b0a5e8b8ea4ca.mp3";
    config2.albumTitle = @"水浒发刊词 听108位英雄的豪情侠义!";
    config2.albumItemTitle = @"水浒传 001 瘟疫蔓延 民不聊生";
    config2.albumCoverImage = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/image/2023-08/2023-08-07/e3c2d4971ceb48719d9ad50a14dbed4b.jpg";
    config2.albumArtist = @"作者:狮子老爸";
    config2.current = 0;
    config2.duration = 1944.0;
    [self.array addObject:config2];
    
    
    playConfig *config3 = [[playConfig alloc] init];
    config3.isAutoPlay = YES;
    config3.url = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/audio/2023-07/2023-07-30/bed56571edc54814a65a56101e62abf4.mp3";
    config3.albumTitle = @"水浒发刊词 听108位英雄的豪情侠义!";
    config3.albumItemTitle = @"水浒传 002 皇帝降旨 请张天师";
    config3.albumCoverImage = @"https://lion-prod-1318118438.cos.ap-beijing.myqcloud.com/image/2023-08/2023-08-07/e3c2d4971ceb48719d9ad50a14dbed4b.jpg";
    config3.albumArtist = @"作者:狮子老爸";
    config3.current = 0;
    config3.duration = 289.0;
    [self.array addObject:config3];
}
@end

注,开启后台模式在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐