一、音频
iOS里面共有四种专门实现播放音频的方式
1.System Sound Services (系统声音服务) 它是最底层也是最简单的声音服务 一般用来播放一些很小的提示音或者警告音,还有很大的局限性:声音长度要小于30s;格式为IMA4;不能控制播放进度;调用方法后立即播放声音;没有循环播放和立体声音播放;
具体代码如下:
//system sounds services 系统声音服务
-(void)systemSoundsServices{
//得到要播放的资源
NSString* wavPath=[[NSBundle mainBundle] pathForResource:@"1" ofType:@"wav"];
//声明声音的ID 要给我们的歌曲添加系统的ID,让系统可以根据该ID找到该音频资源
SystemSoundID soundsID;
//创建ID
//第一个参数:音频资源的路径
//第二个参数:对soundsID取地址
//把文件路径转换成URL
NSURL* url=[NSURL fileURLWithPath:wavPath];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &soundsID);
//播放带震动
AudioServicesPlayAlertSound(soundsID);
//只播放声音,不带震动效果
AudioServicesPlaySystemSound(soundsID);
}
另一种写法也可以实现
-(void)cfsystemSoundsServices{
CFBundleRef mainBun;
SystemSoundID soundsID;
mainBun=CFBundleGetMainBundle();
CFURLRef soundRef=CFBundleCopyResourceURL(mainBun, CFSTR("1"), CFSTR("wav"), NULL);
AudioServicesCreateSystemSoundID(soundRef, &soundsID);
AudioServicesPlaySystemSound(soundsID);
}
2.OpenAL (跨平台的开源的音频处理接口) 一般用来做开发游戏的音频;详细资料请参考以下两个网址
http://www.devdiv.com/thread-19636-1-1.html
http://www.cocoachina.com/bbs/read.php?tid-112679-page-1.html
3.Audio Queue Services (播放和录制音频服务) 主要用来实现录制音频,为了简化音频文件的处理,通常还需要使用到AudioFileServices,可以参考http://blog.csdn.net/midfar/article/details/7233454
4.AVAudioPlayer (高级音频播放器) 可以支持多种格式的音频播放;可以播放任一长度的音频文件;支持循环播放;可以同步播放多个音频文件;控制播放进度以及从音频的任意一点开始播放。
在这里我们做一个简单的音乐播放器,从上往下,第一个进度条是控制进度的,第二个slider是控制音量的
先在storyBoard中搭建界面,并将要用的控件拖成属性或方法
先导入<AVFoundation/AVFoundation.h> <AudioToolbox/AudioToolbox.h>
#import "ViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import "customSlider.h"
@interface ViewController ()<customSliderDelegate,AVAudioPlayerDelegate>{
AVAudioPlayer* player;
}
@property (weak, nonatomic) IBOutlet customSlider *progressSlider;//自定义的进度条
@property (weak, nonatomic) IBOutlet UILabel *timeLabel;//音频的时长
@property(nonatomic,retain)NSTimer* timer;//定时器
@property (weak, nonatomic) IBOutlet UIImageView *albumImg;//专辑图片
@property (weak, nonatomic) IBOutlet UIImageView *bigImg;//高斯模糊的背景图
@property (weak, nonatomic) IBOutlet UILabel *musicNamaLabel;//音乐的名字
@property (weak, nonatomic) IBOutlet UIButton *pauseOrplayBtn;//暂停或者播放的按钮
@property(nonatomic,retain)NSMutableArray* allMusicArray;//存放所有的歌曲
@property(nonatomic,assign)NSInteger currentIndex;//当前播放的歌曲是第几首
- (IBAction)lastBtn:(UIButton *)sender;//上一曲的点击按钮
- (IBAction)nextBtn:(UIButton *)sender;//下一曲的点击按钮
- (IBAction)playOrPause:(UIButton *)sender;//暂停或者播放的按钮
- (IBAction)volumAction:(UISlider *)sender;//控制声音的slider
@end
@implementation ViewController
//判断当前音乐是否播放完毕
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
[self nextBtn:nil];
}
//存放所有的歌曲
-(NSMutableArray*)allMusicArray{
if (!_allMusicArray) {
_allMusicArray=[[NSMutableArray alloc] init];
}
return _allMusicArray;
}
//创建本地歌曲资源
-(void)creatSourceOfMusic{
NSDictionary* music_Dic1=@{@"name":@"杨承熹-IF YOU.mp3",@"img":@"111.png"};
NSDictionary* music_Dic2=@{@"name":@"薛之谦 - 你还要我怎样.mp3",@"img":@"xzq11.png"};
NSDictionary* music_Dic3=@{@"name":@"Maroon 5-Sugar.mp3",@"img":@"sugar.png"};
NSDictionary* music_Dic4=@{@"name":@"Justin Bieber-Eenie Meenie.mp3",@"img":@"justin.png"};
NSDictionary* music_Dic5=@{@"name":@"香香-恋歌.mp3",@"img":@"lg.png"};
NSDictionary* music_Dic6=@{@"name":@"薛之谦-绅士.mp3",@"img":@"xzq22.png"};
// 添加
[self.allMusicArray addObjectsFromArray:@[music_Dic1,music_Dic2,music_Dic3,music_Dic4,music_Dic5,music_Dic6]];
}
//上一首
- (IBAction)lastBtn:(UIButton *)sender {
//判断当前播放的歌曲是否为第一首歌,如果是,就播放最后一首,如果不是,就-1
if (self.currentIndex==0) {
//是第一首歌,播放最后一首
NSDictionary* dic=self.allMusicArray.lastObject;
[self avAudioPlayerWithName:dic[@"name"]];
self.albumImg.image=[UIImage imageNamed:dic[@"img"]];
self.bigImg.image=[UIImage imageNamed:dic[@"img"]];
self.musicNamaLabel.text=dic[@"name"];
[self playPlayer];
self.currentIndex=self.allMusicArray.count-1;
}else{
self.currentIndex--;
NSDictionary* dic=self.allMusicArray[self.currentIndex];
[self avAudioPlayerWithName:dic[@"name"]];
self.albumImg.image=[UIImage imageNamed:dic[@"img"]];
self.bigImg.image=[UIImage imageNamed:dic[@"img"]];
self.musicNamaLabel.text=dic[@"name"];
[self playPlayer];
}
[self.pauseOrplayBtn setTitle:@"暂停" forState:(UIControlStateNormal)];
}
//下一首
- (IBAction)nextBtn:(UIButton *)sender {
//判断当前播放的歌曲是否为最后一首歌,如果是,就播放第一首
if (self.currentIndex==self.allMusicArray.count-1) {
NSDictionary* dic=self.allMusicArray.firstObject;
[self avAudioPlayerWithName:dic[@"name"]];
self.albumImg.image=[UIImage imageNamed:dic[@"img"]];
self.bigImg.image=[UIImage imageNamed:dic[@"img"]];
self.musicNamaLabel.text=dic[@"name"];
[self playPlayer];
self.currentIndex=0;
}else{
self.currentIndex++;
NSDictionary* dic=self.allMusicArray[self.currentIndex];
[self avAudioPlayerWithName:dic[@"name"]];
self.albumImg.image=[UIImage imageNamed:dic[@"img"]];
self.bigImg.image=[UIImage imageNamed:dic[@"img"]];
self.musicNamaLabel.text=dic[@"name"];
[self playPlayer];
}
[self.pauseOrplayBtn setTitle:@"暂停" forState:(UIControlStateNormal)];
}
//毛玻璃效果
-(void)effectMethod{
UIBlurEffect* blureffect=[UIBlurEffect effectWithStyle:(UIBlurEffectStyleLight)];
UIVisualEffectView* effectView=[[UIVisualEffectView alloc] initWithEffect:blureffect];
effectView.frame=self.view.bounds;
[self.bigImg addSubview:effectView];
}
-(NSTimer*)timer{
if (!_timer) {
//初始化的定时器会自动开启,不需要手动开启
//第一个参数:每隔几秒执行一次定时器回调方法
//第二个参数:定时器的回调方法在哪个类中实现就写哪个类的对象,一般写self
//第三个参数:定时器的回调方法 如果带参数,参数就是定时器对象本身,类似于按钮
//第四个参数:向定时器回调方法中传值,通过_timer.userInfo得到此处所写的值,如果不需要传值,此处直接填nil
//第五个参数:是否重复执行回调方法 YES:重复执行 NO:只执行一次
_timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//暂停timer 在将来的某一时刻开启定时器
[_timer setFireDate:[NSDate distantFuture]];
}
return _timer;
}
//timer的回调方法
-(void)timerAction{
//更新进度条的进度
self.progressSlider.value=player.currentTime;
//时间调整
//剩余的秒数
NSTimeInterval restDuration=player.duration-player.currentTime;
self.timeLabel.text=[self transformWithSeconds:(int)restDuration];
self.albumImg.layer.transform=CATransform3DRotate(self.albumImg.layer.transform, M_PI/60, 0, 0, 1);
}
//暂停
-(void)pausePlayer{
[player pause];
//暂停定时器
[self.timer setFireDate:[NSDate distantFuture]];
}
//播放
-(void)playPlayer{
[player play];
//开启定时器
[self.timer setFireDate:[NSDate distantPast]];
}
//AVAudioPlayer 只能播放本地音乐
-(void)avAudioPlayerWithName:(NSString*)musicName{
//获取资源路径
NSString* musicPath=[[NSBundle mainBundle] pathForResource:musicName ofType:nil];
NSURL* url=[NSURL fileURLWithPath:musicPath];
player=[[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
//指定代理
player.delegate=self;
//设置音量
player.volume=0.5;
//准备播放
[player prepareToPlay];
//获取歌曲的总时长
NSLog(@"111--%f",player.duration);
//将slider的最大值设置为歌曲的总时长
self.progressSlider.maximumValue=(int)player.duration;
//显示总时长
self.timeLabel.text=[self transformWithSeconds:(int)player.duration];
//播放
// [player play];
}
//播放或者暂停
- (IBAction)playOrPause:(UIButton *)sender {
//判断当前是否正在播放歌曲
if (player.isPlaying) {
//说明音乐正在播放,暂停
// [player pause];
[self pausePlayer];
[sender setTitle:@"播放" forState:(UIControlStateNormal)];
}else{
//说明音乐在暂停状态,开始播放
// [player play];
[self playPlayer];
[sender setTitle:@"暂停" forState:(UIControlStateNormal)];
}
}
//调整音量
- (IBAction)volumAction:(UISlider *)sender {
//通过滑竿调整音量
player.volume=sender.value;
}
//调整播放进度
- (IBAction)progressAction:(customSlider *)sender {
if (player.isPlaying) {
//先暂停掉
// [player pause];
[self pausePlayer];
}
//让播放器的当前播放时间设置为和进度条一致
// player.currentTime = sender.value;
}
//手指离开slider的时候会执行的代理方法
-(void)endDraggingWithSlider:(customSlider *)slider{
player.currentTime = slider.value;
// [player play];
[self playPlayer];
[self.pauseOrplayBtn setTitle:@"暂停" forState:(UIControlStateNormal)];
}
//将秒转换为00:00:00的格式
-(NSString*)transformWithSeconds:(int)seconds{
//获取小时
int hours=seconds/3600;
//获取分钟
//除去小时,看剩下多少秒,总秒数对3600取余就是剩余的秒数
int minutes=seconds%3600/60;
//获取秒数
int second=seconds%60;
NSString* hourStr=@"";
NSString* minuteStr=@"";
NSString* secondStr=@"";
if (hours<10) {
hourStr=[NSString stringWithFormat:@"0%d",hours];
}else{
hourStr=[NSString stringWithFormat:@"%d",hours];
}
if (minutes<10) {
minuteStr=[NSString stringWithFormat:@"0%d",minutes];
}else{
minuteStr=[NSString stringWithFormat:@"%d",minutes];
}
if (second<10) {
secondStr=[NSString stringWithFormat:@"0%d",second];
}else{
secondStr=[NSString stringWithFormat:@"%d",second];
}
NSArray* array=@[hourStr,minuteStr,secondStr];
return [array componentsJoinedByString:@":"];
}
- (void)viewDidLoad {
[super viewDidLoad];
//创建资源的方法
[self creatSourceOfMusic];
NSDictionary* dic=self.allMusicArray[0];
[self avAudioPlayerWithName:dic[@"name"]];
self.musicNamaLabel.text=dic[@"name"];
self.albumImg.layer.masksToBounds=YES;
self.albumImg.layer.cornerRadius=75;
self.progressSlider.delegate=self;
[self effectMethod];
}
在这里还需要我们自定义一个slider用来控制音频的播放进度customSlider继承于UISlider
customSlider.h中
#import <UIKit/UIKit.h>
@class customSlider;
@protocol customSliderDelegate<NSObject>
-(void)endDraggingWithSlider:(customSlider*)slider;
@end
@interface customSlider : UISlider
@property(nonatomic,assign)id<customSliderDelegate>delegate;
@end
customSlider.m中
#import "customSlider.h"
@implementation customSlider
//当手指离开滑动条的时候
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
if (self.delegate && [self.delegate respondsToSelector:@selector(endDraggingWithSlider:)]) {
[self.delegate endDraggingWithSlider:self];
}else{
NSLog(@"customSlider的代理未指定或者代理方法未实现");
}
}
这样一个简单的音乐播放器就完成了,但这里只能播放本地的音乐哦😊😊😊😊😊😊😊
二、视频
iOS里面的视频用到的是AVPlayer(包含在AVFoundation)内,与AVAudioPlayer有点类似,但是AVPlayer的功能更加强大,它既可以播放音频也可以播放视频,而且可以直接播放网络音频。
在这里做一个简单的视频播放器,从上往下,第一个进度条是控制进度的,第二个slider是控制音量的
先在storyBoard中搭建界面,并将要用的控件拖成属性或方法
先导入<AVFoundation/AVFoundation.h>
#import "AVPlayerViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface AVPlayerViewController ()
@property(nonatomic,retain)AVPlayer* player;//播放器对象
@property(nonatomic,retain)AVPlayerItem* playerItem;//播放单元
@property(nonatomic,retain)AVPlayerLayer* layer;
@property (weak, nonatomic) IBOutlet UIButton *playOrpause;//播放或暂停
@property (weak, nonatomic) IBOutlet UISlider *progressSlider;//进度条
@property (weak, nonatomic) IBOutlet UIButton *nextAction;//下一个
@property(nonatomic,assign)BOOL isPlaying;//标志当前是否为播放状态
@property(nonatomic,retain)NSMutableArray* allSourcesMArray;//存放所有的视频连接
@property(nonatomic,assign)NSInteger currentIndex;//当前视频播放的是第几个
- (IBAction)volumAction:(UISlider *)sender;//控制音量的slider
- (IBAction)preAction:(UIButton *)sender;//上一个
- (IBAction)playAction:(UIButton *)sender;//播放按钮
@end
@implementation AVPlayerViewController
-(NSMutableArray*)allSourcesMArray{
if (!_allSourcesMArray) {
_allSourcesMArray=[[NSMutableArray alloc] init];
[_allSourcesMArray addObject:@"http://huya-w2.dwstatic.com/56/12/1642/3412476-100-1477091884.mp4"];
[_allSourcesMArray addObject:@"http://www.xiaobuqi.com//d/file/joke/2016-01/1452962161.mp4"];
[_allSourcesMArray addObject:@"http://www.xiaobuqi.com//d/file/joke/2016-01/1452184059.mp4"];
}
return _allSourcesMArray;
}
-(AVPlayerLayer*)layer{
if (!_layer) {
_layer=[[AVPlayerLayer alloc] init];
//射中playerLayer的大小
_layer.frame=self.view.bounds;
//设置视频缩放模式
_layer.videoGravity=AVLayerVideoGravityResizeAspect;
//添加
[self.view.layer addSublayer:_layer];
}
return _layer;
}
-(void)playerMethodWithSourcePath:(NSString*)sourcePath{
//资源URL
if (!sourcePath ||sourcePath.length==0) {
NSLog(@"资源路径不能为空");
return;
}
NSURL* url=nil;
if ([sourcePath hasPrefix:@"http"]) {
//网络资源
url=[NSURL URLWithString:sourcePath];
}else{
//本地资源
url=[NSURL fileURLWithPath:sourcePath];
}
//创建播放单元
self.playerItem=[AVPlayerItem playerItemWithURL:url];
//创建播放器
if (self.player && self.player.currentItem) {
//说明播放器对象已经存在
[self.player.currentItem removeObserver:self forKeyPath:@"status"];
[self.player replaceCurrentItemWithPlayerItem:self.playerItem];
}else{
//说明播放器对象不存在
self.player=[AVPlayer playerWithPlayerItem:self.playerItem];
}
//创建显示的layer对象
if (!self.layer.player) {
//如果layer没有绑定播放器,就给他绑定一个播放器
self.layer.player=self.player;
}
//通过KVO监听item的status的属性的变化
//表示多媒体资源是否准备可以播放
//// self.playerItem.status;
// //KVO key-value-observer 检测对象属性是否发生变化
// //1.只有通过KVC或者属性的setter方法进行赋值,KVO才能监测到
// //2.如果观察同一个对象的同一个属性,在没有移除旧的观察者又添加新的同一个观察者,直接崩溃
// //3.如果没有添加观察者,但又移除观察者,崩溃
// //4.如果观察者被销毁,但是没有移除该观察者,崩溃
//5.如果被观察者被销毁,但是没有移除观察者,崩溃
// //为被观察者的对象添加观察者
// // 第一个参数:观察者对象,KVO的回调方法在哪个类中实现,观察者就是哪个类对象
// //第二个参数:要监测被观察对象的哪个属性,此处就写对应的属性名
// //第三个参数:要观察属性的哪种状态
// //第四个参数:上下文,一般用来为KVO的回调方法传值,如果不需要,直接写nil
[self.playerItem addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew) context:nil];
// 调整音量
self.player.volume=0.5;
//监测播放进度
//第一个参数:从什么地方开始监测
// //第二个参数:将监测过程在哪个队列中进行
// //第三个参数:block 视频播放过程中该block会多次执行,block中的CMTime是当前视频播放的位置
// //防止block中造成循环引用造成内存泄漏
__weak AVPlayerViewController *vc=self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, self.playerItem.currentTime.timescale) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
//更改进度条的进度
vc.progressSlider.value= time.value/time.timescale;
}];
//通过广播中心监测视频播放完成的状态
//第一个参数:观察者,用来监听广播中心的消息
// //第二个参数:回调方法,当我们监听的消息过来就会执行该回调方法,如果带参数,参数类型是广播
// //第三个参数:具体广播的名称
// //第四个参数:广播内容的标记,如果赋值为nil,就是监听该广播下所有的消息,如果是具体的值,那就只监听对应标记的消息
NSNotificationCenter* notification=[NSNotificationCenter defaultCenter];
[notification addObserver:self selector:@selector(palyerFinish:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
//实现广播通知中心的回调方法
-(void)palyerFinish:(NSNotification*)notification{
//广播中可能带的值 userInfo和object都是发广播时赋的值
NSLog(@"%@",notification.userInfo);
NSLog(@"%@",notification.object);
NSLog(@"视频播放完成");
}
//KVO的回调方法
//第一个参数:被观察的属性名
//第二个参数:被观察的对象
//第三个参数:被观察的属性值的新值、旧值都存在该字典中
//第四个参数:上下文,用来接收添加观察者的时候context传递过来的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
//从字典中获取最新状态下的值
AVPlayerItemStatus newStatus=[change[NSKeyValueChangeNewKey] intValue];
switch (newStatus) {
case AVPlayerItemStatusFailed:
NSLog(@"资源有误,无法播放");
break;
case AVPlayerItemStatusUnknown:
NSLog(@"资源未知错误");
break;
case AVPlayerItemStatusReadyToPlay:
[self.player play];
self.isPlaying=YES;
//播放单元获取的duration是一个CMTime类型的结构体 CMTime是一个表示视频的时间单位
//value:视频的总帧数
//timescale:帧/秒 该视频的播放速率 每秒多少帧
if (self.playerItem.duration.timescale) {
self.progressSlider.maximumValue= self.playerItem.duration.value *1.0/self.playerItem.duration.timescale;
}
NSLog(@"资源已准备完成,可以播放");
break;
default:
break;
}
}
//调整音量
- (IBAction)volumAction:(UISlider *)sender {
self.player.volume=sender.value;
}
//调整进度
- (IBAction)progressAction:(UISlider *)sender {
[self.player pause];
//将视频从指定位置开始播放
//第一个参数:从哪个位置开始播放
//第二个参数:调整完成之后要进行的操作
[self.player seekToTime:CMTimeMakeWithSeconds(sender.value, self.playerItem.currentTime.timescale) completionHandler:^(BOOL finished) {
if (finished) {
NSLog(@"视频已播放完成,请结束观看");
[self.player play];
}
}];
}
//上一个
- (IBAction)preAction:(UIButton *)sender {
NSInteger index=self.currentIndex-1;
[self changeSourceWithIndex:index];
[self.playOrpause setTitle:@"暂停" forState:UIControlStateNormal];
}
//播放暂停
- (IBAction)playAction:(UIButton *)sender {
if (self.isPlaying) {
[self.player pause];
self.isPlaying=NO;
[sender setTitle:@"播放" forState:(UIControlStateNormal)];
}else{
[self.player play];
self.isPlaying=YES;
[sender setTitle:@"暂停" forState:(UIControlStateNormal)];
}
}
//下一个
- (IBAction)nextAction:(UIButton *)sender {
NSInteger index=self.currentIndex+1;
[self changeSourceWithIndex:index];
[self.playOrpause setTitle:@"暂停" forState:UIControlStateNormal];
}
//切换资源的方法
-(void)changeSourceWithIndex:(NSInteger)index{
[self.player pause];
if (index<0) {
//在播放最后一首的时候点击了上一曲
[self playerMethodWithSourcePath:self.allSourcesMArray.lastObject];
self.currentIndex=self.allSourcesMArray.count-1;
}else if (index==self.allSourcesMArray.count){
//在播放最后一首的时候,点击了下一曲
[self playerMethodWithSourcePath:self.allSourcesMArray[0]];
self.currentIndex=0;
}else{
//在中间,既不是第一,也不是最后
[self playerMethodWithSourcePath:self.allSourcesMArray[index]];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.isPlaying=NO;
self.currentIndex=0;
// [self avPlayer];
[self playerMethodWithSourcePath:self.allSourcesMArray[0]];
//通知中心发送广播
[[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:@"这里是FM99.9,我是余菲,我在陕西等您" userInfo:@{@"name":@"余菲"}];
}
这样一个简单的视频播放器就完成了,😊😊😊😊
其实思路和音乐播放器差不多