美文网首页学学人家的框架学无止境恩美第二个APP项目
AVPlayer 制作一个简单的在线音乐播放器

AVPlayer 制作一个简单的在线音乐播放器

作者: CoderGuogt | 来源:发表于2016-08-05 16:22 被阅读324次
    0AC7D6DB-AC44-45E6-A457-2D1517F1DF3F.png

    歌手图片旋转:
    先设置一个全局变量动画:

    {
        CABasicAnimation* _rotationAnimation; /**< 歌手图片旋转动画 */
    }
    

    初始化动画操作,歌手图片旋转动画

       // 歌手图片动画效果
        _rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        _rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2];
        _rotationAnimation.duration = kTimerInterval;
        _rotationAnimation.cumulative = YES;
        _rotationAnimation.repeatCount = CGFLOAT_MAX; // 设置旋转次数
    

    当歌曲开始播放的时候,将动画操作添加到歌手图片ImageView

    [self.playerImageView.layer addAnimation:_rotationAnimation forKey:@"rotationAnimation"];
    

    这样,类似网易云音乐歌手图片旋转的效果出来了

    接下来就是播放器的实现了,在这里使用的是AVPlayer来实现网络音乐播放
    AVPlayer中含有一个属性:AVPlayerItem,这个是媒体资源,包含媒体的总时间、缓冲情况等

    1.初始化一个AVPlayer

       NSURL *url = [NSURLURLWithString:@"http://mr7.doubanio.com/3fd082ae3370d22e48e300ab1d5d6590/1/fm/song/p190415_128k.mp4"];
       self.songItem = [AVPlayerItemplayerItemWithURL:url];
       self.player = [AVPlayerplayerWithPlayerItem:self.songItem];
    

    2.播放音乐

    [self.playerplay];
    

    3.暂停音乐

    [self.playerpause];
    

    4.一个简单的在线播放网络音乐就完成了,接下来需要显示歌曲的时间,首先添加一个监听媒体资源的加载情况

    // 监听媒体资源缓冲情况
    [self.songItem addObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];
    

    5.在媒体加载时响应监听方法时获取到歌曲的总时间,在这里,只需要获取一次,由于只有一首歌,所以使用GCD的方式去获取歌曲总时间

    /**
     *  KVO监听方法
     *
     *  @param keyPath
     *  @param object
     *  @param change
     *  @param context
     */
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        AVPlayerItem *songItem = object;
        if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
            // 媒体总时长
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                self.songTime = CMTimeGetSeconds(songItem.duration); // 获取到媒体的总时长
            });
            // 媒体缓冲
            NSArray *array=songItem.loadedTimeRanges;
            CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间的范围
            float startSeconds = CMTimeGetSeconds(timeRange.start);
            float durationSeconds = CMTimeGetSeconds(timeRange.duration);
            NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲的总长度
            NSLog(@"共缓冲:%.3f, %lf",totalBuffer, CMTimeGetSeconds(songItem.duration));
        }
    }
    

    获取到了歌曲的时间,我们还需要显示当前歌曲播放的时间,根据获取到的时间去实现进度条的改变

    __weak typeof(self) weakSelf = self;
    // 监听播放进度
    self.timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            CGFloat current = CMTimeGetSeconds(time);
            weakSelf.currentTime = current;
     }];
    

    到了这里,可以根据获得到的时间来计算进度条的value,接下来是根据通过改变进度条的value来实现播放器播放进度,给进度条添加一个target事件

    // 监听进度条的手动改变
    [self.progress addTarget:self action:@selector(progressValueChage) forControlEvents:UIControlEventValueChanged];
    

    当拖动进度条的监听方法去实现控制播放器的播放进度

    /**
     *  进度条改变操作
     */
    - (void)progressValueChage {
        CGFloat currentTime = self.songTimes * self.progress.value;
        [self.player changeProgress:currentTime];
    }
    
    /**
     *  手动改变进度
     *
     *  @param second 当前播放时间
     */
    - (void)changeProgress:(CGFloat)second {
        [self.player pause];
        [self.player seekToTime:CMTimeMakeWithSeconds(second, NSEC_PER_SEC) completionHandler:^(BOOL finished) {
            [self.player play];
        }];
    }
    

    这样一个简单的播放器网络音乐就完成了
    最后附上完整的代码
    ViewController.m文件

    #import "ViewController.h"
    #import "GPPlayer.h"
    #import "UIView+Category.h"
    
    #define kImageViewWidth 230
    #define kTimerInterval 20
    
    @interface ViewController ()
    
    @property (nonatomic, strong) GPPlayer *player; /**< 播放器 */
    @property (weak, nonatomic) IBOutlet UILabel *songTimeLabel;
    @property (weak, nonatomic) IBOutlet UILabel *currentTime;
    @property (nonatomic, weak) UIImageView *playerImageView; /**< 歌手图片 */
    @property (weak, nonatomic) IBOutlet UISlider *progress; /**< 进度条 */
    @property (nonatomic, assign) CGFloat songTimes; /**< 歌曲总时间 */
    
    @end
    
    @implementation ViewController
    
    {
        CABasicAnimation* _rotationAnimation; /**< 歌手图片旋转动画 */
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 歌手图片
        UIImageView *playerImageView = [[UIImageView alloc] init];
        playerImageView.y = 50;
        playerImageView.size = CGSizeMake(kImageViewWidth, kImageViewWidth);
        playerImageView.centerX = self.view.centerX;
        playerImageView.image = [UIImage imageNamed:@"xufei.jpg"];
        playerImageView.layer.cornerRadius = kImageViewWidth * 0.5;
        playerImageView.layer.masksToBounds = YES;
        [self.view addSubview:playerImageView];
        self.playerImageView = playerImageView;
        
        // 歌手图片动画效果
        _rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        _rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2];
        _rotationAnimation.duration = kTimerInterval;
        _rotationAnimation.cumulative = YES;
        _rotationAnimation.repeatCount = CGFLOAT_MAX; // 设置旋转次数
        
        // 监听进度条的手动改变
        [self.progress addTarget:self action:@selector(progressValueChage) forControlEvents:UIControlEventValueChanged];
    }
    
    /**
     *  播放
     *
     *  @param sender
     */
    - (IBAction)play:(UIButton *)sender {
        sender.showsTouchWhenHighlighted = YES;
        [self.player play];
        // 旋转歌手图片
        [self imageViewRotate];
    }
    
    /**
     *  播放
     *
     *  @param sender
     */
    - (IBAction)pause:(UIButton *)sender {
        [self.player pause];
        [self.playerImageView.layer removeAnimationForKey:@"rotationAnimation"];
    }
    
    #pragma mark - 自定义方法
    
    /**
     *  歌手图片旋转动画
     */
    - (void)imageViewRotate {
        [self.playerImageView.layer addAnimation:_rotationAnimation forKey:@"rotationAnimation"];
    }
    
    /**
     *  时间转换
     *
     *  @param time 秒数
     *
     *  @return
     */
    - (NSString *)convertStringWithTime:(float)time {
        if (isnan(time)) time = 0.f;
        int min = time / 60.0;
        int sec = time - min * 60;
        NSString * minStr = min > 9 ? [NSString stringWithFormat:@"%d",min] : [NSString stringWithFormat:@"0%d",min];
        NSString * secStr = sec > 9 ? [NSString stringWithFormat:@"%d",sec] : [NSString stringWithFormat:@"0%d",sec];
        NSString * timeStr = [NSString stringWithFormat:@"%@:%@",minStr, secStr];
        return timeStr;
    }
    
    #pragma mark - KVO
    /**
     *  观察者监听方法
     *
     *  @param keyPath
     *  @param object
     *  @param change
     *  @param context  
     */
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:@"songTime"]) {
            self.songTimes = self.player.songTime;
            self.songTimeLabel.text = [self convertStringWithTime:self.player.songTime];
        } else if ([keyPath isEqualToString:@"currentTime"]) {
            self.currentTime.text = [self convertStringWithTime:self.player.currentTime];
            CGFloat pro = self.player.currentTime / self.songTimes; /**< 播放进度 */
            self.progress.value = pro;
        }
    }
    
    /**
     *  进度条改变操作
     */
    - (void)progressValueChage {
        CGFloat currentTime = self.songTimes * self.progress.value;
        [self.player changeProgress:currentTime];
    }
    
    #pragma mark - 懒加载
    
    /**
     *  播放器
     *
     *  @return
     */
    - (GPPlayer *)player {
        if (_player == nil) {
            _player = [[GPPlayer alloc] init];
            [_player addObserver:self forKeyPath:@"songTime" options:NSKeyValueObservingOptionNew context:nil];
            [_player addObserver:self forKeyPath:@"currentTime" options:NSKeyValueObservingOptionNew context:nil];
        }
        
        return _player;
    }
    
    @end
    

    GPPlayer.h

    @interface GPPlayer : NSObject
    
    @property (nonatomic, assign) CGFloat songTime; /**< 歌曲总时间 */
    @property (nonatomic, assign) CGFloat currentTime; /**< 当前播放时间 */
    
    /**
     *  播放音乐
     */
    - (void)play;
    
    /**
     *  暂停播放
     */
    - (void)pause;
    
    /**
     *  手动改变播放进度
     */
    - (void)changeProgress:(CGFloat)second;
    
    @end
    

    GPPlayer.m

    #import "GPPlayer.h"
    #import <AVFoundation/AVFoundation.h>
    
    @interface GPPlayer ()
    
    @property (nonatomic, strong) AVPlayer *player; /**< 播放器 */
    @property (nonatomic, strong) AVPlayerItem *songItem;
    @property (nonatomic, strong) id timeObserve;
    
    @end
    
    @implementation GPPlayer
    
    - (instancetype)init {
        if (self = [super init]) {
            [self setup];
        }
        
        return self;
    }
    
    /**
     *  初始化播放器
     */
    - (void)setup {
        NSURL *url = [NSURL URLWithString:@"http://mr7.doubanio.com/3fd082ae3370d22e48e300ab1d5d6590/1/fm/song/p190415_128k.mp4"];
        self.songItem = [AVPlayerItem playerItemWithURL:url];
        self.player = [AVPlayer playerWithPlayerItem:self.songItem];
        [self addObserver];
    }
    
    /**
     *  添加观察者
     */
    - (void)addObserver {
        // 监听媒体资源的状态
        [self.songItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
        // 监听媒体资源缓冲情况
        [self.songItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
        __weak typeof(self) weakSelf = self;
        // 监听播放进度
        self.timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            CGFloat current = CMTimeGetSeconds(time);
            weakSelf.currentTime = current;
        }];
        // 监听播放是否完成
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerFinish:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    }
    
    /**
     *  KVO监听方法
     *
     *  @param keyPath
     *  @param object
     *  @param change
     *  @param context
     */
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        AVPlayerItem *songItem = object;
        if ([keyPath isEqualToString:@"status"]) {
            // 媒体加载
            switch (self.player.status) {
                case AVPlayerStatusFailed:
                    NSLog(@"KVO:加载失败,网络或者服务器出现问题");
                    break;
                    case AVPlayerStatusReadyToPlay:
                    NSLog(@"KVO:准备完毕,可以播放");
                    break;
                    case AVPlayerStatusUnknown:
                    NSLog(@"KVO:未知状态");
                    break;
            }
        } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
            // 媒体总时长
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                self.songTime = CMTimeGetSeconds(songItem.duration); // 获取到媒体的总时长
            });
            // 媒体缓冲
            NSArray *array=songItem.loadedTimeRanges;
            CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
            float startSeconds = CMTimeGetSeconds(timeRange.start);
            float durationSeconds = CMTimeGetSeconds(timeRange.duration);
            NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
            NSLog(@"共缓冲:%.2f, %lf",totalBuffer, CMTimeGetSeconds(songItem.duration));
        }
    }
    
    /**
     *  监听播放器播放完成
     *
     *  @param sender 
     */
    - (void)playerFinish:(NSNotification *)sender {
        NSLog(@"播放完成");
    }
    
    /**
     *  手动改变进度
     *
     *  @param second 当前播放时间
     */
    - (void)changeProgress:(CGFloat)second {
        [self.player pause];
        [self.player seekToTime:CMTimeMakeWithSeconds(second, NSEC_PER_SEC) completionHandler:^(BOOL finished) {
            [self.player play];
        }];
    }
    
    #pragma mark - 播放器操作
    
    /**
     *  播放音乐
     */
    - (void)play {
        [self.player play];
    }
    
    /**
     *  暂停播放
     */
    - (void)pause {
        [self.player pause];
    }
    
    @end
    

    相关文章

      网友评论

      本文标题:AVPlayer 制作一个简单的在线音乐播放器

      本文链接:https://www.haomeiwen.com/subject/mpghsttx.html