English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Hace mucho tiempo que no escribía, últimamente el trabajo extra ha sido muy severo, hoy aproveché un momento para organizar el reproductor de música DOUAudioStreamer que he utilizado, ya que el proyecto anterior utilizaba AVPlayer, esto también es posible, pero es necesario hacer un caché durante un tiempo antes de la reproducción, después de que el jefe lo vió, solicitó que cambiara el caché para la reproducción (cuando hay conexión a internet, hacer clic en el botón de reproducción para reproducir inmediatamente), ¿por qué no lo dijo antes! ¿por qué no lo dijo antes! ¿por qué no lo dijo antes! ¿Qué más se puede hacer? Solo puedo perdonarlo, seguir escribiendo código... (mejor subo el código directamente)
I. Importar biblioteca de terceros
pod 'DOUAudioStreamer'
o descargar desde la dirección de GitHup:https://github.com/douban/DOUAudioStreamer
II. Uso
1.Obtener el archivo NAKPlaybackIndicatorView y los archivos MusicIndicator.h y MusicIndicator.m del demo y agregar la importación de cabeceras
//Reproducción de música
#import "DOUAudioStreamer.h"
#import "NAKPlaybackIndicatorView.h"
#import "MusicIndicator.h"
#import "Track.h"
Como se muestra en la figura:
2.Crear una clase Track para almacenar la URL de reproducción de música
3.Añadir DOUAudioStreamer al archivo .h necesario y usar el patrón de diseño singleton para inicializar
+ (instancetype)sharedInstance ; @property (nonatomic, strong) DOUAudioStreamer *streamer;
Como se muestra en la figura:
En el archivo .m se implementa:
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 del botón de reproducción
#pragma mark ---Botón de reproducción de música -(void)playMusicStart:(UIButton *)sender { //Obtener celda a través de un botón MusicCollectionViewCell *musicCell = (MusicCollectionViewCell *)[[sender superview] superview]; if (_playFirst == 0) {//_playFirst == 0 para la reproducción inicial, otros para pausar NSURL *_audioTrack.audioFileURL = url; @try { [self removeStreamerObserver]; @catch(id anException) { } } //Es necesario establecer _streamer en nil antes de reproducir con DOUAudioStreamer _streamer = nil; _streamer = [DOUAudioStreamer streamerWithAudioFile:_audioTrack]; [self addStreamerObserver]; [_streamer play]; } if ([_streamer status] == DOUAudioStreamerPaused || if ([_streamer status] == DOUAudioStreamerIdle) { [sender setBackgroundImage:[UIImage imageNamed:@"icono_de_reproduccion_music"] forState:UIControlStateNormal]; [_streamer play]; } else { [sender setBackgroundImage:[UIImage imageNamed:@"icono_de_parada_music"] forState:UIControlStateNormal]; [_streamer pause]; } _playFirst++; }
Para agregar observadores
- (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]; } /// Reproductor destruido - (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; } }
De esta manera, se puede reproducir.
La pantalla de música está activa cuando el dispositivo está bloqueado, se detiene la reproducción al quitar el auricular y se monitorea el evento de interrupción de audio
-(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]; //使app能够接收远程控制事件 [[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:@"icono_stop_music"] forState:UIControlStateNormal]; } else if (AVAudioSessionInterruptionTypeEnded == [notificación.userInfo[AVAudioSessionInterruptionTypeKey] intValue])}} { } } // Detener reproducción después de quitar los auriculares -(void)cambioRuta:(NSNotification *)notificación{ NSDictionary *dic=notificación.userInfo; int razonCambio= [dic[AVAudioSessionRouteChangeReasonKey] intValue]; //Igual a AVAudioSessionRouteChangeReasonOldDeviceUnavailable indica que la salida antigua no está disponible if (razonCambio==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { AVAudioSessionRouteDescription *rutaDescription=dic[AVAudioSessionRouteChangePreviousRouteKey]; AVAudioSessionPortDescription *portDescription= [rutaDescription.salidas primerObjeto]; //Detener si el dispositivo original es un auricular if ([portDescription.portType isEqualToString:@"Cascos"]) { [_streamer pause]; UIButton *btn = (UIButton *)[self.view viewWithTag:2000]; [btn setBackgroundImage:[UIImage imageNamed:@"icono_stop_music"] forState:UIControlStateNormal]; } } } //Mostrar música en la pantalla de bloqueo (este método se puede llamar al hacer clic en reproducir y pasar valores) - (void)configurarInformacionDeBloqueoDePantallaConSing:(NSString *)firmar ConFirmador:(NSString *)firmador ConImage:(UIImage *)image { // 1.obtener centro de bloqueo de pantalla 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:2000]; switch (receivedEvent.subtype) {//Determinar si es control remoto case UIEventSubtypeRemoteControlPause: [[HYNEntertainmentController sharedInstance].streamer pause]; [sender setBackgroundImage:[UIImage imageNamed:@"icono_de_parada_music"] forState:UIControlStateNormal]; break; case UIEventSubtypeRemoteControlStop: break; case UIEventSubtypeRemoteControlPlay: [[HYNEntertainmentController sharedInstance].streamer play]; [sender setBackgroundImage:[UIImage imageNamed:@"icono_de_reproduccion_music"] forState:UIControlStateNormal]; break; case UIEventSubtypeRemoteControlTogglePlayPause: break; case UIEventSubtypeRemoteControlNextTrack: break; case UIEventSubtypeRemoteControlPreviousTrack: break; default: break; } } }
Imágenes globales:
La imagen superior es el estado no reproducido
La imagen superior es el estado de reproducción
La imagen superior es el estado de bloqueo de la pantalla
No debería haber mucho más que agregar, por ahora vamos a cerrar, si hay algún problema, puedes discutirlo en el área de comentarios a continuación. Gracias por tu apoyo a la tutoriales de gritos.