English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Há muito tempo não escrevi nada, recentemente o trabalho de overtime foi muito severo, hoje tentei encontrar tempo para organizar o reprodutor de música DOUAudioStreamer que usei, pois o projeto anterior usava AVPlayer, que também é bom, mas precisa de um tempo de cache antes de reproduzir, o chefe viu e pediu, que mude o cache para reprodução (quando há rede, clique no botão de reprodução e reproduza imediatamente), por que não dizer cedo? Por que não dizer cedo? Por que não dizer cedo? Como pode ser? Só posso perdoá-lo, continuar a escrever código...... (vamos direto ao ponto)
I. Importar biblioteca de terceiros
pod 'DOUAudioStreamer'
ou endereço de download do GitHup:https://github.com/douban/DOUAudioStreamer
II. Uso
1.Obter o arquivo NAKPlaybackIndicatorView e os arquivos MusicIndicator.h e MusicIndicator.m do demo e importar os cabeçalhos
//Reprodução de música
#import "DOUAudioStreamer.h"
#import "NAKPlaybackIndicatorView.h"
#import "MusicIndicator.h"
#import "Track.h"
Como na figura:
2.Criar uma classe Track para armazenar URLs de reprodução de música
3.Adicionar DOUAudioStreamer ao arquivo de interface.h e inicializar com singleton
+ (instancetype)sharedInstance ; @property (nonatomic, strong) DOUAudioStreamer *streamer;
Como na figura:
No .m implementar:
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; }
Evento do botão de reprodução
#pragma mark ---Botão de reprodução de música -(void)playMusicStart:(UIButton *)sender { //Obter célula por meio de botão MusicCollectionViewCell *musicCell = (MusicCollectionViewCell *)[[sender superview] superview]; if(_playFirst == 0){//_playFirst == 0 para reprodução inicial, outros para pausa NSURL *url = [NSURL URLWithString:HttpImgUrl(musicCell.model.musicUrl)]; _audioTrack.audioFileURL = url; @try { [self removeStreamerObserver]; }; @catch(id anException){ } //Antes de reprodução no DOUAudioStreamer, deve ser configurado como 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]; }else{ [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; [_streamer pause]; } _playFirst++; }
Para adicionar observador
- (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]; } /// destruição do player - (void)dealloc{ se (_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)removerObservadorDoStreamer { [_streamer removeObserver:self forKeyPath:@"status"]; [_streamer removeObserver:self forKeyPath:@"duration"]; [_streamer removeObserver:self forKeyPath:@"bufferingRatio"]; } - (void)observarValorPelaChaveDoCaminho:(NSString *)chave do caminho do objeto:(id)object mudar:(NSDictionary *)mudar contexto:(void *)context { se (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; } }
Desta forma, será possível reproduzir.
A exibição de música na tela de bloqueio, pausa a reprodução após a remoção do fone de ouvido, e monitoramento de eventos de interrupção de áudio
-(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; //Accept remote control [self becomeFirstResponder]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } //This cannot be forgotten -(BOOL)canBecomeFirstResponder{ return YES; } - (void)viewDidLoad { [super viewDidLoad]; //Music player [self initPlayer]; } #pragma mark =========================Music Playback============================== //Music player -(void)initPlayer { _audioTrack = [[Track alloc] init]; AVAudioSession *session = [AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; //Let the app support receiving remote control events [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; //Add notification, pause playback after pulling out the earphones [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil]; // Listen to audio interruption events [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionWasInterrupted:) name:AVAudioSessionInterruptionNotification object:session]; } // Listen to audio interruption events - (void)audioSessionWasInterrupted:(NSNotification *)notification { //When interrupted if (AVAudioSessionInterruptionTypeBegan == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) { [_streamer pause]; UIButton *btn = (UIButton *)[self.view viewWithTag:", "第44段": "sender = (UIButton2000]; [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; } else if (AVAudioSessionInterruptionTypeEnded == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue])}} { } } // pausar reprodução após a remoção do fone de ouvido -(void)routeChange:(NSNotification *)notification{ NSDictionary *dic=notification.userInfo; int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue]; //igual a AVAudioSessionRouteChangeReasonOldDeviceUnavailable indica que a saída antiga não está disponível if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey]; AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject]; //o dispositivo original é fone de ouvido, então pause if ([portDescription.portType isEqualToString:@"Headphones"]) { [_streamer pause]; UIButton *btn = (UIButton *)[self.view viewWithTag:", "第44段": "sender = (UIButton2000]; [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal]; } } } //exibir música no bloqueio da tela (este método pode ser chamado ao clicar em reprodução, passando valores) - (void)setupLockScreenInfoWithSing:(NSString *)sign WithSigner:(NSString *)signer WithImage:(UIImage *)image { // 1.obter centro de bloqueio da tela MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter]; //Initialize a dictionary to store music information NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary]; // 2Set the song name if (sign) { [playingInfoDict setObject:sign forKey:MPMediaItemPropertyAlbumTitle]; } // Set the artist name if (signer) { [playingInfoDict setObject:signer forKey:MPMediaItemPropertyArtist]; } // 3Set the cover image //UIImage *image = [self getMusicImageWithMusicId:self.currentModel]; if (image) { MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image]; [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork]; } // 4Set the total duration of the song //[playingInfoDict setObject:self.currentModel.detailDuration forKey:MPMediaItemPropertyPlaybackDuration]; //Assign music information to the nowPlayingInfo property of the lock screen center playingInfoCenter.nowPlayingInfo = playingInfoDict; // 5.Enable remote interaction [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } //Lock screen operation - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { if (receivedEvent.type == UIEventTypeRemoteControl) { UIButton *sender = (UIButton *)[self.view viewWithTag:", "第44段": "sender = (UIButton2000]; switch (receivedEvent.subtype) {//Verificar se é controle remoto 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; } } }
Imagem geral:
A imagem acima é o estado não reproduzido
A imagem acima é o estado de reprodução
A imagem acima é o estado de bloqueio da tela
Deve não haver nada mais a adicionar, por enquanto vamos parar, se houver algo que não esteja bem, você pode discutir na seção de comentários abaixo, obrigado pelo apoio ao tutorial Gritar.