虽然对于视频播放之前就有所了解,但是一直没有过多的在意,更没有研究过,仅仅在于知道。
这篇文章仅仅是将视频播放的一些控件进行了简单的深入,并没有对框架等进行深挖,想看到更深层次知识的可以跳过了。下面是菜鸟的总结:
经过一周的了解,如果对于视频播放没有什么特殊的要求,只需要实现播放功能就很简单了。直接使用MPMoviePlayerViewController或者使用MPMoviePlayerController完全够用。两个类很简单,从名字上就可以看出,一个是ViewController,另一个是View。MPMoviePlayerViewController是对MPMoviePlayerController的封装,两个类用法,没有太大不同。
首先,MPMoviePlayerViewController:
需要注意一点即可:只能够Present,不能push。
第一,导入MediaPlayer.framework,将#import <MediaPlayer>导入
第二,创建,播放,监听播放的状态。
-
(void)viewDidLoad { [super viewDidLoad];
/* 打开本地视频 NSString url = [[NSBundlemainBundle]pathForResource:@"IMG_0322"ofType:@"mp4"]; MPMoviePlayerViewController playerViewController = [[MPMoviePlayerViewControlleralloc]initWithContentURL:[NSURLfileURLWithPath:url]]; /MPMoviePlayerViewController *movie = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]];
//播放模式 /**
- MPMovieControlStyleNone ————— 相当于播放视频,甚至没有返回操作
- MPMovieControlStyleEmbedded ———— 嵌入式播放
- MPMovieControlStyleFullscreen ———— 全屏播放 */ //播放器的样式 movie.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; //控制模式(触摸) // movie.moviePlayer.scalingMode = MPMovieScalingModeAspectFill;
//有的时候,播放器直接要求横屏播放 //先将整个项目设为竖屏模式,或者将此页面设为竖屏模式,然后写下以下三行代码,即可实现横屏 movie.moviePlayer.fullscreen = NO; CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI/2); movie.view.transform = transform;
movie.view.backgroundColor = [UIColor clearColor]; movie.view.frame = self.view.bounds; [movie.moviePlayer prepareToPlay]; [self.navigationController presentMoviePlayerViewControllerAnimated:movie];
//使用NSNotification监听播放器的各种状态 //停止播放 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:movie.moviePlayer];
//播放状态 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:movie];
/**
-
movie.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;状态下才调用 */ //控制器即将进入全屏 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventFullscreenHandler:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
//command+点击,可以看到notification都可以监听到哪些事件,此处不多列举了
}
/**
-
停止播放 */
- (void)movieFinishedCallback:(NSNotification *)noti{
MPMoviePlayerController* theMovie = [noti object]; [[NSNotificationCenter defaultCenter]removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie];
[self.navigationController dismissMoviePlayerViewControllerAnimated]; }
/**
- 播放状态改变,注意播放完成时的状态是暂停
-
@param notification 通知对象 / -(void)mediaPlayerPlaybackStateChange:(NSNotification )notification{
MPMoviePlayerController *theMovie = [notification object];
switch (theMovie.playbackState) { case MPMoviePlaybackStatePlaying: NSLog(@"正在播放..."); break; case MPMoviePlaybackStatePaused: NSLog(@"暂停播放."); break; case MPMoviePlaybackStateStopped: NSLog(@"停止播放."); break; default: NSLog(@"播放状态:%li",theMovie.playbackState); break; } }
-(void)movieEventFullscreenHandler(NSNotification *)notification{ NSLog(@"即将进入全屏."); }
-
(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController];
_moviePlayerController = nil; }
二.MPMoviePlayerController(基本和上面的ViewController类似,但是更具与优势,能自己控制播放状态) 稍微难一点的就是,想要点击横屏的按钮,实现横屏状态,花费了一点时间查阅资料,主要是两个通知(MPMoviePlayerWillEnterFullscreenNotification,MPMoviePlayerWillExitFullscreenNotification),监听实现。 但是如果项目一进页面就是实现横屏就简单了,直接上面介绍的MPMoviePlayerViewController的方法拿来使用,就足够了。
-
(void)viewDidLoad { [super viewDidLoad]; _moviePlayerController = [[MPMoviePlayerController alloc]initWithContentURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]]; // _moviePlayerController.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; [_moviePlayerController prepareToPlay]; [self.view addSubview:_moviePlayerController.view];
_moviePlayerController.shouldAutoplay=YES; _moviePlayerController.controlStyle = MPMovieControlStyleDefault; _moviePlayerController.scalingMode = MPMovieScalingModeAspectFit;
_moviePlayerController.repeatMode = MPMovieRepeatModeNone; [_moviePlayerController setFullscreen:NO];
[_moviePlayerController.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width)]; height = _moviePlayerController.view.frame.size.height;
//这里注册监听相关操作的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; //播放完后的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventWillFullscreenHandler:) name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventWillExitFullscreenHandler:) name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayerController];
} -
(IBAction)BtnClickAction:(id)sender {
if (sender==_startBtn) { [_moviePlayerController play]; }else if (sender==_stopBtn){ [_moviePlayerController stop]; }else if (sender==_pauseBtn){ [_moviePlayerController pause]; } }
/**
- 先将整个项目设为横竖屏模式,或者将此页面设为横竖屏模式,然后做以下操作,即可实现横屏竖屏切换 */
-
(void)movieEventWillFullscreenHandler:(NSNotification*)notification {
NSLog(@"即将进入全屏状态"); MPMoviePlayerController *theMovie = [notification object];
if ([UIScreen mainScreen].bounds.size.height <= height) {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"]; height = theMovie.view.frame.size.height;
} else {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"]; height = theMovie.view.frame.size.height;
} }
-
(void)movieEventWillExitFullscreenHandler:(NSNotification*)notification {
NSLog(@"即将退出全屏状态"); MPMoviePlayerController *theMovie = [notification object];
if ([UIScreen mainScreen].bounds.size.height <= height) {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"]; height = theMovie.view.frame.size.height; } else {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"]; height = theMovie.view.frame.size.height;
}
}
-
(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayerController];
_moviePlayerController = nil; }
MP类型的基本上在iOS 9之后就被废弃了,虽然好用,但不灵活,为了能够自定义播放起的外观,更方便的支持更多格式,AVFoundation兴起。 AVPlayer相信是现在大多数稍微要求高一点的视频播放都会选择的。
三.AVPlayer
首先,导入AVFoundation框架,在 .m导入#import
然后,初始化
-
(void)viewDidLoad { [super viewDidLoad];
self.title = @"AVPlayer";
self.progressSlider.minimumValue = 0; self.progressSlider.maximumValue = 100;
/**
- 需要先导入AVFoundation http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8 http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 ⬆️此视频播放会出现卡顿现象? 更深层的研究,边加载边播放? AVPlayer是先将视频下载下来吗? */
NSString playString = @"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"; NSURL url = [NSURL URLWithString:playString];
//设置播放的项目 _item = [[AVPlayerItem alloc] initWithURL:url];
//初始化player对象 self.player = [[AVPlayer alloc] initWithPlayerItem:_item];
//设置播放页面 _layer = [AVPlayerLayer playerLayerWithPlayer:_player]; //设置播放页面的大小 _layer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300); _layer.backgroundColor = [UIColor darkGrayColor].CGColor; //设置播放窗口和当前视图之间的比例显示内容 _layer.videoGravity = AVLayerVideoGravityResizeAspect; //添加播放视图到self.view [self.moviePlayerView.layer addSublayer:_layer]; //设置播放进度的默认值 self.progressSlider.value = 0; //设置播放的默认音量值(0.0-1,0) self.player.volume = 1.0;
block UISlider *blockSlider = self.progressSlider; block UILabel blockCurrentLab = self.currentTimeLab; // __block UILabel blockTotalLab = self.totalTimeLab; __block id blockSelf = self;
block UISlider *imgSlider = self.sliderImg; block UILabel *blockCurrentImgLab = self.currentImgLab;
//当前播放的进度,不想监听的话直接隐掉 id timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { float current = CMTimeGetSeconds(time);//当前播放秒数 float total = CMTimeGetSeconds(_item.duration);//总秒数 if (current) { // NSLog(@"%f", current / total);
blockSlider.value = current / total * 100; imgSlider.value = current / total * 100;
// blockTotalLab.text = [blockSelf convertTime:total]; blockCurrentLab.text = [blockSelf convertTime:current]; blockCurrentImgLab.text = [blockSelf convertTime:current];
}
}];
//添加自定义按钮
[self addImgViewButton];
//添加手势
[self addTapGestureRcognizeAction];
/**
* 监听屏幕旋转的通知
*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
//有add,必须remove,监听播放状态,否则返回上一页,播放器仍然没有被释放掉*****(五星,非常可重要)
[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性
//监听缓冲进度
// [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}
有几个比较费时间,个人感觉较难的,一个是屏幕旋转,一个是自定义的视屏播放器, 1.屏幕旋转 直接监听整个设备的旋转,一劳永逸,尝试了很多更改frame的,不尽满意。注意,在监听旋转的同时,仍然需要将player的父视图(moviePlayerView)的父视图(totalView)的frame进行相应调整
- (void)orientChange:(NSNotification *)noti
{
NSDictionary* ntfDict = [noti userInfo];
UIDeviceOrientation orient = [UIDevice currentDevice].orientation;
/*
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down */
CGFloat height = _layer.frame.size.height;
switch (orient)
{
case UIDeviceOrientationPortrait:
_lockBtn.hidden = YES;
_layer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300);
_totalView.frame = CGRectMake(0, 64, self.view.frame.size.width, 300);
_moviePlayerTapView.frame = CGRectMake(0, 64, self.view.frame.size.width, 300);
_layer.videoGravity = AVLayerVideoGravityResizeAspect;
height = _layer.frame.size.height;
break;
case UIDeviceOrientationLandscapeLeft:
_lockBtn.hidden = NO;
_layer.frame = self.view.frame;
_totalView.frame = self.view.frame;
_moviePlayerTapView.frame = self.view.frame;
_layer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.moviePlayerView.layer insertSublayer:_layer atIndex:0];
_stateBtn.userInteractionEnabled = YES;
[self.totalView bringSubviewToFront:_VolumnView];
[self.totalView bringSubviewToFront:_consoleView];
break;
case UIDeviceOrientationPortraitUpsideDown:
break;
case UIDeviceOrientationLandscapeRight:
_lockBtn.hidden = NO;
_layer.frame = self.view.frame;
_totalView.frame = self.view.frame;
_layer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.moviePlayerView.layer insertSublayer:_layer atIndex:0];
_stateBtn.userInteractionEnabled = YES;
[self.totalView bringSubviewToFront:_VolumnView];
[self.totalView bringSubviewToFront:_consoleView];
height = _layer.frame.size.height;
break;
default:
break;
}
}
监听播放状态notification调用方法、
-
(void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary )change context:(void *)context {
AVPlayerItem playerItem = (AVPlayerItem )object; if ([keyPath isEqualToString:@"status"]) { if ([playerItem status] == AVPlayerStatusReadyToPlay) { self.startBtn.userInteractionEnabled = YES; self.stateBtn.userInteractionEnabled = YES; self.sliderImg.userInteractionEnabled = YES; self.progressSlider.userInteractionEnabled = YES; NSLog(@"AVPlayerStatusReadyToPlay----视频已经可以开始播放");
CMTime duration = playerItem.duration;// 获取视频总长度 CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒 NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration)); self.totalTimeLab.text = [self convertTime:totalSecond]; self.totalImgLab.text = [self convertTime:totalSecond]; } else if ([playerItem status] == AVPlayerStatusFailed) { NSLog(@"AVPlayerStatusFailed------视频不能播放"); self.startBtn.userInteractionEnabled = NO; self.stateBtn.userInteractionEnabled = NO; self.sliderImg.userInteractionEnabled = NO; self.progressSlider.userInteractionEnabled = NO; }
} // else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { // NSLog(@"视频缓冲中。。。"); // NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度 // NSLog(@"Time Interval:%f",timeInterval); // CMTime duration = playerItem.duration; // CGFloat totalDuration = CMTimeGetSeconds(duration); // NSLog(@"缓冲总进度:%f",totalDuration); // } }
// //- (NSTimeInterval)availableDuration { // NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges]; // CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域 // float startSeconds = CMTimeGetSeconds(timeRange.start); // float durationSeconds = CMTimeGetSeconds(timeRange.duration); // NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度 // return result; //}
//秒数转时间
- (NSString )convertTime:(CGFloat)second{ NSDate d = [NSDate dateWithTimeIntervalSince1970:second]; NSDateFormatter formatter = [[NSDateFormatter alloc] init]; if (second/3600 >= 1) { [formatter setDateFormat:@"HH:mm:ss"]; } else { [formatter setDateFormat:@"mm:ss"]; } NSString showtimeNew = [formatter stringFromDate:d]; return showtimeNew; }
为了使播放器在viewWillDisappear的时候,被释放,必须移除notification*****(五星,重要)
-
(void)viewWillDisappear:(BOOL)animated{
[_item removeObserver:self forKeyPath:@"status" context:nil];
[self.player replaceCurrentItemWithPlayerItem:nil]; }
百度云:https://pan.baidu.com/s/1pLzWcsz 如有错误,敬请指正。