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

شرح شامل لاستخدام DOUAudioStreamer في مشغل الموسيقى iOS

لم أكتب شيئًا منذ فترة طويلة، وكان العمل الإضافي مؤخرًا شديدًا جدًا، لذا سأحاول اليوم تنظيم مشغل الموسيقى الذي أستخدمه، DOUAudioStreamer، حيث كان المشروع يستخدم AVPlayer من قبل، هذا يمكن أيضًا، لكن يجب أولاً التخزين لبعض الوقت قبل البث، بعد أن شاهده المدير طلب مني أن أغير التخزين إلى البث (عند وجود اتصال بالإنترنت، يتم البث عند النقر على زر البث)، لماذا لم يقل ذلك من قبل؟ لماذا لم يقل ذلك من قبل؟ لماذا لم يقل ذلك من قبل؟ ماذا يمكن أن أفعله؟ لا يمكن إلا أن أغفر له، وأستمر في كتابة الشيفرة...... (لنأتي بالكود مباشرة)

الجزء الأول: استيراد مكتبة خارجية

pod 'DOUAudioStreamer'

أو عنوان الرابط في GitHup:https://github.com/douban/DOUAudioStreamer

الجزء الثاني

1. من الملفات الموجودة في demo الحصول على ملف NAKPlaybackIndicatorView وملفات MusicIndicator.h وMusicIndicator.m، ثم استيراد ملف الرأس

//استماع إلى الموسيقى
#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
  MusicCollectionViewCell *musicCell = (MusicCollectionViewCell *)[[sender superview] superview];
 if(_playFirst == 0){//_playFirst == 0 لأول مرة تشغيل، أخرى للإيقاف
  NSURL *url = [NSURL URLWithString:HttpImgUrl(musicCell.model.musicUrl)];
  _audioTrack.audioFileURL = url;
  @try {
   [self removeStreamerObserver];
  } 
  }
  //في وقت تشغيل 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];
 } else {
  [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;
}
}
}

الصورة الكاملة:

الصورة أعلاه هي حالة عدم التشغيل

الصورة أعلاه هي حالة التشغيل

الصورة أعلاه هي حالة القفل

لا يوجد شيء يجب إضافته الآن، يرجى التوقف مؤقتًا، إذا كان هناك نقص، يمكنك مناقشته في منطقة التعليقات أدناه، شكرًا لدعم دروس النداء.

أنت قد تستمتع بهذه