一个简单的后台音频播放demo,锁屏控制播放
·
这个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
注,开启后台模式
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)