006-音视频

一、音频

  音频播放分为音效播放和音乐播放两大类。前者主要是指的是一些短的音频播放,通常作为点缀音频。对于这一类音频不需要进行进度、循环等控制。后者是一些较长的音频,通常是主音频,对于这些音频的播放通常需要进行精确的控制。在iOS中播放两类音频分别使用AudioToolbox.framework和AVFoundation.framework来完成音效和音乐播放

1.音效(System Sound Service)

 1)说明

  AudioToolbox.framework是一套基于C语言的框架,使用它来播放音效其本质是将段音频注册到系统声音服务(System Sound Service)

  System Sound Service是一种简单、底层的声音播放服务,但是它本身也存在着一些限制:

    a.音频播放时长不超过30秒

    b.数据必须是PCM或者IMA4格式

    c.音频文件必须打包成.caf、.aif、.wav中的一种(注意这是官方文档的说法,实际测试发现一些.mp3格式也可以播放)

    d.只可以播放本地文件(boundle中的音效文件)

 2)使用System Sound Service播放音效的步骤如下:

  a.调用AudioServicesCreateSystemSoundID(CFURLRef  inFileURL, SystemSoundID *outSystemSoundID)函数获得系统声音ID

  b.如果需要监听播放完成操作,则使用AudioServicesAddSystemSoundCompletion(SystemSoundID inSystemSoundID,
CFRunLoopRef  inRunLoop, CFStringRef  inRunLoopMode, AudioServicesSystemSoundCompletionProc  inCompletionRoutine, void *inClientData)方法注册回调函数

  c.调用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID)方法播放音效(后者带有震动效果)

 3)程序实例

 1 // SoundController.m文件
 2 #import "SoundController.h"
 3 #import <AudioToolbox/AudioToolbox.h> // 播放音效需要导入的框架
 4 
 5 @interface SoundController ()
 6 
 7 @end
 8 
 9 @implementation SoundController
10 
11 - (void)viewDidLoad
12 {
13     [super viewDidLoad];
14     
15     [self initUI]; // 界面
16 }
17 
18 #pragma mark - 界面
19 - (void)initUI
20 {
21     self.view.backgroundColor = [UIColor whiteColor];
22     
23     // 播放音效按钮
24     UIButton *playBtn = [UIButton buttonWithType:UIButtonTypeSystem];
25     playBtn.bounds = CGRectMake(0, 0, 150, 40);
26     playBtn.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0);
27     [playBtn setTitle:@"播放音效" forState:UIControlStateNormal];
28     playBtn.tintColor = [UIColor whiteColor];
29     playBtn.backgroundColor = [UIColor grayColor];
30     playBtn.titleLabel.font = [UIFont boldSystemFontOfSize:20];
31     [playBtn addTarget:self action:@selector(playBtnClick:) forControlEvents:UIControlEventTouchUpInside];
32     [self.view addSubview:playBtn];
33 }
34 
35 #pragma mark - 播放音效点击事件
36 - (void)playBtnClick:(UIButton *)playBtn
37 {
38     [self playSoundEffectWithFileName:@"yinxiao.caf"];
39 }
40 
41 #pragma mark - 播放音效文件
42 // 参数:fileName 文件名
43 - (void)playSoundEffectWithFileName:(NSString *)fileName
44 {
45     // 获得音效文件路径(必须是本地文件)
46     NSString *audioFile = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
47     // 根据音效文件路径获得音效文件URL
48     NSURL *fileURL = [NSURL fileURLWithPath:audioFile];
49     
50     // 获得系统声音ID
51     SystemSoundID soundID = 0;
52     
53     // 此函数会将音效文件加入到系统音频服务中并返回一个长整形ID
54     // 参数1:音频文件URL
55     // 参数2:声音ID
56     AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(fileURL), &soundID);
57     
58     // 如果需要在播放完成之后执行某些操作,可以调用下面方法注册一个播放完成的回调函数
59     AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallBack, NULL);
60     
61     // 播放音效文件
62     AudioServicesPlaySystemSound(soundID); // 不带振动效果
63     AudioServicesPlayAlertSound(soundID); // 带有振动效果
64 }
65 
66 #pragma mark - 播放完成回调函数
67 // 参数1:soundID 系统声音ID
68 // 参数2:clientData 回调时传递的数据
69 void soundCompleteCallBack(SystemSoundID soundID,void * clientData){
70     NSLog(@"播放完成...");
71 }
72 
73 @end

2.音乐(AVAudioPlayer)

 1)说明

  如果播放较大的音频文件或者要对音频文件有精准的控制,则System Sound Service可能就很难满足实际需求了,通常这种情况会选择使用AVFoundation.framework中的AVAudioPlayer来实现。AVAudioPlayer可以看成一个播放器,它支持多种音频格式,而且能够进行进度、音量、播放速度等控制。另外与System Sound Service一样,AVFoundation.framework中的AVAudioPlayer也只能是播放本地音乐(boundle里面的音乐文件)

 2)常见属性和方法

属性 说明
@property(readonly, getter=isPlaying) BOOL playing 是否正在播放,只读
@property(readonly) NSUInteger numberOfChannels 音频声道数,只读
@property(readonly) NSTimeInterval duration 音频时长
@property(readonly) NSURL *url 音频文件路径,只读
@property(readonly) NSData *data 音频数据,只读
@property float pan 立体声平衡(-1.0:左声道,0.0:左右声道平衡,1.0:右声道)
@property float volume 音量大小(范围0 - 1.0)
@property BOOL enableRate 是否允许改变播放速率
@property float rate 播放速率(0.5 - 2.0,1.0:正常播放,如要修改播放速率则必须设置enableRate为YES)
@property NSTimeInterval currentTime 当前播放时长
@property(readonly) NSTimeInterval deviceCurrentTime 输出设备播放音频的时间,注意如果播放中被暂停此时间也会继续累加
@property NSInteger numberOfLoops 循环播放次数(0:不循环,小于0:无限循环,大于0:表示循环次数)
@property(readonly) NSDictionary *settings 音频播放设置信息,只读
@property(getter=isMeteringEnabled) BOOL meteringEnabled 是否启用音频测量,默认为NO,一旦启用音频测量可以通过updateMeters方法更新测量值
@property(nonatomic, copy) NSArray *channelAssignments 获得或设置播放声道

 

对象方法 说明
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError 使用文件URL初始化播放器,注意这个URL不能是HTTP URL,AVAudioPlayer不支持加载网络媒体流,只能播放本地文件
- (instancetype)initWithData:(NSData *)data error:(NSError **)outError 使用NSData初始化播放器,注意使用此方法时必须文件格式和文件后缀一致,否则出错,所以相比此方法更推荐使用上述方法或- (instancetype)initWithData:(NSData *)data fileTypeHint:(NSString *)utiString error:(NSError **)outError方法进行初始化
- (BOOL)prepareToPlay; 加载音频文件到缓冲区,注意即使在播放之前音频文件没有加载到缓冲区程序也会隐式调用此方法。
- (BOOL)play; 播放音频文件
- (BOOL)playAtTime:(NSTimeInterval)time 在指定的时间开始播放音频
- (void)pause; 暂停播放
- (void)stop; 停止播放
- (void)updateMeters 更新音频测量值,注意如果要更新音频测量值必须设置meteringEnabled为YES,通过音频测量值可以即时获得音频分贝等信息
- (float)peakPowerForChannel:(NSUInteger)channelNumber; 获得指定声道的分贝峰值,注意如果要获得分贝峰值必须在此之前调用updateMeters方法
- (float)averagePowerForChannel:(NSUInteger)channelNumber 获得指定声道的分贝平均值,注意如果要获得分贝平均值必须在此之前调用updateMeters方法

 

代理方法 说明
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag 音频播放完成
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error 音频解码发生错误

 3)AVAudioPlayer的使用步骤

  a.初始化AVAudioPlayer对象,此时通常指定本地文件路径

  b.设置播放器属性,例如重复次数、音量大小等

  c.调用play方法播放

 4)代码示例

  当然由于AVAudioPlayer一次只能播放一个音频文件,所有上一曲、下一曲其实可以通过创建多个播放器对象来完成,这里暂不实现。播放进度的实现主要依靠一个定时器实时计算当前播放时长和音频总时长的比例

  1 // MusicController.m文件
  2 #import "MusicController.h"
  3 #import <AVFoundation/AVFoundation.h>  // 需要导入的框架
  4 
  5 @interface MusicController ()<AVAudioPlayerDelegate>
  6 
  7 #pragma mark - 音乐播放器
  8 @property (nonatomic, strong) AVAudioPlayer *audioPlayer;
  9 #pragma mark - 播放进度
 10 @property (nonatomic, strong) UIProgressView *progressView;
 11 #pragma mark - 播放/暂停
 12 @property (nonatomic, strong) UIButton *playOrPauseBtn;
 13 #pragma mark - 定时器
 14 @property (nonatomic, strong) NSTimer *timer;
 15 
 16 @end
 17 
 18 @implementation MusicController
 19 
 20 #pragma mark - 懒加载
 21 // 1.audioPlayer
 22 - (AVAudioPlayer *)audioPlayer
 23 {
 24     if (!_audioPlayer) {
 25         // 本地音乐文件
 26         NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"演员.mp3" ofType:nil];
 27         NSURL *url = [NSURL fileURLWithPath:urlStr];
 28         NSError *error = nil;
 29         // 初始化播放器
 30         // 注意:这里的URL只能是文件路径,也就是只能是本地音乐文件;不支持HTTP URL,也就是网络歌曲
 31         _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
 32         // 设置播放器属性
 33         _audioPlayer.numberOfLoops = 0; // 设置为0不循环
 34         _audioPlayer.delegate = self;
 35         [_audioPlayer prepareToPlay]; // 加载音频文件到缓存
 36         if (error) {
 37             NSLog(@"初始化播放器过程发生错误,错误信息:%@", error.localizedDescription);
 38             return nil;
 39         }
 40     }
 41     return _audioPlayer;
 42 }
 43 // 2.timer
 44 - (NSTimer *)timer
 45 {
 46     if (!_timer) {
 47         _timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
 48         // 加入到运行循环
 49         [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
 50     }
 51     return _timer;
 52 }
 53 
 54 - (void)viewDidLoad
 55 {
 56     [super viewDidLoad];
 57    
 58     [self initUI]; // 界面
 59 }
 60 
 61 #pragma mark - 界面
 62 - (void)initUI
 63 {
 64     // playOrPauseBtn
 65     _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeSystem];
 66     _playOrPauseBtn.frame = CGRectMake(100, 100, self.view.bounds.size.width - 100 * 2, 60);
 67     [_playOrPauseBtn setTitle:@"PLAY" forState:UIControlStateNormal];
 68     [_playOrPauseBtn setTitle:@"PAUSE" forState:UIControlStateSelected];
 69     _playOrPauseBtn.tintColor = [UIColor redColor];
 70     [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnClick:) forControlEvents:UIControlEventTouchUpInside];
 71     [self.view addSubview:_playOrPauseBtn];
 72     
 73     // progress
 74     _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(30, CGRectGetMaxY(_playOrPauseBtn.frame) + 10, self.view.bounds.size.width - 30 * 2, 5)];
 75     _progressView.progress = 0;
 76     [self.view addSubview:_progressView];
 77 }
 78 
 79 #pragma mark - 播放/暂停点击事件
 80 - (void)playOrPauseBtnClick:(UIButton *)sender
 81 {
 82     sender.selected = !sender.selected;
 83     
 84     if (sender.isSelected) {
 85         // 播放
 86         [self.audioPlayer play];
 87         // 恢复定时器
 88         self.timer.fireDate=[NSDate distantPast];
 89     
 90     } else {
 91         [self.audioPlayer pause];
 92         // 暂停定时器
 93         // 注意不能调用invalidate方法,此方法会取消,之后无法恢复
 94         self.timer.fireDate = [NSDate distantFuture];
 95     }
 96 }
 97 
 98 #pragma mark - 定时器事件
 99 - (void)updateProgress
100 {
101     float progressValue = self.audioPlayer.currentTime / self.audioPlayer.duration;
102     // 进度设置
103     [self.progressView setProgress:progressValue animated:YES];
104 }
105 
106 #pragma mark - 代理方法<AVAudioPlayerDelegate>
107 // 播放完毕调用
108 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
109 {
110     NSLog(@"音乐播放完成...");
111 }
112 
113 @end

3.音频会话(AVAudioPlayer & AVAudioSession)

如果需要使播放器即使退出到后台也是可以播放的,即支持后台播放功能,我们还需要做以下几件事情:

 1)设置后台运行模式:在plist列表中添加字段“Required background modes”,并且设置item 0 = App plays audio or streams audio/video using AirPlay(其实可以直接通过Xcode在Project Targets-Capabilities-Background Modes中设置)

  这一步的目的是不让应用程序在后台被挂起,而是处于运行状态

 2)设置AVAudioSession的类型为AVAudioSessionCategoryPlayback并且调用setActive::方法启动会话

1     AVAudioSession *audioSession=[AVAudioSession sharedInstance];
2     [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
3     [audioSession setActive:YES error:nil];

  a.在iOS中每一个应用都有一个音频会话,这个会话就通过AVAudioSession来表示。

  b.AVAudioSession属于AVFoundation框架,并且是单例模式设计,通过sharedInstance进行访问。

  c.在使用Apple设备时大家会发现有些应用只要打开其他音频播放就会终止,而有些应用却可以和其他应用同时播放,在多种音频环境中如何去控制播放的方式就是通过音频回话来完成的。

  音频会话的几种会话模式:

 注意:在设置完音频会话类型之后需要调用setActive::方法将会话激活才能起作用。如果设备上已经存在一个正在播放的音频会话,打开我们的应用之后设置了后台播放的会话类型,此时原本正在播放的音频会因我们应用的音频播放而停止,此时,如果希望我们的应用程序在音频播放结束之后,能够继续播放原来的音频,那么我们应该在我们自己的 应用中调用setActive::方法关闭会话。

3)如果需要让应用退出后台支持耳机控制,可以添加远程控制事件(这一步不是后台播放所必须)

代码实例(支持后台播放、支持拔出耳机暂停音乐)

  1 // MusicPlayerController.m文件
  2 #import "MusicPlayerController.h"
  3 #import <AVFoundation/AVFoundation.h>  // 需要导入的框架
  4 
  5 @interface MusicPlayerController ()<AVAudioPlayerDelegate>
  6 
  7 #pragma mark - 音乐播放器
  8 @property (nonatomic, strong) AVAudioPlayer *audioPlayer;
  9 #pragma mark - 播放进度
 10 @property (nonatomic, strong) UIProgressView *progressView;
 11 #pragma mark - 播放/暂停
 12 @property (nonatomic, strong) UIButton *playOrPauseBtn;
 13 #pragma mark - 定时器
 14 @property (nonatomic, strong) NSTimer *timer;
 15 
 16 @end
 17 
 18 @implementation MusicPlayerController
 19 
 20 #pragma mark - 懒加载
 21 // 1.audioPlayer
 22 - (AVAudioPlayer *)audioPlayer
 23 {
 24     if (!_audioPlayer) {
 25         // 本地音乐文件
 26         NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"演员.mp3" ofType:nil];
 27         NSURL *url = [NSURL fileURLWithPath:urlStr];
 28         NSError *error = nil;
 29         // 初始化播放器
 30         // 注意:这里的URL只能是文件路径,也就是只能是本地音乐文件;不支持HTTP URL,也就是网络歌曲
 31         _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
 32         // 设置播放器属性
 33         _audioPlayer.numberOfLoops = 0; // 设置为0不循环
 34         _audioPlayer.delegate = self;
 35         [_audioPlayer prepareToPlay]; // 加载音频文件到缓存
 36         if (error) {
 37             NSLog(@"初始化播放器过程发生错误,错误信息:%@", error.localizedDescription);
 38             return nil;
 39         }
 40         
 41         // 设置后台播放模式
 42         AVAudioSession *audioSession = [AVAudioSession sharedInstance];
 43         [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
 44         [audioSession setActive:YES error:nil];
 45         // 添加通知,拔出耳机后暂停播放
 46         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
 47     }
 48     return _audioPlayer;
 49 }
 50 // 2.timer
 51 - (NSTimer *)timer
 52 {
 53     if (!_timer) {
 54         _timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
 55         // 加入到运行循环
 56         [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
 57     }
 58     return _timer;
 59 }
 60 
 61 - (void)viewDidLoad
 62 {
 63     [super viewDidLoad];
 64     
 65     [self initUI]; // 界面
 66 }
 67 
 68 #pragma mark - 显示当前视图控制器时,注册远程事件
 69 - (void)viewWillAppear:(BOOL)animated
 70 {
 71     [super viewWillAppear:animated];
 72     // 开启远程控制
 73     [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
 74     // 作为第一响应者
 75     // [self becomeFirstResponder];
 76 }
 77 
 78 #pragma mark - 当前视图消失时取消远程控制
 79 - (void)viewWillDisappear:(BOOL)animated
 80 {
 81     [super viewWillDisappear:animated];
 82     // 结束远程控制
 83     [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
 84     // 失去第一响应
 85     // [self resignFirstResponder];
 86 }
 87 
 88 #pragma mark - 界面
 89 - (void)initUI
 90 {
 91     self.view.backgroundColor = [UIColor whiteColor];
 92     
 93     // playOrPauseBtn
 94     _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeSystem];
 95     _playOrPauseBtn.frame = CGRectMake(100, 100, self.view.bounds.size.width - 100 * 2, 60);
 96     [_playOrPauseBtn setTitle:@"PLAY" forState:UIControlStateNormal];
 97     [_playOrPauseBtn setTitle:@"PAUSE" forState:UIControlStateSelected];
 98     _playOrPauseBtn.tintColor = [UIColor redColor];
 99     [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnClick:) forControlEvents:UIControlEventTouchUpInside];
100     [self.view addSubview:_playOrPauseBtn];
101     
102     // progress
103     _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(30, CGRectGetMaxY(_playOrPauseBtn.frame) + 10, self.view.bounds.size.width - 30 * 2, 5)];
104     _progressView.progress = 0;
105     [self.view addSubview:_progressView];
106 }
107 
108 #pragma mark - 播放/暂停点击事件
109 - (void)playOrPauseBtnClick:(UIButton *)sender
110 {
111     sender.selected = !sender.selected;
112     
113     if (sender.isSelected) {
114         // 播放
115         [self play];
116         
117     } else {
118         // 暂停
119         [self pause];
120     }
121 }
122 
123 #pragma mark - 播放
124 - (void)play
125 {
126     // 播放
127     [self.audioPlayer play];
128     // 恢复定时器
129     self.timer.fireDate=[NSDate distantPast];
130 }
131 
132 #pragma mark - 暂停
133 - (void)pause
134 {
135     // 暂停
136     [self.audioPlayer pause];
137     // 暂停定时器
138     // 注意不能调用invalidate方法,此方法会取消,之后无法恢复
139     self.timer.fireDate = [NSDate distantFuture];
140 }
141 
142 #pragma mark - 定时器事件
143 - (void)updateProgress
144 {
145     float progressValue = self.audioPlayer.currentTime / self.audioPlayer.duration;
146     // 进度设置
147     [self.progressView setProgress:progressValue animated:YES];
148 }
149 
150 #pragma mark - 代理方法<AVAudioPlayerDelegate>
151 // 播放完毕调用
152 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
153 {
154     NSLog(@"音乐播放完成...");
155     // 根据实际情况播放完成可以将会话关闭,其他音频应用继续播放
156     [[AVAudioSession sharedInstance] setActive:NO error:nil];
157 }
158 
159 #pragma mark - 通知事件
160 // 一旦输出改变则执行此方法
161 - (void)routeChange:(NSNotification *)notification
162 {
163     NSDictionary *dict = notification.userInfo;
164     int changeReason = [dict[AVAudioSessionRouteChangeReasonKey] intValue];
165     // 等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示旧输出不可用
166     if (changeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
167         AVAudioSessionRouteDescription *routeDescription = dict[AVAudioSessionRouteChangePreviousRouteKey];
168         AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
169         // 原设备为耳机则暂停
170         if ([portDescription.portType isEqualToString:@"Headphones"]) {
171             // 点击播放/暂停按键
172             [self playOrPauseBtnClick:_playOrPauseBtn];
173         }
174     }
175 }
176 
177 #pragma mark - 销毁方法
178 - (void)dealloc
179 {
180     [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
181 }
182 
183 @end

4.播放音乐库中的音乐(MPMusicPlayerController)

 1)说明

  MPMusicPlayerController是属于MediaPlayer.frameowork框架

 2)属性和方法

属性 说明
@property (nonatomic, readonly) MPMusicPlaybackState playbackState

播放器状态,枚举类型:
MPMusicPlaybackStateStopped:停止播放

MPMusicPlaybackStatePlaying:正在播放
MPMusicPlaybackStatePaused:暂停播放
MPMusicPlaybackStateInterrupted:播放中断
MPMusicPlaybackStateSeekingForward:向前查找
MPMusicPlaybackStateSeekingBackward:向后查找

@property (nonatomic) MPMusicRepeatMode repeatMode 重复模式,枚举类型:
MPMusicRepeatModeDefault:默认模式,使用用户的首选项(系统音乐程序设置)
MPMusicRepeatModeNone:不重复
MPMusicRepeatModeOne:单曲循环
MPMusicRepeatModeAll:在当前列表内循环
@property (nonatomic) MPMusicShuffleMode shuffleMode 随机播放模式,枚举类型:
MPMusicShuffleModeDefault:默认模式,使用用户首选项(系统音乐程序设置)
MPMusicShuffleModeOff:不随机播放
MPMusicShuffleModeSongs:按歌曲随机播放
MPMusicShuffleModeAlbums:按专辑随机播放
@property (nonatomic, copy) MPMediaItem *nowPlayingItem 正在播放的音乐项
@property (nonatomic, readonly) NSUInteger indexOfNowPlayingItem 当前正在播放的音乐在播放队列中的索引
@property(nonatomic, readonly) BOOL isPreparedToPlay 是否准好播放准备
@property(nonatomic) NSTimeInterval currentPlaybackTime 当前已播放时间,单位:秒
@property(nonatomic) float currentPlaybackRate 当前播放速度,是一个播放速度倍率,0表示暂停播放,1代表正常速度

 

类方法 说明
+ (MPMusicPlayerController *)applicationMusicPlayer; 获取应用播放器,注意此类播放器无法在后台播放
+ (MPMusicPlayerController *)systemMusicPlayer 获取系统播放器,支持后台播放

 

对象方法 说明
- (void)setQueueWithQuery:(MPMediaQuery *)query 使用媒体队列设置播放源媒体队列
- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection 使用媒体项集合设置播放源媒体队列
- (void)skipToNextItem 下一曲
- (void)skipToBeginning 从起始位置播放
- (void)skipToPreviousItem 上一曲
- (void)beginGeneratingPlaybackNotifications 开启播放通知,注意不同于其他播放器,MPMusicPlayerController要想获得通知必须首先开启,默认情况无法获得通知
- (void)endGeneratingPlaybackNotifications 关闭播放通知
- (void)prepareToPlay 做好播放准备(加载音频到缓冲区),在使用play方法播放时如果没有做好准备回自动调用该方法
- (void)play 开始播放
- (void)pause 暂停播放
- (void)stop 停止播放
- (void)beginSeekingForward 开始向前查找(快进)
- (void)beginSeekingBackward 开始向后查找(快退)
- (void)endSeeking 结束查找

  

通知 说明
(注意:要想获得MPMusicPlayerController通知必须首先调用beginGeneratingPlaybackNotifications开启通知)
MPMusicPlayerControllerPlaybackStateDidChangeNotification 播放状态改变
MPMusicPlayerControllerNowPlayingItemDidChangeNotification 当前播放音频改变
MPMusicPlayerControllerVolumeDidChangeNotification 声音大小改变
MPMediaPlaybackIsPreparedToPlayDidChangeNotification 准备好播放

 

 3)MPMusicPlayerController

  a.MPMusicPlayerController有两种播放器:

    applicationMusicPlayer:应用退出后,音乐播放会自动停止

    systemMusicPlayer:应用停止后不会退出播放状态

  b.MPMusicPlayerController加载音乐不同于AVAudioPlayer

    AVAudioPlayer是通过一个文件路径来加载

    MPMusicPlayerController是需要一个播放队列来加载

  c.MPMusicPlayerController提供了两个方法来加载播放队列

    - (void)setQueueWithQuery:(MPMediaQuery *)query;

    - (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection;

    正是因为MPMusicPlayerController的播放音频来源是一个队列,因此它支持上一曲、下一曲等操作

  d.MPMusicPlayerController如何获取MPMediaQueue或者MPMediaItemCollection

    MPMediaQueue对象有以下方法用来获取媒体队列:

    + (MPMediaQuery *)albumsQuery;

    + (MPMediaQuery *)artistsQuery;

    + (MPMediaQuery *)songsQuery;

    + (MPMediaQuery *)playlistsQuery;

    + (MPMediaQuery *)podcastsQuery;

    + (MPMediaQuery *)audiobooksQuery;

    + (MPMediaQuery *)compilationsQuery;

    + (MPMediaQuery *)composersQuery;

    + (MPMediaQuery *)genresQuery;

  e.设置音乐来源

    MPMediaQueue:- (void)setQueueWithQuery:(MPMediaQuery *)query;

    MPMediaItemCollection:- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection;

  f.MPMediaPickerController

    如果希望用户自己来选择将要播放的音乐,这个时候可以使用MPMediaPickerController

    MPMediaPickerController是一个视图控制器,类似于UIImagePickerController

    选择完播放来源后可以在其代理方法中获得MPMediaItemCollection对象

  g.MPMediaItem(媒体文件对象)

    无论通过哪一种方式获得MPMusicPlayerController的媒体源,我们都可以通过MPMediaItem对象获得媒体文件的信息

    一个MPMediaItem对象对应着一个媒体文件,通过它可以访问媒体标题、专辑名称、专辑封面、音乐时长等等

    无论是MPMediaQueue,还是MPMediaItemCollection都有一个items属性,这是一个装有MPMediaItem的数组,通过这个属性可以获得MPMediaItem对象

 4)代码实例

  1 // MusicQueueController.m文件
  2 #import "MusicQueueController.h"
  3 #import <MediaPlayer/MediaPlayer.h> // 导入框架
  4 
  5 @interface MusicQueueController ()<MPMediaPickerControllerDelegate>
  6 
  7 // 媒体选择控制器
  8 @property (nonatomic, strong) MPMediaPickerController *mediaPicker;
  9 // 音乐播放器
 10 @property (nonatomic, strong) MPMusicPlayerController *musicPlayer;
 11 
 12 #pragma mark - 界面上的按钮
 13 @property (nonatomic, strong) UIButton *selectBtn; // 选择音乐来源按钮
 14 @property (nonatomic, strong) UIButton *playBtn; // 播放按钮
 15 @property (nonatomic, strong) UIButton *pauseBtn; // 暂停按钮
 16 @property (nonatomic, strong) UIButton *stopBtn; // 停止按钮
 17 @property (nonatomic, strong) UIButton *nextBtn; // 下一曲按钮
 18 @property (nonatomic, strong) UIButton *prevBtn; // 上一曲按钮
 19 
 20 @end
 21 
 22 @implementation MusicQueueController
 23 
 24 #pragma mark - 懒加载
 25 // 1.mediaPicker
 26 - (MPMediaPickerController *)mediaPicker
 27 {
 28     if (!_mediaPicker) {
 29         // 初始化媒体选择器
 30         // 这里设置媒体类型为音乐,其实这里也可以选择视频、广播等
 31         _mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeMusic];
 32         // 允许多选
 33         _mediaPicker.allowsPickingMultipleItems = YES;
 34         // 显示icloud选项
 35         // _mediaPicker.showsCloudItems = YES;
 36         _mediaPicker.prompt = @"请选择要播放的音乐";
 37         // 设置选择器代理
 38         _mediaPicker.delegate = self;
 39     }
 40     return _mediaPicker;
 41 }
 42 // 2.musicPlayer
 43 - (MPMusicPlayerController *)musicPlayer
 44 {
 45     if (!_musicPlayer) {
 46         // systemMusicPlayer:应用停止后不会退出播放状态
 47         _musicPlayer = [MPMusicPlayerController systemMusicPlayer];
 48         // 开启通知(否则监控不到MPMusicPlayerController的通知)
 49         [_musicPlayer beginGeneratingPlaybackNotifications];
 50         // 添加通知
 51         [self addNotification];
 52         // 如果不使用MPMediaPickerController可以使用如下方法获得音乐库媒体队列
 53         // [_musicPlayer setQueueWithItemCollection:[self getLocalMediaItemCollection]];
 54     }
 55     return _musicPlayer;
 56 }
 57 
 58 - (void)viewDidLoad
 59 {
 60     [super viewDidLoad];
 61     
 62     [self initUI]; // 界面
 63 }
 64 
 65 #pragma mark - 界面
 66 - (void)initUI
 67 {
 68     self.view.backgroundColor = [UIColor whiteColor];
 69     
 70     // 选择音乐来源按钮
 71     CGFloat selectBtnX = 40;
 72     CGFloat selectBtnY = 80;
 73     CGFloat selectBtnW = self.view.bounds.size.width - 40 * 2;
 74     CGFloat selectBtnH = 40;
 75     _selectBtn = [UIButton buttonWithType:UIButtonTypeCustom];
 76     _selectBtn.frame = CGRectMake(selectBtnX, selectBtnY, selectBtnW, selectBtnH);
 77     [_selectBtn setTitle:@"选择音乐来源" forState:UIControlStateNormal];
 78     _selectBtn.tintColor = [UIColor whiteColor];
 79     _selectBtn.backgroundColor = [UIColor blackColor];
 80     _selectBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
 81     [_selectBtn addTarget:self action:@selector(selectBtnClick:) forControlEvents:UIControlEventTouchUpInside];
 82     [self.view addSubview:_selectBtn];
 83     
 84     // 播放按钮
 85     CGFloat playBtnX = _selectBtn.frame.origin.x;
 86     CGFloat playBtnY = CGRectGetMaxY(_selectBtn.frame) + 10;
 87     CGFloat playBtnW = _selectBtn.bounds.size.width;
 88     CGFloat playBtnH = _selectBtn.bounds.size.height;
 89     _playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
 90     _playBtn.frame = CGRectMake(playBtnX, playBtnY, playBtnW, playBtnH);
 91     [_playBtn setTitle:@"播放" forState:UIControlStateNormal];
 92     _playBtn.tintColor = [UIColor whiteColor];
 93     _playBtn.backgroundColor = [UIColor blackColor];
 94     _playBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
 95     [_playBtn addTarget:self action:@selector(playBtnClick:) forControlEvents:UIControlEventTouchUpInside];
 96     [self.view addSubview:_playBtn];
 97     
 98     // 暂停按钮
 99     CGFloat pauseBtnX = _selectBtn.frame.origin.x;
100     CGFloat pauseBtnY = CGRectGetMaxY(_playBtn.frame) + 10;
101     CGFloat pauseBtnW = _selectBtn.bounds.size.width;
102     CGFloat pauseBtnH = _selectBtn.bounds.size.height;
103     _pauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
104     _pauseBtn.frame = CGRectMake(pauseBtnX, pauseBtnY, pauseBtnW, pauseBtnH);
105     [_pauseBtn setTitle:@"暂停" forState:UIControlStateNormal];
106     _pauseBtn.tintColor = [UIColor whiteColor];
107     _pauseBtn.backgroundColor = [UIColor blackColor];
108     _pauseBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
109     [_pauseBtn addTarget:self action:@selector(pauseBtnClick:) forControlEvents:UIControlEventTouchUpInside];
110     [self.view addSubview:_pauseBtn];
111     
112     // 停止按钮
113     CGFloat stopBtnX = _selectBtn.frame.origin.x;
114     CGFloat stopBtnY = CGRectGetMaxY(_pauseBtn.frame) + 10;
115     CGFloat stopBtnW = _selectBtn.bounds.size.width;
116     CGFloat stopBtnH = _selectBtn.bounds.size.height;
117     _stopBtn = [UIButton buttonWithType:UIButtonTypeCustom];
118     _stopBtn.frame = CGRectMake(stopBtnX, stopBtnY, stopBtnW, stopBtnH);
119     [_stopBtn setTitle:@"停止" forState:UIControlStateNormal];
120     _stopBtn.tintColor = [UIColor whiteColor];
121     _stopBtn.backgroundColor = [UIColor blackColor];
122     _stopBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
123     [_stopBtn addTarget:self action:@selector(stopBtnClick:) forControlEvents:UIControlEventTouchUpInside];
124     [self.view addSubview:_stopBtn];
125     
126     // 下一曲按钮
127     CGFloat nextBtnX = _selectBtn.frame.origin.x;
128     CGFloat nextBtnY = CGRectGetMaxY(_stopBtn.frame) + 10;
129     CGFloat nextBtnW = _selectBtn.bounds.size.width;
130     CGFloat nextBtnH = _selectBtn.bounds.size.height;
131     _nextBtn = [UIButton buttonWithType:UIButtonTypeCustom];
132     _nextBtn.frame = CGRectMake(nextBtnX, nextBtnY, nextBtnW, nextBtnH);
133     [_nextBtn setTitle:@"下一曲" forState:UIControlStateNormal];
134     _nextBtn.tintColor = [UIColor whiteColor];
135     _nextBtn.backgroundColor = [UIColor blackColor];
136     _nextBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
137     [_nextBtn addTarget:self action:@selector(nextBtnClick:) forControlEvents:UIControlEventTouchUpInside];
138     [self.view addSubview:_nextBtn];
139     
140     // 上一曲按钮
141     CGFloat prevBtnX = _selectBtn.frame.origin.x;
142     CGFloat prevBtnY = CGRectGetMaxY(_nextBtn.frame) + 10;
143     CGFloat prevBtnW = _selectBtn.bounds.size.width;
144     CGFloat prevBtnH = _selectBtn.bounds.size.height;
145     _prevBtn = [UIButton buttonWithType:UIButtonTypeCustom];
146     _prevBtn.frame = CGRectMake(prevBtnX, prevBtnY, prevBtnW, prevBtnH);
147     [_prevBtn setTitle:@"上一曲" forState:UIControlStateNormal];
148     _prevBtn.tintColor = [UIColor whiteColor];
149     _prevBtn.backgroundColor = [UIColor blackColor];
150     _prevBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
151     [_prevBtn addTarget:self action:@selector(prevBtnClick:) forControlEvents:UIControlEventTouchUpInside];
152     [self.view addSubview:_prevBtn];
153     
154     // 按钮交互设置
155     _selectBtn.enabled = YES;
156     _playBtn.enabled = NO;
157     _pauseBtn.enabled = NO;
158     _stopBtn.enabled = NO;
159     _nextBtn.enabled = NO;
160     _prevBtn.enabled = NO;
161 }
162 
163 #pragma mark - 点击事件
164 // 1.选择音乐来源
165 - (void)selectBtnClick:(UIButton *)sender
166 {
167     [self presentViewController:self.mediaPicker animated:YES completion:nil];
168 }
169 // 2.播放
170 - (void)playBtnClick:(UIButton *)sender
171 {
172     [self.musicPlayer play];
173 }
174 // 3.暂停
175 - (void)pauseBtnClick:(UIButton *)sender
176 {
177     [self.musicPlayer pause];
178 }
179 // 4.停止
180 - (void)stopBtnClick:(UIButton *)sender
181 {
182     [self.musicPlayer stop];
183 }
184 // 5.下一曲
185 - (void)nextBtnClick:(UIButton *)sender
186 {
187     [self.musicPlayer skipToNextItem];
188 }
189 // 6.上一曲
190 - (void)prevBtnClick:(UIButton *)sender
191 {
192     [self.musicPlayer skipToPreviousItem];
193 }
194 
195 #pragma mark - 直接通过MPMediaQueue获得媒体队列或媒体集合
196 // 1.取得媒体队列
197 - (MPMediaQuery *)getLocalMediaQuery
198 {
199     MPMediaQuery *mediaQueue = [MPMediaQuery songsQuery];
200     for (MPMediaItem *item in mediaQueue.items) {
201         NSLog(@"标题:%@,%@", item.title, item.albumTitle);
202     }
203     return mediaQueue;
204 }
205 // 2.取得媒体集合
206 - (MPMediaItemCollection *)getLocalMediaItemCollection
207 {
208     MPMediaQuery *mediaQueue = [MPMediaQuery songsQuery];
209     NSMutableArray *array = [NSMutableArray array];
210     for (MPMediaItem *item in mediaQueue.items) {
211         [array addObject:item];
212         NSLog(@"标题:%@,%@",item.title,item.albumTitle);
213     }
214     MPMediaItemCollection *mediaItemCollection = [[MPMediaItemCollection alloc]initWithItems:[array copy]];
215     return mediaItemCollection;
216 }
217 
218 #pragma mark - 代理方法<MPMediaPickerController>
219 // 1.选择完成
220 -(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
221 {
222     // 按钮交互设置
223     _selectBtn.enabled = YES;
224     _playBtn.enabled = YES;
225     _pauseBtn.enabled = NO;
226     _stopBtn.enabled = NO;
227     _nextBtn.enabled = YES;
228     _prevBtn.enabled = YES;
229     
230     // 第一个播放音乐
231     MPMediaItem *mediaItem=[mediaItemCollection.items firstObject];
232     // 注意很多音乐信息(标题、专辑、表演者、封面、时长等)都可以通过MPMediaItem的valueForKey:方法得到,但是从iOS7开始都有对应的属性可以直接访问
233     // NSString *title = [mediaItem valueForKey:MPMediaItemPropertyAlbumTitle];
234     // NSString *artist = [mediaItem valueForKey:MPMediaItemPropertyAlbumArtist];
235     // MPMediaItemArtwork *artwork = [mediaItem valueForKey:MPMediaItemPropertyArtwork];
236     //UIImage *image = [artwork imageWithSize:CGSizeMake(100, 100)]; // 专辑图片
237     NSLog(@"标题:%@,表演者:%@,专辑:%@", mediaItem.title, mediaItem.artist, mediaItem.albumTitle);
238     [self.musicPlayer setQueueWithItemCollection:mediaItemCollection];
239     [self dismissViewControllerAnimated:YES completion:nil];
240 }
241 // 2.取消选择
242 -(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker
243 {
244     // 按钮交互设置
245     _selectBtn.enabled = YES;
246     _playBtn.enabled = NO;
247     _pauseBtn.enabled = NO;
248     _stopBtn.enabled = NO;
249     _nextBtn.enabled = NO;
250     _prevBtn.enabled = NO;
251     [self dismissViewControllerAnimated:YES completion:nil];
252 }
253 
254 #pragma mark - 通知
255 // 1.添加通知
256 - (void)addNotification
257 {
258     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
259     [notificationCenter addObserver:self selector:@selector(playbackStateChange:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.musicPlayer];
260 }
261 // 2.播放状态改变通知
262 - (void)playbackStateChange:(NSNotification *)notification
263 {
264     switch (self.musicPlayer.playbackState) {
265         case MPMusicPlaybackStatePlaying:
266             NSLog(@"正在播放...");
267             // 按钮交互设置
268             _selectBtn.enabled = NO;
269             _playBtn.enabled = NO;
270             _pauseBtn.enabled = YES;
271             _stopBtn.enabled = YES;
272             _nextBtn.enabled = YES;
273             _prevBtn.enabled = YES;
274             break;
275         case MPMusicPlaybackStatePaused:
276             NSLog(@"播放暂停.");
277             // 按钮交互设置
278             _selectBtn.enabled = YES;
279             _playBtn.enabled = YES;
280             _pauseBtn.enabled = NO;
281             _stopBtn.enabled = NO;
282             _nextBtn.enabled = YES;
283             _prevBtn.enabled = YES;
284             break;
285         case MPMusicPlaybackStateStopped:
286             NSLog(@"播放停止.");
287             // 按钮交互设置
288             _selectBtn.enabled = YES;
289             _playBtn.enabled = YES;
290             _pauseBtn.enabled = NO;
291             _stopBtn.enabled = NO;
292             _nextBtn.enabled = YES;
293             _prevBtn.enabled = YES;
294             break;
295         default:
296             break;
297     }
298 }
299 
300 #pragma mark - 销毁方法
301 - (void)dealloc
302 {
303     // 结束通知
304     [self.musicPlayer endGeneratingPlaybackNotifications];
305 }
306 
307 @end

5.录音(AVAudioRecorder)

 1)说明

  a.AVAudioRecorder属于AVFoundation框架

  b.AVAudioRecorder是专门处理录音操作的,它同样支持多种音频格式

  c.AVAudioRecorder跟AVAudioPlayer创建有所不同,创建录音机时除了指定的路径外,还须指定录音设置信息(文件格式、采样率、通道数、每个采样点的位数等)

 2)属性和方法

属性 说明
@property(readonly, getter=isRecording) BOOL recording; 是否正在录音,只读
@property(readonly) NSURL *url 录音文件地址,只读
@property(readonly) NSDictionary *settings 录音文件设置,只读
@property(readonly) NSTimeInterval currentTime 录音时长,只读,注意仅仅在录音状态可用
@property(readonly) NSTimeInterval deviceCurrentTime 输入设置的时间长度,只读,注意此属性一直可访问
@property(getter=isMeteringEnabled) BOOL meteringEnabled 是否启用录音测量,如果启用录音测量可以获得录音分贝等数据信息
@property(nonatomic, copy) NSArray *channelAssignments 当前录音的通道

  

对象方法 说明
- (instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError 录音机对象初始化方法,注意其中的url必须是本地文件url,settings是录音格式、编码等设置
- (BOOL)prepareToRecord 准备录音,主要用于创建缓冲区,如果不手动调用,在调用record录音时也会自动调用
- (BOOL)record 开始录音
- (BOOL)recordAtTime:(NSTimeInterval)time 在指定的时间开始录音,一般用于录音暂停再恢复录音
- (BOOL)recordForDuration:(NSTimeInterval) duration 按指定的时长开始录音
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration 在指定的时间开始录音,并指定录音时长
- (void)pause 暂停录音
- (void)stop 停止录音
- (BOOL)deleteRecording 删除录音,注意要删除录音此时录音机必须处于停止状态
- (void)updateMeters 更新测量数据,注意只有meteringEnabled为YES此方法才可用
- (float)peakPowerForChannel:(NSUInteger)channelNumber 指定通道的测量峰值,注意只有调用完updateMeters才有值
- (float)averagePowerForChannel:(NSUInteger)channelNumber 指定通道的测量平均值,注意只有调用完updateMeters才有值

 

代理方法 说明
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag 完成录音
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error 录音编码发生错误

  3)代码实例(实现录音功能、录音结束自动播放、监听录音的声波变化)

 

  1 // RecorderController.m文件
  2 #import "RecorderController.h"
  3 #import <AVFoundation/AVFoundation.h>
  4 
  5 #define kRecordAudioFile @"myRecord.caf"
  6 
  7 @interface RecorderController ()<AVAudioRecorderDelegate>
  8 
  9 @property (nonatomic,strong) AVAudioRecorder *audioRecorder; // 音频录音机
 10 @property (nonatomic,strong) AVAudioPlayer *audioPlayer; // 音频播放器,用于播放录音文件
 11 @property (nonatomic,strong) NSTimer *timer; // 录音声波监控(注意这里暂时不对播放进行监控)
 12 
 13 @property (nonatomic, strong) UIButton *recordBtn; // 开始录音
 14 @property (nonatomic, strong) UIButton *pauseBtn; // 暂停录音
 15 @property (nonatomic, strong) UIButton *resumeBtn; // 恢复录音
 16 @property (nonatomic, strong) UIButton *stopBtn; // 停止录音
 17 @property (nonatomic, strong) UIProgressView *audioPowerProgressView; // 音频波动
 18 
 19 @end
 20 
 21 @implementation RecorderController
 22 
 23 #pragma mark - 懒加载
 24 // 1.audioRecorder
 25 - (AVAudioRecorder *)audioRecorder
 26 {
 27     if (!_audioRecorder) {
 28         // 创建录音文件保存路径
 29         NSURL *url = [self getSavePath];
 30         // 创建录音格式设置
 31         NSDictionary *setting = [self getAudioSetting];
 32         // 创建录音机
 33         NSError *error = nil;
 34         _audioRecorder = [[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
 35         _audioRecorder.delegate = self;
 36         _audioRecorder.meteringEnabled = YES; // 如果要监控声波则必须设置为YES
 37         if (error) {
 38             NSLog(@"创建录音机对象时发生错误,错误信息:%@", error.localizedDescription);
 39             return nil;
 40         }
 41     }
 42     return _audioRecorder;
 43 }
 44 // 2.audioPlayer
 45 - (AVAudioPlayer *)audioPlayer
 46 {
 47     if (!_audioPlayer) {
 48         // 取得录音文件保存路径
 49         NSURL *url = [self getSavePath];
 50         NSError *error = nil;
 51         // 创建播放器
 52         _audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
 53         _audioPlayer.numberOfLoops = 0;
 54         [_audioPlayer prepareToPlay]; // 播放缓存
 55         if (error) {
 56             NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
 57             return nil;
 58         }
 59     }
 60     return _audioPlayer;
 61 }
 62 // 3.timer
 63 - (NSTimer *)timer
 64 {
 65     if (!_timer) {
 66         _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];
 67     }
 68     return _timer;
 69 }
 70 
 71 - (void)viewDidLoad
 72 {
 73     [super viewDidLoad];
 74     
 75     [self initUI]; // 界面
 76     [self setAudioSession]; // 设置音频会话
 77 }
 78 
 79 #pragma mark - 界面
 80 - (void)initUI
 81 {
 82     self.view.backgroundColor = [UIColor colorWithRed:50 / 255.0 green:88 / 255.0 blue:111 / 255.0 alpha:1];
 83     
 84     // recordBtn
 85     CGFloat recordBtnX = 10;
 86     CGFloat recordBtnY = self.view.frame.size.height / 2.0 + 50;
 87     CGFloat recordBtnW = (self.view.bounds.size.width - 5 * recordBtnX) / 4;
 88     CGFloat recordBtnH = 35;
 89     _recordBtn = [UIButton buttonWithType:UIButtonTypeSystem];
 90     _recordBtn.frame = CGRectMake(recordBtnX, recordBtnY, recordBtnW, recordBtnH);
 91     [_recordBtn setTitle:@"开始录音" forState:UIControlStateNormal];
 92     _recordBtn.titleLabel.font = [UIFont systemFontOfSize:13];
 93     _recordBtn.backgroundColor = [UIColor whiteColor];
 94     _recordBtn.tintColor = [UIColor greenColor];
 95     _recordBtn.layer.borderWidth = 3;
 96     _recordBtn.layer.borderColor = [UIColor greenColor].CGColor;
 97     [_recordBtn addTarget:self action:@selector(recordBtnClick:) forControlEvents:UIControlEventTouchUpInside];
 98     [self.view addSubview:_recordBtn];
 99     
100     // pauseBtn
101     CGFloat pauseBtnX = CGRectGetMaxX(_recordBtn.frame) + recordBtnX;
102     CGFloat pauseBtnY = _recordBtn.frame.origin.y;
103     CGFloat pauseBtnW = _recordBtn.bounds.size.width;
104     CGFloat pauseBtnH = _recordBtn.bounds.size.height;
105     _pauseBtn = [UIButton buttonWithType:UIButtonTypeSystem];
106     _pauseBtn.frame = CGRectMake(pauseBtnX, pauseBtnY, pauseBtnW, pauseBtnH);
107     [_pauseBtn setTitle:@"暂停录音" forState:UIControlStateNormal];
108     _pauseBtn.titleLabel.font = [UIFont systemFontOfSize:13];
109     _pauseBtn.backgroundColor = [UIColor whiteColor];
110     _pauseBtn.tintColor = [UIColor greenColor];
111     _pauseBtn.layer.borderWidth = 3;
112     _pauseBtn.layer.borderColor = [UIColor greenColor].CGColor;
113     [_pauseBtn addTarget:self action:@selector(pauseBtnClick:) forControlEvents:UIControlEventTouchUpInside];
114     [self.view addSubview:_pauseBtn];
115     
116     // resumeBtn
117     CGFloat resumeBtnX = CGRectGetMaxX(_pauseBtn.frame) + recordBtnX;
118     CGFloat resumeBtnY = _recordBtn.frame.origin.y;
119     CGFloat resumeBtnW = _recordBtn.bounds.size.width;
120     CGFloat resumeBtnH = _recordBtn.bounds.size.height;
121     _resumeBtn = [UIButton buttonWithType:UIButtonTypeSystem];
122     _resumeBtn.frame = CGRectMake(resumeBtnX, resumeBtnY, resumeBtnW, resumeBtnH);
123     [_resumeBtn setTitle:@"恢复录音" forState:UIControlStateNormal];
124     _resumeBtn.titleLabel.font = [UIFont systemFontOfSize:13];
125     _resumeBtn.backgroundColor = [UIColor whiteColor];
126     _resumeBtn.tintColor = [UIColor greenColor];
127     _resumeBtn.layer.borderWidth = 3;
128     _resumeBtn.layer.borderColor = [UIColor greenColor].CGColor;
129     [_resumeBtn addTarget:self action:@selector(resumeBtnClick:) forControlEvents:UIControlEventTouchUpInside];
130     [self.view addSubview:_resumeBtn];
131     
132     // stopBtn
133     CGFloat stopBtnX = CGRectGetMaxX(_resumeBtn.frame) + recordBtnX;
134     CGFloat stopBtnY = _recordBtn.frame.origin.y;
135     CGFloat stopBtnW = _recordBtn.bounds.size.width;
136     CGFloat stopBtnH = _recordBtn.bounds.size.height;
137     _stopBtn = [UIButton buttonWithType:UIButtonTypeSystem];
138     _stopBtn.frame = CGRectMake(stopBtnX, stopBtnY, stopBtnW, stopBtnH);
139     [_stopBtn setTitle:@"停止录音" forState:UIControlStateNormal];
140     _stopBtn.titleLabel.font = [UIFont systemFontOfSize:13];
141     _stopBtn.backgroundColor = [UIColor whiteColor];
142     _stopBtn.tintColor = [UIColor greenColor];
143     _stopBtn.layer.borderWidth = 3;
144     _stopBtn.layer.borderColor = [UIColor greenColor].CGColor;
145     [_stopBtn addTarget:self action:@selector(stopBtnClick:) forControlEvents:UIControlEventTouchUpInside];
146     [self.view addSubview:_stopBtn];
147     
148     // audioPowerProgressView
149     CGFloat audioPowerProgressViewX = recordBtnX;
150     CGFloat audioPowerProgressViewY = self.view.frame.size.height / 2.0;
151     CGFloat audioPowerProgressViewW = self.view.bounds.size.width - audioPowerProgressViewX * 2;
152     CGFloat audioPowerProgressViewH = 20;
153     _audioPowerProgressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
154     _audioPowerProgressView.frame = CGRectMake(audioPowerProgressViewX, audioPowerProgressViewY, audioPowerProgressViewW, audioPowerProgressViewH);
155     _audioPowerProgressView.progress = 0;
156     _audioPowerProgressView.progressTintColor = [UIColor redColor];
157     _audioPowerProgressView.trackTintColor = [UIColor grayColor];
158     [self.view addSubview:_audioPowerProgressView];
159 }
160 
161 #pragma mark - 设置音频会话
162 - (void)setAudioSession
163 {
164     AVAudioSession *audioSession = [AVAudioSession sharedInstance];
165     // 设置为播放和录音状态,以便在录制完之后播放录音
166     [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
167     [audioSession setActive:YES error:nil];
168 }
169 
170 #pragma mark - 取得录音文件保存路径
171 - (NSURL *)getSavePath
172 {
173     NSString *urlStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
174     urlStr = [urlStr stringByAppendingPathComponent:kRecordAudioFile];
175     NSLog(@"录音文件保存路径:%@", urlStr);
176     NSURL *url = [NSURL fileURLWithPath:urlStr];
177     return url;
178 }
179 
180 #pragma mark - 取得录音文件设置
181 // 返回的是参数字典
182 - (NSDictionary *)getAudioSetting
183 {
184     NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
185     // 设置录音格式
186     [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
187     // 设置录音采样率,8000是电话采样率,对于一般录音已经够了
188     [dicM setObject:@(8000) forKey:AVSampleRateKey];
189     // 设置通道,这里采用单声道
190     [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
191     // 每个采样点位数,分为8、16、24、32
192     [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
193     // 是否使用浮点数采样
194     [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
195     return dicM;
196 }
197 
198 #pragma mark - 更新设置录音声波状态
199 - (void)audioPowerChange
200 {
201     // 更新测量值
202     [self.audioRecorder updateMeters];
203     // 取得第一个通道的音频,注意音频强度范围时-160到0
204     float power = [self.audioRecorder averagePowerForChannel:0];
205     CGFloat progress = (1.0 / 160.0) * (power + 160.0);
206     // 设置audioPowerProgressView的值
207     _audioPowerProgressView.progress = progress;
208 }
209 
210 #pragma mark - 点击事件
211 // 1.开始录音
212 - (void)recordBtnClick:(UIButton *)sender
213 {
214     if (![self.audioRecorder isRecording]) {
215         // 首次使用应用时如果调用recordBtnClick:方法会询问用户是否允许使用麦克风
216         [self.audioRecorder record];
217         self.timer.fireDate = [NSDate distantPast];
218     }
219 }
220 // 2.暂停录音
221 - (void)pauseBtnClick:(UIButton *)sender
222 {
223     if ([self.audioRecorder isRecording]) {
224         [self.audioRecorder pause];
225         self.timer.fireDate = [NSDate distantFuture];
226     }
227 }
228 // 3.恢复录音
229 - (void)resumeBtnClick:(UIButton *)sender
230 {
231     [self recordBtnClick:sender];
232 }
233 // 4.停止录音
234 - (void)stopBtnClick:(UIButton *)sender
235 {
236     [self.audioRecorder stop];
237     self.timer.fireDate = [NSDate distantFuture];
238     _audioPowerProgressView.progress = 0.0;
239 }
240 
241 #pragma mark - 代理方法<AVAudioRecorderDelegate>
242 // 参数1:recorder 录音机对象
243 // 参数2:flag 是否成功
244 - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
245 {
246     if (![self.audioPlayer isPlaying]) {
247         [self.audioPlayer play];
248     }
249     NSLog(@"录音完成!");
250 }
251 
252 @end

 

二、视频

1.MPMoviePlayerController(通常作嵌入视频播放)

 1)简介

  a.属于框架MediaPlayer.framework

  b.支持本地视频和网络视频播放

  c.MPMoviePlayerController自身实现了MPMediaPlayback协议,因此具有一般的播放器控制功能,例如:播放、停止、暂停等等

  d.MPMoviePlayerController自身并不是一个完成的视图控制器,如果要在UI中展示视频,需要将该类中的属性View添加到界面中

 2)常用属性和方法 

属性 说明
@property (nonatomic, copy) NSURL *contentURL 播放媒体URL(可以是本地路径,也可以是网络路径)
@property (nonatomic, readonly) UIView *view 播放器视图(如果要显示视频,必须将此视图添加到控制器视图中)
@property (nonatomic, readonly) UIView *backgroundView 播放器背景视图
@property (nonatomic, readonly) MPMoviePlaybackState playbackState 媒体播放状态,枚举类型:
MPMoviePlaybackStateStopped:停止播放
MPMoviePlaybackStatePlaying:正在播放
MPMoviePlaybackStatePaused:暂停
MPMoviePlaybackStateInterrupted:中断
MPMoviePlaybackStateSeekingForward:向前定位
MPMoviePlaybackStateSeekingBackward:向后定位
@property (nonatomic, readonly) MPMovieLoadState loadState 网络媒体加载状态,枚举类型:
MPMovieLoadStateUnknown:位置类型
MPMovieLoadStatePlayable:
MPMovieLoadStatePlaythroughOK:这种状态如果shouldAutoPlay为YES将自动播放
MPMovieLoadStateStalled:停滞状态
@property (nonatomic) MPMovieControlStyle controlStyle 控制面板风格,枚举类型:
MPMovieControlStyleNone:无控制面板 
MPMovieControlStyleEmbedded:嵌入视频风格 
MPMovieControlStyleFullscreen:全屏 
MPMovieControlStyleDefault:默认风格
@property (nonatomic) MPMovieRepeatMode repeatMode 重复播放模式,枚举类型:
MPMovieRepeatModeNone:不重复,默认值
MPMovieRepeatModeOne:重复播放
@property (nonatomic) BOOL shouldAutoplay 当网络媒体缓存到一定数据时是否自动播放,默认为YES
@property (nonatomic, getter=isFullscreen) BOOL fullscreen 是否全屏展示(默认为NO,注意如果要通过此属性设置全屏必须在视图显示完成后设置,否则无效)
@property (nonatomic) MPMovieScalingMode scalingMode 视频缩放填充模式,枚举类型:
MPMovieScalingModeNone:不进行任何缩放
MPMovieScalingModeAspectFit:固定缩放比例并且尽量全部展示视频,不会裁切视频
MPMovieScalingModeAspectFill:固定缩放比例并填充满整个视图展示,可能会裁切视频
MPMovieScalingModeFill:不固定缩放比例压缩填充整个视图,视频不会被裁切但是比例失衡
@property (nonatomic, readonly) BOOL readyForDisplay 是否有相关媒体被播放
@property (nonatomic, readonly) MPMovieMediaTypeMask movieMediaTypes 媒体类别,枚举类型:
MPMovieMediaTypeMaskNone:未知类型
MPMovieMediaTypeMaskVideo:视频
MPMovieMediaTypeMaskAudio:音频
@property (nonatomic) MPMovieSourceType movieSourceType 媒体源,枚举类型:
MPMovieSourceTypeUnknown:未知来源
MPMovieSourceTypeFile:本地文件
MPMovieSourceTypeStreaming:流媒体(直播或点播)
@property (nonatomic, readonly) NSTimeInterval duration 媒体时长,如果未知则返回0
@property (nonatomic, readonly) NSTimeInterval playableDuration 媒体可播放时长,主要用于表示网络媒体已下载视频时长
@property (nonatomic, readonly) CGSize naturalSize 视频实际尺寸,如果未知则返回CGSizeZero
@property (nonatomic) NSTimeInterval initialPlaybackTime 起始播放时间
@property (nonatomic) NSTimeInterval endPlaybackTime 终止播放时间
@property (nonatomic) BOOL allowsAirPlay 是否允许无线播放,默认为YES
@property (nonatomic, readonly, getter=isAirPlayVideoActive) BOOL airPlayVideoActive 当前媒体是否正在通过AirPlay播放
@property(nonatomic, readonly) BOOL isPreparedToPlay 是否准备好播放
@property(nonatomic) NSTimeInterval currentPlaybackTime 当前播放时间,单位:秒
@property(nonatomic) float currentPlaybackRate 当前播放速度,如果暂停则为0,正常速度为1.0,非0数据表示倍率

 

对象方法 说明
- (instancetype)initWithContentURL:(NSURL *)url 使用指定的URL初始化媒体播放控制器对象
- (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated 设置视频全屏,注意如果要通过此方法设置全屏则必须在其视图显示之后设置,否则无效
- (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option 获取在指定播放时间的视频缩略图,第一个参数是获取缩略图的时间点数组;第二个参数代表时间点精度,枚举类型:
MPMovieTimeOptionNearestKeyFrame:时间点附近
MPMovieTimeOptionExact:准确时间
- (void)cancelAllThumbnailImageRequests 取消所有缩略图获取请求
- (void)prepareToPlay 准备播放,加载视频数据到缓存,当调用play方法时如果没有准备好会自动调用此方法
- (void)play 开始播放
- (void)pause 暂停播放
- (void)stop 停止播放
- (void)beginSeekingForward 向前定位
- (void)beginSeekingBackward 向后定位
- (void)endSeeking 停止快进/快退

 

通知 说明
MPMoviePlayerScalingModeDidChangeNotification 视频缩放填充模式发生改变
MPMoviePlayerPlaybackDidFinishNotification 媒体播放完成或用户手动退出,具体完成原因可以通过通知userInfo中的key为MPMoviePlayerPlaybackDidFinishReasonUserInfoKey的对象获取
MPMoviePlayerPlaybackStateDidChangeNotification 播放状态改变,可配合playbakcState属性获取具体状态
MPMoviePlayerLoadStateDidChangeNotification 媒体网络加载状态改变
MPMoviePlayerNowPlayingMovieDidChangeNotification 当前播放的媒体内容发生改变
MPMoviePlayerWillEnterFullscreenNotification 将要进入全屏
MPMoviePlayerDidEnterFullscreenNotification 进入全屏后
MPMoviePlayerWillExitFullscreenNotification 将要退出全屏
MPMoviePlayerDidExitFullscreenNotification 退出全屏后
MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification 当媒体开始通过AirPlay播放或者结束AirPlay播放
MPMoviePlayerReadyForDisplayDidChangeNotification 视频显示状态改变
MPMovieMediaTypesAvailableNotification 确定了媒体可用类型后
MPMovieSourceTypeAvailableNotification 确定了媒体来源后
MPMovieDurationAvailableNotification 确定了媒体播放时长后
MPMovieNaturalSizeAvailableNotification 确定了媒体的实际尺寸后
MPMoviePlayerThumbnailImageRequestDidFinishNotification 缩略图请求完成之后
MPMediaPlaybackIsPreparedToPlayDidChangeNotification 做好播放准备后

注:MPMoviePlayerController的状态信息并不是通过代理来和外界交互的,而是通过通知中心。

 3)代码示例

  注:本例实现获取缩略图(视频第一帧画面),点击缩略图播放视频

  1 // VideoPlayerController.m文件
  2 #import "VideoPlayerController.h"
  3 #import <MediaPlayer/MediaPlayer.h>  // 导入框架
  4 #import <AVFoundation/AVFoundation.h> // 使用AVURLAsset获取视频第一帧需要导入的框架
  5 
  6 @interface VideoPlayerController ()
  7 
  8 @property (nonatomic, strong) MPMoviePlayerController *moviePlayer; // 视频播放控制器
  9 @property (nonatomic, strong) UIImageView *imageView; // 显示第一帧视频的图片
 10 @property (nonatomic, assign) BOOL isThumbnailRequestFinished;  // 第一帧请求是否成功
 11 
 12 @end
 13 
 14 @implementation VideoPlayerController
 15 
 16 #pragma mark - 懒加载
 17 // 1.moviePlayer
 18 - (MPMoviePlayerController *)moviePlayer
 19 {
 20     if (!_moviePlayer) {
 21         // 获取视频URL
 22         NSURL *url = [self getNetworkUrl];
 23         // 根据URL初始化视频播放控制器
 24         _moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
 25         _moviePlayer.view.frame = CGRectMake(0, 64, self.view.bounds.size.width, 200);
 26         // 将视频播放控制器的View添加到控制器的View上
 27         [self.view addSubview:_moviePlayer.view];
 28     }
 29     return _moviePlayer;
 30 }
 31 // 2.imageView
 32 - (UIImageView *)imageView
 33 {
 34     if (!_imageView) {
 35         CGFloat imageViewX = 0;
 36         CGFloat imageViewY = 10;
 37         CGFloat imageViewW = self.moviePlayer.view.bounds.size.width;
 38         CGFloat imageViewH = self.moviePlayer.view.bounds.size.height - imageViewY * 2;
 39         _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageViewX, imageViewY, imageViewW, imageViewH)];
 40         _imageView.userInteractionEnabled = YES;
 41         // 添加点击手势
 42         UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick:)];
 43         [_imageView addGestureRecognizer:tap];
 44         [self.moviePlayer.view addSubview:_imageView];
 45     }
 46     return _imageView;
 47 }
 48 
 49 - (void)viewDidLoad
 50 {
 51     [super viewDidLoad];
 52     
 53     [self initUI]; // 界面
 54 }
 55 
 56 #pragma mark - 界面
 57 - (void)initUI
 58 {
 59     self.view.backgroundColor = [UIColor blueColor];
 60     
 61     // [self thumbnailImageRequest];  // 请求第一帧视频画面
 62     [self thumbnailImageRequest:0.1]; // 请求第一帧视频画面
 63     [self addNotification]; // 注册通知
 64 }
 65 
 66 #pragma mark - 注册通知
 67 - (void)addNotification
 68 {
 69     // 监听播放状态发生改变的通知
 70     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
 71     // 监听播放完成的通知
 72     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
 73     // 监听缩略图请求成功的通知
 74     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerThumbnailRequestFinished:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:self.moviePlayer];
 75 }
 76 
 77 #pragma mark - 手势点击事件
 78 // 点击第一帧视频播放
 79 - (void)tapClick:(UITapGestureRecognizer *)tap
 80 {
 81     // 判断缩略图是否请求成功
 82     if (!_isThumbnailRequestFinished) {
 83         return;
 84     }
 85     
 86     self.imageView.hidden = YES;
 87     [self.moviePlayer play];  // 播放
 88 }
 89 
 90 #pragma mark - 取得视频URL
 91 - (NSURL *)getNetworkUrl
 92 {
 93     NSString *urlStr = @"http://192.168.1.177:8080/HKGI/upload/001.mp4";
 94     urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 95     NSURL *url = [NSURL URLWithString:urlStr];
 96     return url;
 97 }
 98 
 99 #pragma mark - 请求第一帧视频
100 // 1.第一种方法(使用MPMoviePlayerController生成缩略图)
101 // 方法1与方法2的区别:方法1在获取缩略图的时候会播放缩略图的那一帧,而方法2不会
102 - (void)thumbnailImageRequest
103 {
104     // 获取0.1s缩略图
105     [self.moviePlayer requestThumbnailImagesAtTimes:@[@0.1] timeOption:MPMovieTimeOptionNearestKeyFrame];
106 }
107 // 2.第二种方法(使用AVFoundation生成缩略图)
108 - (void)thumbnailImageRequest:(CGFloat )timeBySecond
109 {
110     // 创建URL
111     NSURL *url = [self getNetworkUrl];
112     // 根据URL创建AVURLAsset
113     AVURLAsset *urlAsset = [AVURLAsset assetWithURL:url];
114     // 根据AVURLAsset创建
115     AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
116     
117     // 截图
118     // requestTime:缩略图创建时间
119     // actualTime:缩略图实际生成的时间
120     NSError *error = nil;
121     // CMTime:电影时间信息的结构体(参数一:视频第几秒,参数二:每秒有多少帧)
122     // 如果要动态的某一秒的第几帧可以使用CMTimeMake方法
123     CMTime time = CMTimeMakeWithSeconds(timeBySecond, 10);
124     // CMTime time = CMTimeMake(timeBySecond, 10);
125     
126     CMTime actualTime;
127     CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];
128     if (error) {
129         NSLog(@"截取视频缩略图时发生错误,错误信息:%@",error.localizedDescription);
130         return;
131     }
132     
133     CMTimeShow(actualTime);
134     // 转化为UIImage
135     UIImage *image = [UIImage imageWithCGImage:cgImage];
136     self.imageView.image = image;
137     _isThumbnailRequestFinished = YES;
138     
139     // 保存到相册
140     // UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
141     // CGImageRelease(cgImage);
142 }
143 
144 #pragma mark - 通知事件
145 // 1.播放状态发生改变
146 - (void)moviePlayerPlaybackStateDidChange:(NSNotification *)notification
147 {
148     switch (self.moviePlayer.playbackState) {
149         case MPMoviePlaybackStatePlaying:
150             NSLog(@"正在播放");
151             break;
152         case MPMoviePlaybackStatePaused:
153             NSLog(@"暂停播放");
154             break;
155         case MPMoviePlaybackStateStopped:
156             NSLog(@"停止播放");
157             break;
158         default:
159             NSLog(@"播放状态:%d", self.moviePlayer.playbackState);
160             break;
161     }
162 }
163 // 2.播放完成
164 - (void)moviePlayerPlaybackStateDidFinish:(NSNotification *)notification
165 {
166     NSLog(@"播放完成:%d", self.moviePlayer.playbackState);
167     self.imageView.hidden = NO;
168 }
169 // 3.缩略图请求成功(该方法每次截图成功都会调用一次)(对于第一种方法获取的缩略图,会以通知形式返回)
170 - (void)moviePlayerThumbnailRequestFinished:(NSNotification *)notification
171 {
172     NSLog(@"视频截图完成");
173     UIImage *image = notification.userInfo[MPMoviePlayerThumbnailImageKey];
174     self.imageView.image = image;
175     _isThumbnailRequestFinished = YES;
176     
177     // 保存图片到相册(首次调用会请求用户获得访问相册权限)
178     // UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
179 }
180 
181 #pragma mark - 销毁通知
182 - (void)dealloc
183 {
184     // 移除所有通知监控
185     [[NSNotificationCenter defaultCenter] removeObserver:self];
186 }
187 
188 @end

2.MPMoviePlayerViewController(单独跳转控制器播放)

 1)说明

  如果MPMoviePlayerController不作为嵌入视频播放(例如腾讯新闻中嵌入一个视频),通常在播放时都是占满一个屏幕的,特别是在iPhone上。因此自iOS3.2以后,苹果自己封装了一个继承自UIViewController的控制器MPMoviePlayerViewController,专门用来全屏播放视频,这个控制器里面有一个属性moviePlayer和一个带有URL的初始化方法。同时它的内部也实现了一些作为模态视图展示所特有的功能。例如:默认是全屏模式展示,弹出后自动播放;作为模态窗口展示时,如果点击“Done”按钮会自动退出模态窗口等

 2)代码示例

  注:本例实现获取缩略图(视频第一帧画面),点击缩略图模态跳转播放视频控制器全屏开始播放 

  1 // VideoController.m文件
  2 #import "VideoController.h"
  3 #import <MediaPlayer/MediaPlayer.h> // 导入框架
  4 #import <AVFoundation/AVFoundation.h> // 使用AVURLAsset获取视频第一帧需要导入的框架
  5 
  6 @interface VideoController ()
  7 
  8 // 播放器视图控制器
  9 @property (nonatomic, strong) MPMoviePlayerViewController *moviePlayerViewController;
 10 // 显示第一帧视频的图片
 11 @property (nonatomic, strong) UIImageView *imageView;
 12 // 第一帧请求是否成功
 13 @property (nonatomic, assign) BOOL isThumbnailRequestFinished;
 14 
 15 @end
 16 
 17 @implementation VideoController
 18 
 19 #pragma mark - 懒加载
 20 // 1.moviePlayerViewController
 21 - (MPMoviePlayerViewController *)moviePlayerViewController
 22 {
 23     if (!_moviePlayerViewController) {
 24         // 获取视频URL
 25         NSURL *url = [self getNetworkUrl];
 26         // 根据URL初始化视频播放控制器
 27         _moviePlayerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
 28         // 注册通知
 29         [self addNotification];
 30     }
 31     return _moviePlayerViewController;
 32 }
 33 // 2.imageView
 34 - (UIImageView *)imageView
 35 {
 36     if (!_imageView) {
 37         CGFloat imageViewX = 20;
 38         CGFloat imageViewY = 80;
 39         CGFloat imageViewW = self.view.bounds.size.width - imageViewX * 2;
 40         CGFloat imageViewH = 200;
 41         _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageViewX, imageViewY, imageViewW, imageViewH)];
 42         _imageView.userInteractionEnabled = YES;
 43         // 添加点击手势
 44         UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick:)];
 45         [_imageView addGestureRecognizer:tap];
 46         [self.view addSubview:_imageView];
 47     }
 48     return _imageView;
 49 }
 50 
 51 - (void)viewDidLoad
 52 {
 53     [super viewDidLoad];
 54     
 55     [self initUI]; // 界面
 56 }
 57 
 58 #pragma mark - 界面
 59 - (void)initUI
 60 {
 61     self.view.backgroundColor = [UIColor whiteColor];
 62     [self thumbnailImageRequest:0.1]; // 请求第一帧视频画面
 63 }
 64 
 65 #pragma mark - 注册通知
 66 - (void)addNotification
 67 {
 68     // 监听播放状态发生改变的通知
 69     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
 70     // 监听播放完成的通知
 71     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
 72 }
 73 
 74 #pragma mark - 手势点击事件
 75 // 点击第一帧视频播放
 76 - (void)tapClick:(UITapGestureRecognizer *)tap
 77 {
 78     // 判断缩略图是否请求成功
 79     if (!_isThumbnailRequestFinished) {
 80         return;
 81     }
 82     
 83     self.moviePlayerViewController = nil;
 84     [self presentMoviePlayerViewControllerAnimated:self.moviePlayerViewController];
 85 }
 86 
 87 #pragma mark - 请求第一帧视频画面
 88 // 使用AVFoundation生成缩略图
 89 - (void)thumbnailImageRequest:(CGFloat )timeBySecond
 90 {
 91     // 创建URL
 92     NSURL *url = [self getNetworkUrl];
 93     // 根据URL创建AVURLAsset
 94     AVURLAsset *urlAsset = [AVURLAsset assetWithURL:url];
 95     // 根据AVURLAsset创建
 96     AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
 97     
 98     // 截图
 99     // requestTime:缩略图创建时间
100     // actualTime:缩略图实际生成的时间
101     NSError *error = nil;
102     // CMTime:电影时间信息的结构体(参数一:视频第几秒,参数二:每秒有多少帧)
103     // 如果要动态的某一秒的第几帧可以使用CMTimeMake方法
104     CMTime time = CMTimeMakeWithSeconds(timeBySecond, 10);
105     // CMTime time = CMTimeMake(timeBySecond, 10);
106     
107     CMTime actualTime;
108     CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];
109     if (error) {
110         NSLog(@"截取视频缩略图时发生错误,错误信息:%@",error.localizedDescription);
111         return;
112     }
113     
114     CMTimeShow(actualTime);
115     // 转化为UIImage
116     UIImage *image = [UIImage imageWithCGImage:cgImage];
117     self.imageView.image = image;
118     _isThumbnailRequestFinished =  YES;
119 }
120 
121 #pragma mark - 获取本地视频URL
122 - (NSURL *)getFileUrl
123 {
124     NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"mp4"];
125     NSURL *url = [NSURL fileURLWithPath:urlStr];
126     return url;
127 }
128 
129 #pragma mark - 获取网络视频URL
130 - (NSURL *)getNetworkUrl
131 {
132     NSString *urlStr = @"http://192.168.1.177:8080/HKGI/upload/001.mp4";
133     urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
134     NSURL *url = [NSURL URLWithString:urlStr];
135     return url;
136 }
137 
138 #pragma mark - 通知事件
139 // 1.播放状态发生改变
140 - (void)moviePlayerPlaybackStateDidChange:(NSNotification *)notification
141 {
142     switch (self.moviePlayerViewController.moviePlayer.playbackState) {
143         case MPMoviePlaybackStatePlaying:
144             NSLog(@"正在播放");
145             break;
146         case MPMoviePlaybackStatePaused:
147             NSLog(@"暂停播放");
148             break;
149         case MPMoviePlaybackStateStopped:
150             NSLog(@"停止播放");
151             break;
152         default:
153             NSLog(@"播放状态:%ld", self.moviePlayerViewController.moviePlayer.playbackState);
154             break;
155     }
156 }
157 // 2.播放完成
158 - (void)moviePlayerPlaybackStateDidFinish:(NSNotification *)notification
159 {
160     NSLog(@"播放完成:%ld", self.moviePlayerViewController.moviePlayer.playbackState);
161 }
162 
163 #pragma mark - 销毁通知
164 - (void)dealloc
165 {
166     // 移除所有通知监控
167     [[NSNotificationCenter defaultCenter] removeObserver:self];
168 }
169 
170 @end

 注:这里需要强调一下,由于MPMoviePlayerViewController的初始化方法做了大量工作(例如设置URL、自动播放、添加点击Done完成的监控等),所以当再次点击播放弹出新的模态窗口的时如果不销毁之前的MPMoviePlayerViewController,那么新的对象就无法完成初始化,这样也就不能再次进行播放。定位到我们代码示例中就是在每次模态跳转播放控制器之前必须将它设置为nil,重新加载初始化一下(self.moviePlayerViewController = nil)

3.AVPlayer(通常用于自定义一个播放器)

 1)说明

  a.MPMoviePlayerController足够强大,只需要写几行代码就能够完成一个播放器的功能;但是由于它的高度封装,使得我们自定义播放器变得相当复杂或是不可能完成。其实在这个时候,我们通常会使用AVFoundation框架的AVPlayer进行自定义播放器。因为它更加接近底层,灵活性也较强。

  b.AVPlayer本身并不能显示视频,而且也不像MPMoviePlayerController有一个View属性。如果AVPlayer要显示,必须创建一个播放器层AVPlayerLayer用于展示。播放器层继承自CALayer。有了这个AVPlayerLayer,我们需要将它添加到控制器的layer中

  c.AVPlayer的常用类:

    AVAsset:主要用于获取多媒体信息,是一个抽象类,不能直接使用。

    AVURLAsset:AVAsset的子类,可以根据一个URL路径创建一个包含媒体信息的AVURLAsset对象。

    AVPlayerItem:一个媒体资源管理对象,管理视频的一些基本信息和状态(一个AVPlayerItem对应着一个视频资源)

 2)代码示例

  1 // CustomAVPlayerController.m文件
  2 #import "CustomAVPlayerController.h"
  3 #import <AVFoundation/AVFoundation.h> // 需要导入的框架
  4 
  5 @interface CustomAVPlayerController ()
  6 
  7 // 播放器对象
  8 @property (nonatomic, strong) AVPlayer *player;
  9 // 播放器容器
 10 @property (nonatomic, strong) UIView *containerView;
 11 // 播放暂停按钮
 12 @property (nonatomic, strong) UIButton *playOrPauseBtn;
 13 // 播放进度
 14 @property (nonatomic, strong) UIProgressView *progressView;
 15 // 保存当前正在播放剧集的按钮
 16 @property (nonatomic, strong) UIButton *currentBtn;
 17 
 18 @end
 19 
 20 @implementation CustomAVPlayerController
 21 
 22 #pragma mark - 懒加载
 23 // player
 24 - (AVPlayer *)player
 25 {
 26     if (!_player) {
 27         // 获取第1个视频文件(AVPlayerItem对象)
 28         AVPlayerItem *playerItem = [self getPlayerItemWithIndex:0];
 29         // 根据AVPlayerItem对象初始化播放器对象
 30         _player = [AVPlayer playerWithPlayerItem:playerItem];
 31         
 32         // 给播放器添加进度更新(定时器)
 33         [self addProgressObserver];
 34         // 给AVPlayerItem(视频文件对象)添加监控(KVO)
 35         [self addObserverToPlayerItem:playerItem];
 36     }
 37     return _player;
 38 }
 39 
 40 - (void)viewDidLoad
 41 {
 42     [super viewDidLoad];
 43     
 44     [self initUI]; // 界面
 45     [self.player play]; // 播放
 46 }
 47 
 48 #pragma mark - 界面
 49 - (void)initUI
 50 {
 51     self.view.backgroundColor = [UIColor whiteColor];
 52     
 53     // containerView
 54     CGFloat containerViewX = 0;
 55     CGFloat containerViewY = 64;
 56     CGFloat containerViewW = self.view.bounds.size.width;
 57     CGFloat containerViewH = 250;
 58     _containerView = [[UIView alloc] initWithFrame:CGRectMake(containerViewX, containerViewY, containerViewW, containerViewH)];
 59     [self.view addSubview:_containerView];
 60     
 61     // 根据AVPlayer创建播放器层
 62     AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
 63     playerLayer.frame = _containerView.bounds;
 64     // 视频填充模式
 65     playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
 66     [_containerView.layer addSublayer:playerLayer];
 67     
 68     // playOrPauseBtn
 69     CGFloat playOrPauseBtnX = 20;
 70     CGFloat playOrPauseBtnY = CGRectGetMaxY(_containerView.frame);
 71     CGFloat playOrPauseBtnW = 40;
 72     CGFloat playOrPauseBtnH = 40;
 73     _playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
 74     _playOrPauseBtn.frame = CGRectMake(playOrPauseBtnX, playOrPauseBtnY, playOrPauseBtnW, playOrPauseBtnH);
 75     [_playOrPauseBtn setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
 76     [_playOrPauseBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateSelected];
 77     [_playOrPauseBtn addTarget:self action:@selector(playOrPauseBtnClick:) forControlEvents:UIControlEventTouchUpInside];
 78     _playOrPauseBtn.selected = YES;
 79     [self.view addSubview:_playOrPauseBtn];
 80     
 81     // progressView
 82     CGFloat progressViewX = CGRectGetMaxX(_playOrPauseBtn.frame);
 83     CGFloat progressViewY = _playOrPauseBtn.frame.origin.y + _playOrPauseBtn.bounds.size.height / 2.0;
 84     CGFloat progressViewW = _containerView.bounds.size.width - progressViewX - _playOrPauseBtn.frame.origin.x;
 85     CGFloat progressViewH = 2;
 86     _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(progressViewX, progressViewY, progressViewW, progressViewH)];
 87     [self.view addSubview:_progressView];
 88     
 89     // bottomView
 90     CGFloat bottomViewX = _playOrPauseBtn.frame.origin.x;
 91     CGFloat bottomViewY = CGRectGetMaxY(_playOrPauseBtn.frame) + 10;
 92     CGFloat bottomViewW = _containerView.bounds.size.width - bottomViewX * 2;
 93     CGFloat bottomViewH = 70;
 94     UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(bottomViewX, bottomViewY, bottomViewW, bottomViewH)];
 95     [self.view addSubview:bottomView];
 96     
 97     // textLabel
 98     CGFloat textLabelX = 0;
 99     CGFloat textLabelY = 0;
100     CGFloat textLabelW = bottomView.bounds.size.width;
101     CGFloat textLabelH = 30;
102     UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(textLabelX, textLabelY, textLabelW, textLabelH)];
103     textLabel.text = @"播放列表";
104     textLabel.font = [UIFont boldSystemFontOfSize:17];
105     textLabel.textColor = [UIColor blueColor];
106     [bottomView addSubview:textLabel];
107     
108     // 循环创建btn
109     NSArray *movieNames = @[@"001", @"002", @"003", @"004"];
110     for (int i = 0; i < movieNames.count; i++) {
111         CGFloat btnW = 55;
112         CGFloat btnH = 35;
113         CGFloat btnX = textLabel.frame.origin.x + (btnW + 5) * i;
114         CGFloat btnY = CGRectGetMaxY(textLabel.frame);
115         UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
116         btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
117         [btn setTitle:movieNames[i] forState:UIControlStateNormal];
118         btn.titleLabel.font = [UIFont boldSystemFontOfSize:13];
119         btn.tintColor = [UIColor blueColor];
120         btn.backgroundColor = [UIColor grayColor];
121         btn.tag = 100 + i;
122         [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
123         [bottomView addSubview:btn];
124         if (i == 0) {
125             _currentBtn = btn;
126             _currentBtn.backgroundColor = [UIColor redColor];
127         }
128     }
129 }
130 
131 #pragma mark - 点击事件
132 // 1.播放暂停按钮
133 - (void)playOrPauseBtnClick:(UIButton *)sender
134 {
135     // 说明暂停
136     if(self.player.rate == 0){
137         sender.selected = YES;
138         [self.player play]; // 播放
139     
140     // 正在播放
141     }else if(self.player.rate == 1){
142         sender.selected = NO;
143         [self.player pause]; // 暂停
144     }
145 }
146 // 2.播放列表剧集按钮
147 - (void)btnClick:(UIButton *)sender
148 {
149     // 进度归0
150     _progressView.progress = 0;
151     // 切换背景
152     _currentBtn.backgroundColor = [UIColor grayColor];
153     _currentBtn = sender;
154     _currentBtn.backgroundColor = [UIColor redColor];
155     // 移除播放完毕通知
156     [self removeNotification];
157     // 移除当前正在播放的视频文件的属性监听(KVO)
158     [self removeObserverFromPlayerItem:self.player.currentItem];
159     // 获得切换后的视频文件对象(AVPlayerItem对象)
160     AVPlayerItem *playerItem = [self getPlayerItemWithIndex:(int)(sender.tag - 100)];
161     // 给AVPlayerItem(视频文件对象)添加监控(KVO)
162     [self addObserverToPlayerItem:playerItem];
163     // 切换视频
164     [self.player replaceCurrentItemWithPlayerItem:playerItem];
165     // 注册通知
166     [self addNotification];
167 }
168 
169 #pragma mark - 根据索引取得AVPlayerItem对象
170 // 一个AVPlayerItem对象对应一个视频文件
171 - (AVPlayerItem *)getPlayerItemWithIndex:(int)index
172 {
173     NSArray *movieNames = @[@"001", @"002", @"003", @"004"];
174     NSString *urlStr = [NSString stringWithFormat:@"http://192.168.1.177:8080/HKGI/upload/%@.mp4", movieNames[index]];
175     urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
176     NSURL *url = [NSURL URLWithString:urlStr];
177     // 根据视频URL创建视频文件对象(AVPlayerItem对象)
178     AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
179     return playerItem;
180 }
181 
182 #pragma mark - 监控
183 // 1.给播放器添加进度更新(定时器)
184 - (void)addProgressObserver
185 {
186     AVPlayerItem *playerItem = self.player.currentItem;
187     // 这里设置每秒更新一次进度值
188     __weak CustomAVPlayerController *weakSelf = self;
189     [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
190         float currentTime = CMTimeGetSeconds(time);
191         float totalTime = CMTimeGetSeconds(playerItem.duration);
192         // 判断当前播放时间是否为0
193         if (currentTime) {
194             [weakSelf.progressView setProgress:(currentTime / totalTime) animated:YES];
195         }
196     }];
197 }
198 // 2.给AVPlayerItem(视频文件对象)添加监控(KVO)
199 - (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem
200 {
201     // 监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
202     [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
203     // 监控网络加载情况属性
204     [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
205 }
206 // 添加播放器通知(通知中心)
207 - (void)addNotification
208 {
209     // 给AVPlayerItem添加播放完成通知
210     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
211 }
212 
213 #pragma mark - 通过KVO监控播放器状态
214 // 参数1:keyPath 监控属性
215 // 参数2:object  监视器
216 // 参数3:change  状态改变
217 // 参数4:context 上下文
218 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
219 {
220     AVPlayerItem *playerItem = object;
221     if ([keyPath isEqualToString:@"status"]) {
222         AVPlayerStatus status= [change[@"new"] intValue];
223         if(status == AVPlayerStatusReadyToPlay) {
224             NSLog(@"正在播放...,视频总长度:%.2f", CMTimeGetSeconds(playerItem.duration));
225         }
226         
227     } else if([keyPath isEqualToString:@"loadedTimeRanges"]) {
228         NSArray *array = playerItem.loadedTimeRanges;
229         CMTimeRange timeRange = [array[0] CMTimeRangeValue]; // 本次缓冲时间范围
230         float startSeconds = CMTimeGetSeconds(timeRange.start);
231         float durationSeconds = CMTimeGetSeconds(timeRange.duration);
232         NSTimeInterval totalBuffer = startSeconds + durationSeconds; // 缓冲总长度
233         NSLog(@"共缓冲:%.2f", totalBuffer);
234     }
235 }
236 
237 #pragma mark - 播放完成通知
238 - (void)playbackFinished:(NSNotification *)notification
239 {
240     NSLog(@"视频播放完成.");
241 }
242 
243 #pragma mark - 移除KVO监听的属性
244 - (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem
245 {
246     // KVO监听播放状态属性status
247     [playerItem removeObserver:self forKeyPath:@"status"];
248     // KVO监听加载进度属性loadedTimeRanges
249     [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
250 }
251 
252 #pragma mark - 移除通知
253 - (void)removeNotification
254 {
255     [[NSNotificationCenter defaultCenter] removeObserver:self];
256 }
257 
258 #pragma mark - 销毁方法
259 - (void)dealloc
260 {
261     // 移除KVO监听的属性
262     [self removeObserverFromPlayerItem:self.player.currentItem];
263     // 移除通知
264     [self removeNotification];
265 }
266 
267 @end

 

三、摄像头

1.UIImageViewPickerController(可用于选取相册相片、拍照和视频录制)

  1)说明

  UIImageViewPickerController继承自UINavigationController,主要可用于选取相册相片、拍照和视频录制

 2)属性和方法

属性 说明

@property (nonatomic) UIImagePickerControllerSourceType

sourceType

拾取源类型,sourceType是枚举类型:
UIImagePickerControllerSourceTypePhotoLibrary:照片库(默认值)
UIImagePickerControllerSourceTypeCamera:摄像头
UIImagePickerControllerSourceTypeSavedPhotosAlbum:相簿
@property (nonatomic, copy) NSArray  *mediaTypes

媒体类型(拍照时可以不用设置;但是当要录像的时候必须设置)

默认情况下此数组包含:

kUTTypeImage:图片

kUTTypeVideo:视频不带声音

kUTTypeMovie:视频带有声音

@property (nonatomic) NSTimeInterval  videoMaximumDuration 视频最大录制时长(默认为10s)
@property (nonatomic) UIImagePickerControllerQualityType  videoQuality 视频质量,枚举类型:
UIImagePickerControllerQualityTypeHigh:高清质量
UIImagePickerControllerQualityTypeMedium:中等质量,适合WiFi传输
UIImagePickerControllerQualityTypeLow:低质量,适合蜂窝网传输
UIImagePickerControllerQualityType640x480:640*480
UIImagePickerControllerQualityTypeIFrame1280x720:1280*720
UIImagePickerControllerQualityTypeIFrame960x540:960*540
@property (nonatomic) BOOL  showsCameraControls 是否显示摄像头控制面板(默认为YES)
@property (nonatomic, retain) UIView  *cameraOverlayView 摄像头上覆盖的视图,可用通过这个视频来自定义拍照或录像界面
@property (nonatomic) CGAffineTransform  cameraViewTransform 摄像头形变

@property (nonatomic) UIImagePickerControllerCameraCaptureMode

cameraCaptureMode

摄像头捕获模式,捕获模式是枚举类型:
UIImagePickerControllerCameraCaptureModePhoto:拍照模式
UIImagePickerControllerCameraCaptureModeVideo:视频录制模式

@property (nonatomic) UIImagePickerControllerCameraDevice

cameraDevice

摄像头设备,cameraDevice是枚举类型:
UIImagePickerControllerCameraDeviceRear:前置摄像头
UIImagePickerControllerCameraDeviceFront:后置摄像头

@property (nonatomic) UIImagePickerControllerCameraFlashMode

cameraFlashMode

闪光灯模式,枚举类型:
UIImagePickerControllerCameraFlashModeOff:关闭闪光灯
UIImagePickerControllerCameraFlashModeAuto:闪光灯自动
UIImagePickerControllerCameraFlashModeOn:打开闪光灯

 

类方法 说明
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType 指定的源类型是否可用,sourceType是枚举类型:
UIImagePickerControllerSourceTypePhotoLibrary:照片库
UIImagePickerControllerSourceTypeCamera:摄像头
UIImagePickerControllerSourceTypeSavedPhotosAlbum:相簿
+ (NSArray *)availableMediaTypesForSourceType:(UIImagePickerControllerSourceType)sourceType 指定的源设备上可用的媒体类型,一般就是图片和视频
+ (BOOL)isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice)cameraDevice 指定的摄像头是否可用,cameraDevice是枚举类型:
UIImagePickerControllerCameraDeviceRear:前置摄像头
UIImagePickerControllerCameraDeviceFront:后置摄像头
+ (BOOL)isFlashAvailableForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice 指定摄像头的闪光灯是否可用
+ (NSArray *)availableCaptureModesForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice 获得指定摄像头上的可用捕获模式,捕获模式是枚举类型:
UIImagePickerControllerCameraCaptureModePhoto:拍照模式
UIImagePickerControllerCameraCaptureModeVideo:视频录制模式

 

对象方法 说明
- (void)takePicture 编程方式拍照
- (BOOL)startVideoCapture 编程方式录制视频
- (void)stopVideoCapture 编程方式停止录制视频

 

代理方法 说明
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 媒体拾取完成
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker 取消拾取

 

扩展方法(主要用于保存照片、视频到相簿) 说明
UIImageWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SEL completionSelector, void *contextInfo) 保存照片到相簿
UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(NSString *videoPath) 能否将视频保存到相簿
void UISaveVideoAtPathToSavedPhotosAlbum(NSString *videoPath, id completionTarget, SEL completionSelector, void *contextInfo) 保存视频到相簿

  3)如果使用UIImagePickerController拍照或是录制视频,使用步骤如下

  a.创建UIImagePickerController对象

  b.指定拾取源:选择照片时指定拾取源是照片或是相簿,拍照和录制指定拾取源为摄像头类型

  c.拍照和录制视频,需要指定摄像头:前置摄像头或者后置摄像头

  d.设置媒体类型mediaType,注意如果是录像必须设置;如果是拍照,次步骤可省略,因为mediaType默认包含kUTTypeImage(注意媒体类型定义在MobileCoreServices.framework中)

  e.指定捕捉模式、拍照或者录制视频(视频录制时必须先设置媒体类型再设置捕捉模式)

  f.展示UIImagePickerController(通常以模态窗口形式打开)

  g.拍照和录制视频结束后在代理方法中展示/保存照片或视频

 4)代码实例

  1 // ImagePickerController.m文件
  2 #import "ImagePickerController.h"
  3 #import <MobileCoreServices/MobileCoreServices.h>
  4 #import <AVFoundation/AVFoundation.h>
  5 
  6 @interface ImagePickerController ()<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
  7 
  8 @property (nonatomic, assign) int isVideo; // 是否录制视频(1:录制视频 0:拍照)
  9 @property (nonatomic, strong) UIImagePickerController *imagePicker;
 10 @property (nonatomic, strong) UIImageView *photoImageView; // 照片展示视图
 11 @property (nonatomic, strong) AVPlayer *player; // 播放器(用于录制完视频后播放视频)
 12 
 13 @end
 14 
 15 @implementation ImagePickerController
 16 
 17 #pragma mark - 懒加载
 18 // imagePicker
 19 - (UIImagePickerController *)imagePicker
 20 {
 21     if (!_imagePicker) {
 22         _imagePicker = [[UIImagePickerController alloc] init];
 23         // 设置UIImagePickerController的拾取源(这里是摄像头)
 24         _imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
 25         // 设置使用哪一个摄像头(这里设置的是后置摄像头)
 26         _imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
 27         // 判断当前用户设置是视频录制,还是拍照(1:视频录制 0:拍照)
 28         if (_isVideo) {
 29             // 视频录制必须设置媒体类型数组(当前设置为视频且带有声音)
 30             _imagePicker.mediaTypes = @[(NSString *)kUTTypeMovie];
 31             // 设置视频质量
 32             _imagePicker.videoQuality = UIImagePickerControllerQualityTypeIFrame1280x720;
 33             // 设置摄像头模式(当前设置为录制视频模式)
 34             _imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
 35         
 36         } else {
 37             // 设置摄像头模式(当前设置为拍照模式)
 38             _imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
 39         }
 40         // 允许编辑
 41         _imagePicker.allowsEditing = YES;
 42         // 设置代理
 43         _imagePicker.delegate = self;
 44     }
 45     return _imagePicker;
 46 }
 47 
 48 - (void)viewDidLoad
 49 {
 50     [super viewDidLoad];
 51     
 52     [self initUI]; // 界面
 53 }
 54 
 55 #pragma mark - 界面
 56 - (void)initUI
 57 {
 58     self.view.backgroundColor = [UIColor whiteColor];
 59     
 60     // photoImageView
 61     CGFloat photoImageViewX = 0;
 62     CGFloat photoImageViewY = 64;
 63     CGFloat photoImageViewW = self.view.bounds.size.width;
 64     CGFloat photoImageViewH = 250;
 65     _photoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(photoImageViewX, photoImageViewY, photoImageViewW, photoImageViewH)];
 66     _photoImageView.userInteractionEnabled = YES;
 67     [self.view addSubview:_photoImageView];
 68     
 69     // btn
 70     CGFloat btnW = 100;
 71     CGFloat btnH = 30;
 72     CGFloat btnX = (self.view.bounds.size.width - btnW) / 2.0;
 73     CGFloat btnY = CGRectGetMaxY(_photoImageView.frame) + 20;
 74     UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
 75     btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
 76     [btn setTitle:@"调取摄像头" forState:UIControlStateNormal];
 77     btn.tintColor = [UIColor blueColor];
 78     btn.backgroundColor = [UIColor grayColor];
 79     btn.titleLabel.font = [UIFont boldSystemFontOfSize:15];
 80     [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
 81     [self.view addSubview:btn];
 82    
 83     // 当前程序为录制视频
 84     _isVideo = YES;
 85 }
 86 
 87 #pragma mark - 点击事件
 88 // 点击调取摄像头
 89 - (void)btnClick:(UIButton *)btn
 90 {
 91     if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
 92         self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
 93     }
 94     [self presentViewController:self.imagePicker animated:YES completion:nil];
 95 }
 96 
 97 #pragma mark - 代理方法<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
 98 // 1.完成
 99 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
100 {
101     // 获取媒体类型
102     NSString *mediaType = info[UIImagePickerControllerMediaType];
103     if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
104         UIImage *image = nil;
105         // 如果允许编辑,则获得编辑后的图片,否则获取原始图片
106         if (self.imagePicker.allowsEditing) {
107             // 获得编辑后的图片
108             image = info[UIImagePickerControllerEditedImage];
109         } else {
110             // 获取原始图片
111             image = info[UIImagePickerControllerOriginalImage];
112         }
113         
114         if (image) {
115             // 显示照片
116             self.photoImageView.image = image;
117             // 保存照片到相册
118             UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
119         }
120     
121     } else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
122         // 获取录制视频路径
123         NSURL *url = info[UIImagePickerControllerMediaURL];
124         NSString *urlStr = url.path;
125         if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(urlStr)) {
126             // 保存视频到相簿,注意也可以使用ALAssetsLibrary来保存
127             UISaveVideoAtPathToSavedPhotosAlbum(urlStr, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);
128         }
129     }
130     [self dismissViewControllerAnimated:YES completion:nil];
131 }
132 // 2.取消
133 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
134 {
135     NSLog(@"取消");
136 }
137 
138 #pragma mark - 视频保存后的回调
139 - (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
140 {
141     if (error) {
142         NSLog(@"保存视频过程中发生错误,错误信息:%@",error.localizedDescription);
143         
144     } else {
145         NSLog(@"视频保存成功.");
146         // 录制完之后自动播放
147         NSURL *url = [NSURL fileURLWithPath:videoPath];
148         _player = [AVPlayer playerWithURL:url];
149         AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
150         playerLayer.frame = self.photoImageView.bounds;
151         [self.photoImageView.layer addSublayer:playerLayer];
152         [_player play];
153     }
154 }
155 
156 @end

2.AVFoundation的拍照和视频录制(可自定义)

 1)说明

  UIImagePickerController的功能很强大,但是它与MPMoviePlayerController类似,由于它的高度封装性,要进行某些自定义工作就比较复杂了。那么如果需要自定义,我们可以使用AVFoundation来实现。AVFoundation框架中提供了很多现成的播放器和录音机,但是事实上它还是提供了一些更底层的内容供开发者使用。因为AVFoundation框架中抽了很多底层输入、输出设备打交道的类,依靠这些类开发人员面对的不再是封装好的音频播放器(AVAudioPlayer)、录音机(AVAudioRecorder)、视频(包括音频)播放器(AVPlayer),而是输入设备(例如:麦克风、摄像头)、输出设备(图片、视频)等。

 2)AVFoundation拍照和视频录制的相关类:

  a.AVCaptureSession:媒体(音、视频)捕获会话,负责把捕获的音视频数据输出到输出设备中。一个AVCaptureSession可以有多个输入输出

  b.AVCaptureDevice:输入设备,包括麦克风、摄像头,通过该对象可以设置物理设备的一些属性(例如相机聚焦、白平衡等)。

  c.AVCaptureDeviceInput:设备输入数据管理对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,该对象将会被添加到AVCaptureSession

  d.AVCaptureOutput:输出数据管理对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutput、AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,该对象将会被添加到AVCaptureSession中管理。注意:前面几个对象的输出数据都是NSData类型,而AVCaptureFileOutput代表数据以文件形式输出,类似的,AVCcaptureFileOutput也不会直接创建使用,通常会使用其子类:AVCaptureAudioFileOutput、AVCaptureMovieFileOutput。当把一个输入或者输出添加到AVCaptureSession之后,AVCaptureSession就会在所有相符的输入、输出设备之间建立连接(AVCaptionConnection)

  e.AVCaptureVideoPreviewLayer:相机拍摄预览图层,是CALayer的子类,使用该对象可以实时查看拍照或视频录制效果,创建该对象需要指定对应的AVCaptureSession对象。

 3)使用AVFoundation拍照和录制视频的一般步骤

  a.创建AVCaptureSession对象

  b.使用AVCaptureDevice的静态方法获得需要使用的设备,例如:拍照和录像就需要获得摄像头设备,录音就要获得麦克风设备

  c.利用输入设备AVCaptureDevice初始化AVCaptureDeviceInput对象

  d.初始化输出数据管理对象,如果要拍照就初始化AVCaptureStillImageOutput对象;如果拍摄视频就初始化AVCaptureMovieFileOutput对象

  e.将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中

  f.创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器中,调用AVCaptureSession的startRunning方法开始捕获

  g.将捕获的音频或视频数据输出到指定问件

 4)代码示例(这里就不做详细写啦,关联博客:http://www.cnblogs.com/kenshincui/p/4186022.html#uiImagePickerController)

 

四、总结

posted @ 2016-06-01 11:14  Frank9098  阅读(380)  评论(0)    收藏  举报