当前位置:首页 > CN2资讯 > 正文内容

ios AVAudioPlayer 播放网络连接 ios网络播放器

4天前CN2资讯


iOS开发之网络音乐播放器(SC音乐)(一)

前言

一直都想做一款自己的网络音乐播放器,两个月前做了一个swift版的网络音乐播放器,但是那个播放器数据来源于我自己用VPS和nginx搭建的服务器,所有的文件都要自己准备,包括mp3、歌词、专辑图片等,非常麻烦,有兴趣的可以跟我要源码。现在这款音乐播放器数据是来源于百度音乐,前前后后花了一个多星期搞定,网上有一些音乐网站的API,有兴趣的同学可以去查一下。

正题



一、播放控制



SC音乐用的是AVPlayer,这个库是苹果自带的视频库,也可以播放音频,可以支持边播放边缓存,使用也比较简单。详细看苹果官网介绍:https://developer.apple.com/documentation/avfoundation/avplayer。这里介绍一下要用到的东西。我们知道,播放器要有播放、暂停、上一曲、下一曲的功能,还要知道播放总时间,当前时间,播放状态,能够从歌曲的任意时间点开始播放。在AVPlayer库中:

play ---- 播放

pause ---- 暂停

rate ---- 播放状态(0.0代表当前状态是暂停, 1.0代表当前状态是播放)

seekToTime ---- 从某个时间点开始播放(拖动进度条用到)

duration ---- 歌曲总时间

currentTime ---- 当前播放时间

上一曲和下一曲可以通过改变歌曲url来实现。

初始化一个AVPlayer需要一个playItem,所以先初始化一个playItem,再用这个playItem去实例化一个play,具体代码:

MusicPlayerManager.h

// // MusicPlayerManager.h // BaiduMusic // // Created by 凌 陈 on 8/21/17. // Copyright © 2017 凌 陈. All rights reserved. // #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface MusicPlayerManager : NSObject typedef enum : NSUInteger { RepeatPlayMode, RepeatOnlyOnePlayMode, ShufflePlayMode, } ShuffleAndRepeatState; @property (nonatomic,strong) AVPlayer *play; @property (nonatomic,strong) AVPlayerItem *playItem; @property (nonatomic,assign) ShuffleAndRepeatState shuffleAndRepeatState; @property (nonatomic,assign) NSInteger playingIndex; + (MusicPlayerManager *)sharedManager; -(void) setPlayItem: (NSString *)songURL; -(void) setPlay; -(void) startPlay; -(void) stopPlay; -(void) play: (NSString *)songURL; @end

MusicPlayerManager.m

// // MusicPlayerManager.m // BaiduMusic // // Created by 凌 陈 on 8/21/17. // Copyright © 2017 凌 陈. All rights reserved. // #import "MusicPlayerManager.h" @implementation MusicPlayerManager static MusicPlayerManager *_sharedManager = nil; +(MusicPlayerManager *)sharedManager { @synchronized( [MusicPlayerManager class] ){ if(!_sharedManager) _sharedManager = [[self alloc] init]; return _sharedManager; } return nil; } -(void) setPlayItem: (NSString *)songURL { NSURL * url = [NSURL URLWithString:songURL]; _playItem = [[AVPlayerItem alloc] initWithURL:url]; } -(void) setPlay { _play = [[AVPlayer alloc] initWithPlayerItem:_playItem]; } -(void) startPlay { [_play play]; } -(void) stopPlay { [_play pause]; } -(void) play: (NSString *)songURL { [self setPlayItem:songURL]; [self setPlay]; [self startPlay]; } @end


将一首歌的url传进play方法就可以实现播放音乐了。上一曲下一曲只是改变一下歌曲的url就可以实现。

歌曲总时长:

_play.currentItem.duration

当前播放时间:

_play.currentTime

从某个时间点开始播放:

//播放器定位到对应的位置

CMTime targetTime = CMTimeMake((int64_t)(currentTime), 1);

[musicPlayer.play seekToTime:targetTime];

播放状态:

//播放或者暂停按键按下,要判断播放状态 if (_play.rate == 0) { // 当前状态为暂停 // 下面要执行播放的代码 } else { // 当前状态为播放 // 下面要执行暂停的代码 }

监管播放(更新播放进度条和当前时间):

_playerTimeObserver = [musicPlayer.play addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { // 这里一秒进来一次,可以更新时间、播放进度条和歌词 // 需要注意的是_playerTimeObserver必须要每首歌播放结束后清掉,不清会有问题。 }

播放结束通知:

// 歌曲播放结束后会调用自定义的方法finishedPlaying [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishedPlaying) name:AVPlayerItemDidPlayToEndTimeNotification object:_play.currentItem];

二、获取百度音乐数据



百度音乐的全接口:http://tingapi.ting.baidu.com/v1/restserver/ting。所有的数据都是以这个为开头,后面加一些其他东西。

可以请求到的数据有很多,这里只说几个:

一、获取歌曲列表(新歌榜、热歌榜、经典老歌榜等)

例:method=baidu.ting.billboard.billList&type=1&size=10&offset=0

完整的请求地址:http://tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&version=2.1.0&method=baidu.ting.billboard.billList&format=json&type=1&offset=0&size=100 (前100热门歌曲,要获取哪个榜只需要改变一下type的值就行了)

参数: type = 1-新歌榜,2-热歌榜,11-摇滚榜,12-爵士,16-流行,21-欧美金曲榜,22-经典老歌榜,23-情歌对唱榜,24-影视金曲榜,25-网络歌曲榜

size = 10 //返回条目数量

offset = 0 //获取偏移

获取到的数据如截图所示:



我们只需要“song_list”里面的数据,点开后发现“song_list“就是一个字典, 继续点开[0],里面是一首歌的信息,包括歌名、歌手名、专辑名等等,但是并没有歌曲url,别急这个要另外获取,需要用到这里的"song_id".




二、获取歌曲url

例:method=baidu.ting.song.lry&songid=877578

完整的请求地址:http://tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&version=2.1.0&method=baidu.ting.song.play&songid=877578

参数:songid = 877578 //假设这个是歌曲id

获取到的数据如图所示:



我们只关注”bitrate“中的数据,点开发现里面有”file_link“,这个就是歌曲的url:


网络请求我用大名鼎鼎的AFNetworking,获取到的数据解析我用MJExtension,主要将数据转成NSArray,将这两个库拉入自己的工程,添加头文件#import "AFNetworking.h"   #import "MJExtension.h"即可。

// 获取歌曲信息请求

// 新歌榜

#pragma mark - 导入数据 - (void)loadNewSongs: (UITableView *)songListTableView { AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; NSString *path = @"http://tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&version=2.1.0&method=baidu.ting.billboard.billList&format=json&type=1&offset=0&size=100";//前100热门歌曲 [manager GET:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { if ([responseObject isKindOfClass:[NSDictionary class]]) { NSArray *array = [responseObject objectForKey:@"song_list"]; _OMSongs = [OMHotSongInfo mj_objectArrayWithKeyValuesArray:array]; // [self reloadTableView:_radioAndMusicTableView]; [songListTableView reloadData]; } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"error--%@",error); }]; }



其它的榜单改一下type的值就可以了。

获取歌曲url请求:

#pragma mark - 获取歌曲url -(void)getSelectedSong: (NSString *)songID index: (long)index { AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; NSString *path = [@"http://tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&version=2.1.0&method=baidu.ting.song.play&songid=" stringByAppendingString:songID]; [manager GET:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { if ([responseObject isKindOfClass:[NSDictionary class]]) { NSDictionary *array = [responseObject objectForKey:@"bitrate"]; self.file_link = [array objectForKey:@"file_link"]; self.file_size = [array objectForKey:@"file_size"]; self.file_duration = [array objectForKey:@"file_duration"]; self.playSongIndex = index; } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"error--%@",error); }]; }





三、播放界面

1、圆形专辑图片(原先是矩形的,需要处理一下)

.h文件

@property (nonatomic, strong) UIView *playControllerView; @property (nonatomic, strong) UIImageView *currentPlaySongImage;




.m文件

// 专辑图片 // 先将专辑图片放到正方形UIImageView, 再将UIImageView圆角设置为正方形边长的一半就得到圆形的UIImageView了 _currentPlaySongImage = [[SCImageView alloc] initWithFrame:CGRectMake(10, 10 , _playControllerView.frame.size.height - 20 , _playControllerView.frame.size.height - 20)]; _currentPlaySongImage.image = [UIImage imageNamed:@"album_default"]; _currentPlaySongImage.clipsToBounds = true; _currentPlaySongImage.layer.cornerRadius = (_playControllerView.frame.size.height - 20) * 0.5; [_playControllerView addSubview:_currentPlaySongImage];




2、专辑图片旋转

我封装了一个UIImageView的旋转动画类,代码如下:

SCImageView.h

// // SCImageView.h // BaiduMusic // // Created by 凌 陈 on 8/22/17. // Copyright © 2017 凌 陈. All rights reserved. // #import <UIKit/UIKit.h> @interface SCImageView : UIImageView -(void) startRotating; -(void) stopRotating; -(void) resumeRotate; @end




SCImageView.m

// // SCImageView.m // BaiduMusic // // Created by 凌 陈 on 8/22/17. // Copyright © 2017 凌 陈. All rights reserved. // #import "SCImageView.h" @implementation SCImageView // 开始旋转 -(void) startRotating { CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; rotateAnimation.fromValue = [NSNumber numberWithFloat:0.0]; rotateAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2]; // 旋转一周 rotateAnimation.duration = 20.0; // 旋转时间20秒 rotateAnimation.repeatCount = MAXFLOAT; // 重复次数,这里用最大次数 [self.layer addAnimation:rotateAnimation forKey:nil]; } // 停止旋转 -(void) stopRotating { CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil]; self.layer.speed = 0.0; // 停止旋转 self.layer.timeOffset = pausedTime; // 保存时间,恢复旋转需要用到 } // 恢复旋转 -(void) resumeRotate { if (self.layer.timeOffset == 0) { [self startRotating]; return; } CFTimeInterval pausedTime = self.layer.timeOffset; self.layer.speed = 1.0; // 开始旋转 self.layer.timeOffset = 0.0; self.layer.beginTime = 0.0; CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; // 恢复时间 self.layer.beginTime = timeSincePause; // 从暂停的时间点开始旋转 } @end




歌曲刚开始播放调用startRotating,开始旋转,点击暂停按键时调用stopRotating停止旋转,点击播放按键时调用resumeRotate恢复旋转(如果调用startRotating则又从头开始旋转)。

3、歌词解析和歌词滚动

首先我们先来看一下lrc文件的格式,如图:


不难发现,除去歌词头部信息后,正文前面[]里面是时间点,右边才是歌词,也就是一段歌词对应一个时间点,这个时间点是开始点,也就是说当歌曲播放到00:49.65这个时间点的时候,歌词应该滚动到“因为在 一千年以后”这段。知道原理就好办了。先解析歌词。

// 解析歌词

.h文件

@property (nonatomic,strong) NSMutableDictionary *mLRCDictinary; @property (nonatomic,strong) NSMutableArray *mTimeArray; @property (nonatomic, assign) BOOL mIsLRCPrepared;



.m文件

-(void) AnalysisLRC: (NSString *)lrcStr { NSString* contentStr = lrcStr; NSArray *lrcArray = [contentStr componentsSeparatedByString:@"\n"]; [songInfo.mLRCDictinary removeAllObjects]; [songInfo.mTimeArray removeAllObjects]; for (NSString *line in lrcArray) { // 首先处理歌词中无用的东西 // [ti:][ar:][al:]这类的直接跳过 if ([line containsString:@"[0"] || [line containsString:@"[1"] || [line containsString:@"[2"] || [line containsString:@"[3"]) { NSArray *lineArr = [line componentsSeparatedByString:@"]"]; NSString *str1 = [line substringWithRange:NSMakeRange(3, 1)]; NSString *str2 = [line substringWithRange:NSMakeRange(6, 1)]; if ([str1 isEqualToString:@":"] && [str2 isEqualToString:@"."]) { NSString *lrcStr = lineArr[1]; NSString *timeStr = [lineArr[0] substringWithRange:NSMakeRange(1, 5)]; [songInfo.mLRCDictinary setObject:lrcStr forKey:timeStr]; [songInfo.mTimeArray addObject:timeStr]; } } else { continue; } } _mIsLRCPrepared = true; [self.tableView reloadData]; }




mLRCDictinary存放配对时间点和歌词段,mTimeArray存放时间点,通过时间点来找到相应的歌词段,不过这个时间点是NSString格式,需要转成int(我的精度要求不高,只到秒,后面的小数没要)

NSString转int

-(int) stringToInt: (NSString *)timeString { NSArray *strTemp = [timeString componentsSeparatedByString:@":"]; int time = [strTemp.firstObject intValue] * 60 + [strTemp.lastObject intValue]; return time; }




int转NSString(显示当前播放时间要用到)

-(NSString *)intToString: (int)needTransformInteger { //实现00:00这种格式播放时间 int wholeTime = needTransformInteger; int min = wholeTime / 60; int sec = wholeTime % 60; NSString *str = [NSString stringWithFormat:@"%02d:%02d", min , sec]; return str; }




歌词滚动:


// songInfo.lrcIndex记录歌词第几行,用currentTime 和 mTimeArray中第几行歌词的时间相比较,大于那个时间歌词tableView滚动到那一行。 if (songInfo.lrcIndex <= songInfo.mLRCDictinary.count - 1) { if ((int)currentTime >= [songInfo stringToInt:songInfo.mTimeArray[songInfo.lrcIndex]]) { _deliverView.midView.midLrcView.currentRow = songInfo.lrcIndex; [_deliverView.midView.midLrcView.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_deliverView.midView.midLrcView.currentRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; [_deliverView.midView.midLrcView.tableView reloadData]; songInfo.lrcIndex = songInfo.lrcIndex + 1; } }


先写到这里,后续还会补充锁屏播放设置,后台播放设置,手势操作等。如果各位觉得还可以,别忘了加个星星哦!

项目运行截图:



iOS开发之网络音乐播放器(SC音乐)(二)

这里附上github地址:https:///Mozartisnotmyname/SCMusic.git


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/20271.html

    分享给朋友:

    “ios AVAudioPlayer 播放网络连接 ios网络播放器” 的相关文章

    Hostodo VPS主机使用体验与性能评测

    当我第一次听说Hostodo时,正是2014年,这家美国VPS主机商在市场上开始崭露头角。印象中,它的低价VPS产品让我感到十分吸引,尤其是在对比市场上其他的主机商时,Hostodo的性价比确实相当有优势。它主营的KVM型和NVMe硬盘的KVM型VPS在当时的市场中并不是常见的选择,迅速吸引了许多站...

    台湾VPS:高效、稳定且安全的虚拟专用服务器解决方案

    台湾VPS,即虚拟专用服务器,是一种通过虚拟化技术将物理服务器划分为多个独立服务器的解决方案。这种服务不仅为用户提供独享的资源和灵活的管理权限,还能够提升性能和效率。在我使用台湾VPS的过程中,它让我深刻体会到这一技术的便利与强大。 台湾VPS的独特之处在于其地理位置和网络基础设施。位于东亚的台湾,...

    NameSilo续费优惠码2024:如何利用优惠码降低域名续费成本

    在域名注册的世界中,NameSilo是一颗璀璨的明珠。成立于2010年的这家公司,迅速赢得了用户的青睐。人们总是追求优质且经济实惠的服务,而NameSilo无疑满足了这一需求。便宜的价格和稳定的服务让它在众多注册商中脱颖而出。 回想起最初使用NameSilo的经历,选择它不仅因为价格的吸引,更多的是...

    Vultr DD Windows安装教程:轻松一步到位

    在云计算越来越流行的今天,Vultr作为一个强大的云服务提供商,吸引了大量用户。对于想在Vultr服务器上安装Windows的用户来说,使用DD命令是一种非常便利的方法。接下来,我将为你详细介绍如何通过这一方式在Vultr上安装Windows。 1.1 使用DD命令直接安装Windows 1.1.1...

    Cloudflare域名解析教程:轻松设置与优化网站性能

    在网络安全和性能优化方面,Cloudflare无疑是一个备受青睐的选择。它不仅为网站提供了强大的DNS解析服务,还有助于提升访问速度和保护网站免受各类网络攻击。我在使用Cloudflare的过程中,逐渐了解了什么是Cloudflare以及它的多种功能和优势。 首先,我们需要知道Cloudflare是...

    银联卡购买:便捷与安全的消费体验

    在这个快速发展的支付时代,银联卡作为一种便捷的支付工具,已经逐渐渗透到我们的日常生活中。我常常发现自己在购物、旅行时,银联卡都能为我省去不少麻烦。通过这张小小的卡片,我可以轻松实现线上和线下消费,四处都能找到它的身影。 银联卡的发展背景十分丰富。自1994年银联成立以来,它不断扩大和完善自己的支付网...