English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Образцовая демонстрация использования iOS плеера DOUAudioStreamer

Долго не писал, lately был слишком занят на работе, сегодня нашел время, чтобы привести в порядок используемый музыкальный плеер DOUAudioStreamer. Поскольку в проекте ранее использовался AVPlayer, это тоже хорошо, но сначала нужно缓存 в течение определенного времени, а затем воспроизводить. После того, как boss посмотрел, он потребовал, чтобы это было caching and playing (при наличии интернета, нажатие кнопки воспроизведения сразу же начнет воспроизведение), почему так рано не сказали! Почему так рано не сказали! Почему так рано не сказали! Что еще можно сделать? Единственное, что можно сделать, это простить его и продолжить писать коды...... (лучше сразу перейти к коду)

Первый раздел: импортируйте сторонний библиотеку

pod 'DOUAudioStreamer'

или адрес скачивания на GitHup:https://github.com/douban/DOUAudioStreamer

Второй раздел

1. Получите файл NAKPlaybackIndicatorView и файлы MusicIndicator.h и MusicIndicator.m из demo и импортируйте заголовочный файл

//Воспроизведение музыки
#import "DOUAudioStreamer.h"
#import "NAKPlaybackIndicatorView.h"
#import "MusicIndicator.h"
#import "Track.h"

如图:

2. Создать класс Track для хранения URL воспроизведения музыки

3. В файле интерфейса .h добавить DOUAudioStreamer и инициализировать его как单体

+ (instancetype)sharedInstance ;
@property (nonatomic, strong) DOUAudioStreamer *streamer;

 

如图:

Реализация в .m:

static void *kStatusKVOKey = &kStatusKVOKey;
static void *kDurationKVOKey = &kDurationKVOKey;
static void *kBufferingRatioKVOKey = &kBufferingRatioKVOKey;
@property (strong, nonatomic) MusicIndicator *musicIndicator;
@property (nonatomic, strong) Track *audioTrack;
+ (instancetype)sharedInstance {
 static HYNEntertainmentController *_sharedMusicVC = nil;
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
  _sharedMusicVC = [[HYNEntertainmentController alloc] init];
  _sharedMusicVC.streamer = [[DOUAudioStreamer alloc] init];
 }); 
 return _sharedMusicVC;
}

 

Пользовательское событие кнопки воспроизведения

#pragma mark ---音乐播放按钮
-(void)playMusicStart:(UIButton *)sender
{
  //通过按钮获取cell
  if(_playFirst == 0){//_playFirst == 0 - это первое воспроизведение, другие - это пауза
 NSURL *url = [NSURL URLWithString:HttpImgUrl(musicCell.model.musicUrl)];
  _audioTrack.audioFileURL = url;
  @try {
  [self removeStreamerObserver];
   @catch(id anException){
  } 
  }
  //В процессе воспроизведения DOUAudioStreamer, его необходимо сначала установить в nil
  _streamer = nil;
  _streamer = [DOUAudioStreamer streamerWithAudioFile:_audioTrack];
  [self addStreamerObserver];
  [_streamer play];
 }
 if([_streamer status] == DOUAudioStreamerPaused ||
  [_streamer status] == DOUAudioStreamerIdle){
  [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal];
  [_streamer play]; 
 }
  [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];
  [_streamer pause]; 
 }
 _playFirst++;
}

 

Добавление наблюдателя

- (void)addStreamerObserver {
 [_streamer addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:kStatusKVOKey];
 [_streamer addObserver:self forKeyPath:@"duration" options:NSKeyValueObservingOptionNew context:kDurationKVOKey];
 [_streamer addObserver:self forKeyPath:@"bufferingRatio" options:NSKeyValueObservingOptionNew context:kBufferingRatioKVOKey]; 
}
/// Плеер уничтожен
- (void)dealloc{
 if (_streamer !=nil) {
  [_streamer pause];
  [_streamer removeObserver:self forKeyPath:@"status" context:kStatusKVOKey];
  [_streamer removeObserver:self forKeyPath:@"duration" context:kDurationKVOKey];
  [_streamer removeObserver:self forKeyPath:@"bufferingRatio" context:kBufferingRatioKVOKey];
  _streamer =nil;
 } 
}
- (void)removeStreamerObserver {
 [_streamer removeObserver:self forKeyPath:@"status"];
 [_streamer removeObserver:self forKeyPath:@"duration"];
 [_streamer removeObserver:self forKeyPath:@"bufferingRatio"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
 if (context == kStatusKVOKey) {
  [self performSelector:@selector(updateStatus)]
      onThread:[NSThread mainThread]
     withObject:nil}
    waitUntilDone:NO];
 } else if (context == kDurationKVOKey) {
  [self performSelector:@selector(updateSliderValue:)]
      onThread:[NSThread mainThread]
     withObject:nil}
    waitUntilDone:NO];
 } else if (context == kBufferingRatioKVOKey) {
  [self performSelector:@selector(updateBufferingStatus)]
      onThread:[NSThread mainThread]
     withObject:nil}
    waitUntilDone:NO];
 }
  [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
 }
}
- (void)updateSliderValue:(id)timer {
}
-(void)updateBufferingStatus
{ 
}
- (void)updateStatus {
 //self.musicIsPlaying = NO;
 _musicIndicator.state = NAKPlaybackIndicatorViewStateStopped;
 switch ([_streamer status]) {
  case DOUAudioStreamerPlaying:
   // self.musicIsPlaying = YES;
   _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying;
   break;
  case DOUAudioStreamerPaused:
   break;
  case DOUAudioStreamerIdle:
   break;
  case DOUAudioStreamerFinished:
   break;
  case DOUAudioStreamerBuffering:
   _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying;
   break;  
  case DOUAudioStreamerError:
   break;
 } 
}

Таким образом, можно будет воспроизводить.

Отображение музыки при блокировке экрана, при извлечении наушников暂停 воспроизведение,监听音频打断事件

-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// принимать удаленное управление
[self becomeFirstResponder];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
// эту нельзя забыть
-(BOOL)canBecomeFirstResponder{
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Музыкальный плеер
[self initPlayer];
}
#pragma mark =========================Музыкальное воспроизведение==============================
// Музыкальный плеер
-(void)initPlayer
{
_audioTrack = [[Track alloc] init];
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
// Позволить приложению принимать события удаленного управления
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Добавление уведомлений, при извлечении наушников приостанавливается воспроизведение
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
// Слушать события прерывания аудио
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionWasInterrupted:) name:AVAudioSessionInterruptionNotification object:session];
}
// Слушать события прерывания аудио
- (void)audioSessionWasInterrupted:(NSNotification *)notification
{
 // При прерывании
if (AVAudioSessionInterruptionTypeBegan == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue])
{
[_streamer pause];
UIButton *btn = (UIButton *)[self.view viewWithTag:2000];
[btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];
}
else if (AVAudioSessionInterruptionTypeEnded == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue])
{
}
}
// Вывод наушников после приостановки воспроизведения
-(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"]) {
[_streamer pause];
UIButton *btn = (UIButton *)[self.view viewWithTag:2000];
[btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];
}
}
}
//锁屏时音乐显示(这个方法可以在点击播放时,调用传值)
- (void)setupLockScreenInfoWithSing:(NSString *)sign WithSigner:(NSString *)signer WithImage:(UIImage *)image
{
// 1.获取锁屏中心
MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
//初始化一个存放音乐信息的字典
NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
// 2、设置歌曲名
if (sign) {
[playingInfoDict setObject:sign forKey:MPMediaItemPropertyAlbumTitle];
}
// 设置歌手名
if (signer) {
[playingInfoDict setObject:signer forKey:MPMediaItemPropertyArtist];
}
// 3设置封面的图片
//UIImage *image = [self getMusicImageWithMusicId:self.currentModel];
if (image) {
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
[playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
}
// 4设置歌曲的总时长
//[playingInfoDict setObject:self.currentModel.detailDuration forKey:MPMediaItemPropertyPlaybackDuration];
//音乐信息赋值给获取锁屏中心的nowPlayingInfo属性
playingInfoCenter.nowPlayingInfo = playingInfoDict;
// 5.开启远程交互
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
//锁屏时操作
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
UIButton *sender = (UIButton *)[self.view viewWithTag:2000];
switch (receivedEvent.subtype) { // Проверка на удаленное управление}
case UIEventSubtypeRemoteControlPause:
[[HYNEntertainmentController sharedInstance].streamer pause];
[sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];
break;
case UIEventSubtypeRemoteControlStop:
break;
case UIEventSubtypeRemoteControlPlay:
[[HYNEntertainmentController sharedInstance].streamer play];
[sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
break;
case UIEventSubtypeRemoteControlNextTrack:
break;
case UIEventSubtypeRemoteControlPreviousTrack:
break;
default:
break;
}
}
}

Общее изображение:

Верхнее изображение - это состояние не воспроизведения

Верхнее изображение - это состояние воспроизведения

Верхнее изображение - это состояние блокировки экрана

Должно быть нечего добавить, временно завершаем, если есть недостатки, можно обсудить в评论区下方, спасибо за поддержку呐喊 руководства.

Вам может понравиться