iOS中视频播放的基本办法总结
前言
本文了iOS中最常见的视频播放方法,不同的方法都各具特点,我电脑维修网希望能够它们的不同,方便在开发中选择合适的技术方案。
Apple为我们提供了多种方法来实现视频播放,包括MPMoviePlayerController,MPMoviePlayerVieController,AVPlayer,AVPlayerVieController等。而值得注意的是,上述的MPMoviePlayerController与MPMoviePlayerVieController在iOS9.0之后被弃用。虽说如此,这还是将它们的用法了一下,下面我们简单来了解一下四种播放方式的区别
iOS播放视频.png
温馨提示代码更直观,附上本文Demo (本地下载)
一、MPMoviePlayerController1.播放视频
MPMoviePlayerController继承于NSObject,使用它播放视频需要将其自带的视频Vie添加到视图控制器的Vie上才能显示视频,使用步骤如下
第一步:引用MediaPlayer框架,声明视图控制器属性PlayerController #import@property(nonatomic,strong)MPMoviePlayerController playerController;
//第二步获取视频路径,创建播放器 //本地视频路径 NSString localFilePath=[[NSBundle mainBundle]pathForResource:@"不能说的秘密" ofType:@"mp4"]; NSURL localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //网络视频路径 NSString ebVideoPath = @"http://api.junqingguanchashi./yunpan/bd/c.php?vid=/junqing/1115.mp4"; NSURL ebVideoUrl = [NSURL URLWithString:ebVideoPath]; self.playerController =[[MPMoviePlayerController alloc]initWithContentURL:ebVideoUrl];
//第三步设置frame将播放器Vie添加到视图控制器Vie上 self.playerController.vie.frame = CGRectMake(0, 10, kDeviceWidth, 300); [self.vie addSubvie: self.playerController.vie];
//第四步设置播放器属性 //设置控制面板风格:无,嵌入,全屏,默认 self.playerController.controlStyle = MPMovieControlStyleDefault; //设置是否自动播放(默认为YES) self.playerController.shouldAulay = NO; //设置播放器显示模式,类似于图片的处理,设置Fill有可能造成部分区域被裁剪 self.playerController.scalingMode = MPMovieScalingModeAspectFit; //设置重复模式 self.playerController.repeatMode = MPMovieRepeatModeOne;
//第五步播放视频 //播放前的准备,会中断当前正在活跃的音频会话 [ self.playerController prepareToPlay]; //播放视频,设置了自动播放之后可以不调用此方法 //[ self.playerController play];
//第六步在退出界面的时候,关闭播放器,移除通知
- (void)dealloc{
//当前视图控制器pop之后并不会关闭播放,需要手动关闭
[self.playerController s];
self.playerController = nil;
//移除播放器相关的通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
2.视频播放相关的通知
MPMoviePlayerController有关视频播放的很多状态控制都是通过通知完成的,尤其是播放在线视频的时候,我们不仅监控视频加载是否成功,也会监控是视频缓存进度等。这里演示一些常用的通知如下
//关于通知的使用(还有很多通知可以监听,可查看SDK) NSNotificationCenter notificaionCenter = [NSNotificationCenter defaultCenter]; //监听播放器状态的变化 [notificaionCenter addObserver:self selector:@selector(playerStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; //监听播放完成 [notificaionCenter addObserver:self selector:@selector(playerFinished) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; //监听切换到全屏 [notificaionCenter addObserver:self selector:@selector(palyerChangeFullScreen) name:MPMoviePlayerDidEnterFullscreenNotification object:nil]; //监听截屏操作完成 [notificaionCenter addObserver:self selector:@selector(playerCaptureFinished:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:nil];
#pragma mark - 监听通知的响应方法
//播放状态变化,注意播放完成时的状态是暂停
- (void)playerStateChanged:(NSNotification )notificaion{
sitch (self.playerController.playbackState) {
case MPMoviePlaybackStateSped:{
NSLog(@"播放停止");
break;
}
case MPMoviePlaybackStatePlaying:{
NSLog(@"播放器正在播放");
break;
}
case MPMoviePlaybackStatePaused:{
NSLog(@"播放器暂停");
break;
}
case MPMoviePlaybackStateInterrupted:{
NSLog(@"播放器中断");
break;
}
case MPMoviePlaybackStateSeekingForard:{
NSLog(@"播放器快进");
break;
}
case MPMoviePlaybackStateSeekingBackard:{
NSLog(@"播放器快退");
break;
}
default:
break;
}
}
//视频播放结束
- (void)playerFinished{
NSLog(@"playerFinished播放结束");
}
//播放器切换到了全屏
- (void)palyerChangeFullScreen{
NSLog(@"palyerChangeFullScreen播放器进入全屏");
}
//播放器截屏结束
- (void)playerCaptureFinished:(NSNotification )notification{
//获取并显示截图
UIImage image=notification.userInfo[MPMoviePlayerThumbnailImageKey];
self.captureImgVie.image = image;
}
3.实现截屏
//添加一个按钮,点击开始截屏 _captureBtn = [[UIButton alloc] initWithframe:CGRectMake(30, CGRectGetMaxY(self.playerController.vie.frame) + 30, kDeviceWidth - 30 2, 50)]; _captureBtn.backgroundColor = [UIColor purpleColor]; [_captureBtn setTitle:@"截图当前屏幕" forState: UIControlStateNormal]; [_captureBtn addTarget:self action:@selector(captureCurrentScreenImg) forControlEvents:UIControlEventTouchUpInside]; [self.vie addSubvie:_captureBtn]; //添加一个ImgVie 显示截屏后的图片 _captureImgVie = [[UIImageVie alloc] initWithframe:CGRectMake((kDeviceWidth - 150)/2, CGRectGetMaxY(_captureBtn.frame) + 20, 150, 150)]; _captureImgVie.contentMode = UIVieContentModeScaleAspectFit; _captureImgVie.backgroundColor = [UIColor grayColor]; [self.vie addSubvie:_captureImgVie];
//截取当前屏幕
- (void)captureCurrentScreenImg{
[self.playerController requestThumbnailImagesAtTimes:@[@(self.playerController.currentPlaybackTime)] timeOption:MPMovieTimeOptionNearestKeyframe];
}
//监听通知播放器截屏结束,得到图片并显示截图
- (void)playerCaptureFinished:(NSNotification )notification{
UIImage image=notification.userInfo[MPMoviePlayerThumbnailImageKey];
self.captureImgVie.image = image;
}
二、MPMoviePlayerVieController
MPMovicePlayerVieControlle只能全屏幕播放视频,它是一个包含了MPMoviePlayerController类型属性的特殊视图控制器,它是通过模态视图弹出的方式显示视频的。理解了这个,我们就可以知道在使用MPMovicePlayerVieController的时候我们可以通过它的MPMoviePlayerController属性设置很多播放器的属性了,具体用法和MPMoviePlayerController相同,就不过多的解释了,播放视频的代码示例如下;
//第一步:获取视频路径 //本地视频 NSString localFilePath=[[NSBundle mainBundle]pathForResource:@"不能说的秘密" ofType:@"mp4"]; NSURL localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //在线视频 //NSString ebVideoPath = @"http://api.junqingguanchashi./yunpan/bd/c.php?vid=/junqing/1115.mp4"; //NSURL ebVideoUrl = [NSURL URLWithString:ebVideoPath]; //第二步:创建视频播放器 MPMoviePlayerVieController playerVieController = [[MPMoviePlayerVieController alloc] initWithContentURL:localVideoUrl]; //第三步:设置播放器属性 //通过moviePlayer属性设置播放器属性(与MPMoviePlayerController类似) playerVieController.moviePlayer.scalingMode = MPMovieScalingModeFill; //第四步:跳转视频播放界面 [self presentVieController:playerVieController animated:YES pletion:nil];三、AVPlayer
AVPlayer相比上述两种方式,播放视频功能更加强大,使用也十分灵活,因为它更加接近底层。AVPlayer本身是不能直接显示视频的,必须创建一个播放层AVPlayerLayer并将其添加到其他的视图Layer上才能显示。
1. 使用AVPlayer需要了解的常用类
- AVAsset一个用于获取多媒体信息的抽象类,但不能直接使用
- AVURLAssetAVAsset的子类,可以根据一个URL路径创建一个包含媒体信息的AVURLAsset对象
- AVPlayerItem一个媒体资源管理对象,用于管理视频的基本信息和状态,一个AVPlayerItem对应一个视频资源
- AVPlayer负责视频播放、暂停、时间控制等操作
- AVPlayerLayer负责显示视频的图层,如果不设置此属性,视频就只有声音没有图像
2. AVPlayer的使用步骤
//第一步:引用AVFoundation框架,添加播放器属性 #import @property (nonatomic,strong)AVPlayer player;//播放器对象 @property (nonatomic,strong)AVPlayerItem currentPlayerItem;
//第二步:获取播放地址URL //本地视频路径 NSString localFilePath=[[NSBundle mainBundle]pathForResource:@"不能说的秘密" ofType:@"mp4"]; NSURL localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //网络视频路径 NSString ebVideoPath = @"http://api.junqingguanchashi./yunpan/bd/c.php?vid=/junqing/1129.mp4"; NSURL ebVideoUrl = [NSURL URLWithString:ebVideoPath];
//第三步:创建播放器(四种方法) //如果使用URL创建的方式会默认为AVPlayer创建一个AVPlayerItem //self.player = [AVPlayer playerWithURL:localVideoUrl]; //self.player = [[AVPlayer alloc] initWithURL:localVideoUrl]; //self.player = [AVPlayer playerWithPlayerItem:playerItem]; AVPlayerItem playerItem = [[AVPlayerItem alloc] initWithURL:ebVideoUrl]; self.currentPlayerItem = playerItem; self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
//第四步:创建显示视频的AVPlayerLayer,设置视频显示属性,并添加视频图层 //contentVie是一个普通Vie,用于放置视频视图 AVPlayerLayer avLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; avLayer.videoGravity = AVLayerVideoGravityResizeAspect; avLayer.frame = _containerVie.bounds; [_containerVie.layer addSublayer:avLayer];
//第六步执行play方法,开始播放 //本地视频可以直接播放 //网络视频需要监测AVPlayerItem的status属性为AVPlayerStatusReadyToPlay时方法才会生效 [self.player play];
3. 添加属性观察
一个AVPlayerItem对象对应着一个视频,我们需要通过AVPlayerItem来获取视频属性。AVPlayerItem必须是在视频资源加载到可以播放的时候才能使用,这是受限于网络的原因。解决这一问题,我们需要使用KVO监测AVPlayerItem的status属性,当其为AVPlayerItemStatusReadyToPlay的时候我们才能获取视频相关属性。相关的代码示例如下
//1.注册观察者,监测播放器属性 //观察Status属性,可以在加载成功之后得到视频的长度 [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNe context:nil]; //观察loadedTimeRanges,可以获取缓存进度,实现缓冲进度条 [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNe context:nil];
//2.添加属性观察
- (void)observevalueForKeyPath:(NSString )keyPath
ofObject:(id)object
change:(NSDictionary )change
context:(void )context {
AVPlayerItem playerItem = (AVPlayerItem )object;
if ([keyPath isEqualToString:@"status"]) {
//获取playerItem的status属性最新的状态
AVPlayerStatus status = [[change objectForKey:@"ne"] intValue];
sitch (status) {
case AVPlayerStatusReadyToPlay:{
//获取视频长度
CMTime duration = playerItem.duration;
//更新显示:视频总时长(自定义方法显示时间的格式)
self.totalNeedPlayTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(duration)];
//开启滑块的滑动功能
self.sliderVie.enabled = YES;
//关闭加载Loading提示
[self shoaAtivityInDicatorVie:NO];
//开始播放视频
[self.player play];
break;
}
case AVPlayerStatusFailed:{//视频加载失败,点击重新加载
[self shoaAtivityInDicatorVie:NO];//关闭Loading视图
self.playerInfoButton.hidden = NO; //显示错误提示按钮,点击后重新加载视频
[self.playerInfoButton setTitle:@"资源加载失败,点击继续尝试加载" forState: UIControlStateNormal];
break;
}
case AVPlayerStatusUnknon:{
NSLog(@"加载遇到未知问题:AVPlayerStatusUnknon");
break;
}
default:
break;
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
//获取视频缓冲进度数组,这些缓冲的数组可能不是连续的
NSArray loadedTimeRanges = playerItem.loadedTimeRanges;
//获取最新的缓冲区间
CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangevalue];
//缓冲区间的开始的时间
NSTimeInterval loadStartSeconds = CMTimeGetSeconds(timeRange.start);
//缓冲区间的时长
NSTimeInterval loadDurationSeconds = CMTimeGetSeconds(timeRange.duration);
//当前视频缓冲时间总长度
NSTimeInterval currentLoadTotalTime = loadStartSeconds + loadDurationSeconds;
//NSLog(@"开始缓冲:%f,缓冲时长:%f,总时间:%f", loadStartSeconds, loadDurationSeconds, currentLoadTotalTime);
//更新显示当前缓冲总时长
_currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime];
//更新显示视频的总时长
_totalNeedLoadTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(self.player.currentItem.duration)];
//更新显示缓冲进度条的值
_progressVie.progress = currentLoadTotalTime/CMTimeGetSeconds(self.player.currentItem.duration);
}
}
//转换时间格式的方法
- (NSString )formatTimeWithTimeInterVal:(NSTimeInterval)timeInterVal{
int minute = 0, hour = 0, secend = timeInterVal;
minute = (secend % 3600)/60;
hour = secend / 3600;
secend = secend % 60;
return [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, secend];
}
4. 获取当前播放时间与总时间
在此之前我们需要了解一个数据类型,也就是上述操作中的CMTime, 在AVPlayer的使用中我们会经常用到它,其实CMTime是一个结构体如下
typedef struct{
CMTimevalue value; // 帧数
CMTimeScale timescale; // 帧率(影片每秒有几帧)
CMTimeFlags flags;
CMTimeEpoch epoch;
} CMTi
在上面的操作中我们看到AVPlayerItem的Duration属性就是一个CMTime类型的数据。所以获取视频的总时长(秒)需要duration.value/duration.timeScale。系统也为我们提供了CMTimeGetSeconds函数更加方便计算:
总时长: duration.value == CMTimeGetSeconds(duration) 。
在快进视频到某一个位置的时候我们也需要创建CMTime作为参数,那么CMTime的创建方法有两种:
//方法1 CMTimeMakeWithSeconds(Flout64 seconds, int32_t scale) //方法2 CMTimeMake(int64_t value, int32_t scale) //注两者的区别在于方法一的第一个参数可以是float
至于获取视频的总时间在上述代码中已有体现,是在检测播放状态变为AVPlayerStatusReadyToPlay的时候获取的
//视频总时长,在AVPlayerItem状态为AVPlayerStatusReadyToPlay时获取 CMTime duration = self.player.currentItem.duration; CGFloat totalTime = CMTimeGetSeconds(duration); //当前AVPlayer的播放时长 CMTime cmTime = self.player.currentTime; CGFloat currentTime = CMTimeGetSeconds(cmTime);
5. 播放进度与状态的刷新
实时更新当前播放时间,这时候我们不必使用定时器,因为AVPlayer已经提供了方法
addPeriodicTimeObserverForInterval: queue: usingBlock。当播放进度改变的时候方法中的回调会被执行。我们可以在这里做刷新时间的操作,代码示例如下
__eak __typeof(self) eakSelf = self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
//当前播放的时间
NSTimeInterval currentTime = CMTimeGetSeconds(time);
//视频的总时间
NSTimeInterval totalTime = CMTimeGetSeconds(eakSelf.player.currentItem.duration);
//设置滑块的当前进度
eakSelf.sliderVie.value = currentTime/totalTime;
//设置显示的时间以00:00:00的格式
eakSelf.currentTimeLabel.text = [eakSelf formatTimeWithTimeInterVal:currentTime];
}];
6. 滑块拖拽修改视频播放进度
//UISlider的响应方法:拖动滑块,改变播放进度
- (IBAction)sliderVieChange:(id)sender {
if(self.player.status == AVPlayerStatusReadyToPlay){
NSTimeInterval playTime = self.sliderVie.value CMTimeGetSeconds(self.player.currentItem.duration);
CMTime seekTime = CMTimeMake(playTime, 1);
[self.player seekToTime:seekTime pletionHandler:^(BOOL finished) {
}];
}
}
四、AVPlayerVieController
AVPlayerVieController是iOS8新增视频框架AVKit中的一个播放器类。由于iOS9弃用前两种播放器类的原因,AVPlayerVieController也将变得更加常用。AVPlayerVieController适合开发播放界面要求不是很高的应用。其相比AVPlayer的使用更加方便,原理上还是AVPlayerVieController包含了一个AVPlayer对象。
AVPlayerVieController有两种播放视频的方式
第一种:直接弹出模态视图控制器播放
//步骤1获取视频路径 NSString ebVideoPath = @"http://api.junqingguanchashi./yunpan/bd/c.php?vid=/junqing/1213.mp4"; NSURL ebVideoUrl = [NSURL URLWithString:ebVideoPath]; //步骤2创建AVPlayer AVPlayer avPlayer = [[AVPlayer alloc] initWithURL:ebVideoUrl]; //步骤3使用AVPlayer创建AVPlayerVieController,并跳转播放界面 AVPlayerVieController avPlayerVC =[[AVPlayerVieController alloc] init]; avPlayerVC.player = avPlayer; [self presentVieController:avPlayerVC animated:YES pletion:nil];
第二种添加AVPlayerVieController的Vie到父视图上播放。
使用这种方式播放的优点在于可以指定播放界面的原始尺寸大小,值得注意的是AVPlayerVieController必须被当前视图控制器所持有,以防止被当做局部变量被释放。为了满足这一条件,我们可以将AVPlayerVieController作为属性,也可以使用addChildVieController方法将其作为当前视图控制器的子视图控制器,示例代码如下
//步骤1获取视频路径 NSString ebVideoPath = @"http://api.junqingguanchashi./yunpan/bd/c.php?vid=/junqing/1213.mp4"; NSURL ebVideoUrl = [NSURL URLWithString:ebVideoPath]; //步骤2创建AVPlayer AVPlayer avPlayer = [[AVPlayer alloc] initWithURL:ebVideoUrl]; //步骤3使用AVPlayer创建AVPlayerVieController,并跳转播放界面 AVPlayerVieController avPlayerVC =[[AVPlayerVieController alloc] init]; avPlayerVC.player = avPlayer; //步骤4设置播放器视图大小 avPlayerVC.vie.frame = CGRectMake(25, 0, 320, 300); //特别注意:AVPlayerVieController不能作为局部变量被释放,否则无法播放成功 //解决1.AVPlayerVieController作为属性 //解决2:使用addChildVieController,AVPlayerVieController作为子视图控制器 [self addChildVieController:avPlayerVC]; [self.vie addSubvie:avPlayerVC.vie];
空调维修
- 我的世界电脑版运行身份怎么弄出来(我的世界
- 空调抽湿是什么意思,设置抽湿的温度有什么意
- 方太燃气灶有一个打不着火 怎么修复与排查方法
- 夏季免费清洗汽车空调的宣传口号
- 清洗完空调后出现漏水现象
- iphone6能玩什么游戏(iphone6游戏)
- 如何设置电脑密码锁屏(如何设置电脑密码锁屏
- win10删除开机密码提示不符合密码策略要求
- 电脑w7显示不是正版(w7不是正版怎么解决)
- 万家乐z8热水器显示e7解决 怎么修复与排查方法
- 1匹空调多少瓦数(1匹空调多少瓦)
- 安卓手机连接电脑用什么软件好(关于安卓手机
- 电脑网页看视频卡是什么原因(爱拍看视频卡)
- 华帝燃气灶点火器一直响然后熄火怎么办:问题
- 电脑壁纸怎么换(关于电脑壁纸怎么换的介绍)
- 冬天空调的出风口应该朝什么方向(冬天空调风