由于项目需求很多时候我们需要自己设计一个视频播放器,其实说是自定制,主要是在UI上进行改变,以及一些功能细节上加以丰富,比如快进,快退,全屏等。
如果你只是简单实现视频播放,想要最懒的方式,可以不用往下看了,直接用MPMoviePlayerViewController就好。
详细代码请参考https://github.com/gofey/LittleDemos里的视频播放器一项,已经封装成一个控件,下面只是介绍实现的过程和主要方法代码,
我是基于AVplayer进行的封装,首先需要三个元素(我也不知道该怎么称呼这几个,姑且这么叫吧)来获取和控制视频,如下:
self.playItem = [[AVPlayerItem alloc]initWithURL:self.video_url];
self.player = [AVPlayerplayerWithPlayerItem:self.playItem];
self.playerLayer = [AVPlayerLayerplayerLayerWithPlayer:self.player];
初始化同时,注册通知
//播放完毕通知
[[NSNotificationCenterdefaultCenter]addObserver:self
selector:@selector(videoPlayDidEnd)
name:AVPlayerItemDidPlayToEndTimeNotification
object:nil];
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotificationobject:nil];//监听是否触发home键挂起程序.
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotificationobject:nil];//监听是否重新进入程序程序.
初始化之后,就要添加观察者,监听事件
[playerItemaddObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];
[playerItemaddObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];
- (void)initSubViews:(CGRect)frame{
CGFloat bottomViewW = frame.size.width;
CGFloat bottomViewH =40;
CGFloat bottomViewX =0;
CGFloat bottomViewY = frame.size.height - bottomViewH;
barHeight = bottomViewH;
self.bottomView = [[BottomBaseViewalloc]initWithFrame:CGRectMake(bottomViewX, bottomViewY, bottomViewW, bottomViewH)];
[selfaddSubview:self.bottomView];
UIGestureRecognizer *gestureTap = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(bottomGesture:)];
[self.bottomViewaddGestureRecognizer:gestureTap];
UIGestureRecognizer *gestureLongPress = [[UILongPressGestureRecognizeralloc]initWithTarget:selfaction:@selector(bottomGesture:)];
[self.bottomViewaddGestureRecognizer:gestureLongPress];
UIGestureRecognizer *gesturePan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(bottomGesture:)];
[self.bottomViewaddGestureRecognizer:gesturePan];
self.bottomView.backgroundColor = [UIColorcolorWithRed:0green:0blue:0alpha:0.5];
// self.bottomView.timer = self.timer;
self.playButton = [UIButtonbuttonWithType:UIButtonTypeCustom];
[self.bottomViewaddSubview:self.playButton];
self.playButton.frame =CGRectMake(0,0, bottomViewH, bottomViewH);
[self.playButtonsetImage:[UIImageimageNamed:@"playBtnImg"]forState:UIControlStateNormal];
[self.playButtonsetImage:[UIImageimageNamed:@"playBtnImgselected"]forState:UIControlStateSelected];
[self.playButtonaddTarget:selfaction:@selector(playBtnTouchUpInside:)forControlEvents:UIControlEventTouchUpInside];
CGFloat fillScreenButtonX = bottomViewW - bottomViewH;
CGFloat fillScreenButtonY =0;
CGFloat fillScreenButtonW = bottomViewH;
CGFloat fillScreenButtonH = bottomViewH;
self.fillScreenButton = [[UIButtonalloc]initWithFrame:CGRectMake(fillScreenButtonX, fillScreenButtonY, fillScreenButtonW, fillScreenButtonH)];
[self.bottomViewaddSubview:self.fillScreenButton];
[self.fillScreenButtonsetImage:[UIImageimageNamed:@"fillScreenBtnImg"]forState:UIControlStateNormal];
[self.fillScreenButtonsetImage:[UIImageimageNamed:@"fillScreenBtnImgSelected"]forState:UIControlStateSelected];
[self.fillScreenButtonaddTarget:selfaction:@selector(fillScreenButtonTouchUpInside:)forControlEvents:UIControlEventTouchUpInside];
self.timeLabel = [[UILabelalloc]init];
self.timeLabel.font = [UIFontsystemFontOfSize:17];
self.timeLabel.text =@"00:00/00:00";
[self.bottomViewaddSubview:self.timeLabel];
self.timeLabel.textColor = [UIColorwhiteColor];
self.timeLabel.textAlignment =NSTextAlignmentCenter;
//计算实际frame大小,并将label的frame变成实际大小
CGSize size =CGSizeMake(1000,10000);
NSDictionary *attribute =@{NSFontAttributeName:self.timeLabel.font};
CGSize labelsize = [self.timeLabel.textboundingRectWithSize:sizeoptions:NSStringDrawingTruncatesLastVisibleLine |NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeadingattributes:attributecontext:nil].size;
CGFloat timeLabelY =0;
CGFloat timeLabelW = labelsize.width +10;
CGFloat timeLabelH = bottomViewH;
CGFloat timeLabelX = bottomViewW - fillScreenButtonW - timeLabelW -10;
self.timeLabel.frame =CGRectMake(timeLabelX, timeLabelY, timeLabelW, timeLabelH);
CGFloat silderX =CGRectGetMaxX(self.playButton.frame) +15;
CGFloat silderW = timeLabelX - silderX -15;
CGFloat silderH =10;
CGFloat silderY = (self.bottomView.bounds.size.height - silderH) / 2.0;
self.playSlider = [[UISlideralloc]initWithFrame:CGRectMake(silderX, silderY, silderW, silderH)];
[self.bottomViewaddSubview:self.playSlider];
[self.playSlidersetThumbImage:[UIImageimageNamed:@"iconfont-yuan"]forState:UIControlStateNormal];
[self.playSlidersetMinimumTrackTintColor:[UIColorblueColor] ];
[self.playSlidersetMaximumTrackTintColor:[UIColorlightGrayColor]];
[self.playSlideraddTarget:selfaction:@selector(progressValueChange:)forControlEvents:UIControlEventValueChanged];
[self.playSlideraddTarget:selfaction:@selector(playSliderTouchUp:)forControlEvents:UIControlEventTouchUpInside];
[self.playSlideraddTarget:selfaction:@selector(playSliderTouchUp:)forControlEvents:UIControlEventTouchUpOutside];
[selfaddSubview:self.progressTipView];
}
还有隐藏和添加状态栏的方法
- (void)hideBottomBar{
NSLog(@"hideBottomBar");
self.hidenBar =YES;
CGRect bottomRect =self.bottomView.frame;
bottomRect.origin.y =self.bounds.size.height;
[UIViewanimateWithDuration:0.4animations:^{
self.bottomView.frame = bottomRect;
}];
}
- (void)appearBottomBar{
NSLog(@"appearBottomBar");
self.hidenBar =NO;
CGRect bottomRect =self.bottomView.frame;
bottomRect.origin.y =self.bounds.size.height -self.bottomView.frame.size.height;
[UIViewanimateWithDuration:0.4animations:^{
self.bottomView.frame = bottomRect;
}];
self.timer = [NSTimerscheduledTimerWithTimeInterval:secondsTimertarget:selfselector:@selector(hideBottomBar)userInfo:nilrepeats:NO];
}
滑块控制进度的方法
- (void)progressValueChange:(UISlider *)sender{
[self.timerinvalidate];
if (self.player.rate == 0) {
[self.playerseekToTime:CMTimeMake(sender.value*self.totalTime*10,10.0)];
} elseif(self.player.rate ==1) {
[selfpause];
[self.playerseekToTime:CMTimeMake(sender.value*self.totalTime*10,10.0)];
}else{
NSLog(@"rate:%f",self.player.rate);
}
}
监听事件
//添加avplayer block回调
- (void)addProgressObserver{
__weak__typeof(self) weakSelf =self;
//这里设置每秒执行一次
[self.playeraddPeriodicTimeObserverForInterval:CMTimeMake(10.0,10.0)queue:dispatch_get_main_queue()usingBlock:^(CMTime time) {
NSTimeInterval current =CMTimeGetSeconds(time);//获取当前的播放时间
weakSelf.currentTime = current;
//如果没有手动拖拽在进行自动更新
[weakSelf.playSlidersetValue:(current /self.totalTime)animated:YES];
//设置UILabel视图显示当前播放时长
weakSelf.timeLabel.text = [weakSelfformatPlayTime:current];
}];
}
//注册观察者监听状态和缓冲
- (void)addObserverWithPlayerItem:(AVPlayerItem *)playerItem
{
[playerItem addObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];
[playerItem addObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];
}
//移除观察者,移除监听
- (void)removeObserveWithPlayerItem:(AVPlayerItem *)playerItem
{
[playerItem removeObserver:selfforKeyPath:@"loadedTimeRanges"];
[playerItem removeObserver:selfforKeyPath:@"status"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
AVPlayerItem *playItem = (AVPlayerItem *)object;
if ([keyPathisEqualToString:@"status"]) {
//当前播放器状态
if ([playItemstatus] ==AVPlayerItemStatusReadyToPlay) {
self.totalTime =CMTimeGetSeconds(playItem.duration);
//[self playBtnTouchUpInside:self.playButton];
self.timeLabel.text = [selfformatPlayTime:0.0f];
self.progressTipLabel.text =self.timeLabel.text;
}else{
NSLog(@"这个视频有毒...");
}
}
elseif([keyPathisEqualToString:@"loadedTimeRanges"]){
//计算缓冲
}
else{
NSLog(@"...");
}
}
- (void)videoPlayDidEnd{
NSLog(@"结束啦");
[self.playItemsetReversePlaybackEndTime:kCMTimeZero];
[self.playItemseekToTime:kCMTimeZero];
[self.playSlidersetValue:0.0animated:YES];
self.playButton.selected =NO;
}
- (void)applicationWillResignActive:(NSNotification *)notification
{
// printf("按理说是触发home按下\n");
if(self.player.rate == 1) {
isPlay =YES;
[selfpause];
}else{
isPlay =NO;
}
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
// printf("按理说是重新进来后响应\n");
if (isPlay) {
[selfplay];
}
}
全屏以及小屏方法
- (void)fillScreenButtonTouchUpInside:(UIButton *)sender
{sender.selected = !sender.selected;
if (sender.selected) {
//进入全屏
self.superView =self.superview;
[self.windowaddSubview:self];
[self.windowbringSubviewToFront:self];
[UIViewanimateWithDuration:0.2fanimations:^{
self.frame =CGRectMake(0,0, [UIScreenmainScreen].bounds.size.width, [UIScreenmainScreen].bounds.size.height);
// self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2.0, ([UIScreen mainScreen].bounds.size.height)/2.0);
[selfupdateSubViewsFrame:self.frame];
}];
}
else{
//返回原状态
[UIViewanimateWithDuration:0.2fanimations:^{
//旋转360度
self.frame =self.originalFrame;
[selfupdateSubViewsFrame:self.frame];
[self.superViewaddSubview:self];
}];
}
self.timer = [NSTimerscheduledTimerWithTimeInterval:secondsTimertarget:selfselector:@selector(hideBottomBar)userInfo:nilrepeats:NO];
}
- (void)updateSubViewsFrame:(CGRect )frame{
[self.playerLayerremoveFromSuperlayer];
self.playerLayer = [AVPlayerLayerplayerLayerWithPlayer:self.player];
self.playerLayer.frame =self.bounds;
[self.layeraddSublayer:self.playerLayer];
[selfbringSubviewToFront:self.bottomView];
CGFloat bottomViewW = frame.size.width;
CGFloat bottomViewH =barHeight;
CGFloat bottomViewX =0;
CGFloat bottomViewY = frame.size.height - bottomViewH;
self.bottomView.frame =CGRectMake(bottomViewX, bottomViewY, bottomViewW, bottomViewH);
self.center =CGPointMake(self.frame.origin.x +self.frame.size.width/2.0,self.frame.origin.y +self.frame.size.height/2.0);
self.playButton.frame =CGRectMake(0,0, bottomViewH, bottomViewH);
CGFloat fillScreenButtonX = bottomViewW - bottomViewH;
CGFloat fillScreenButtonY =0;
CGFloat fillScreenButtonW = bottomViewH;
CGFloat fillScreenButtonH = bottomViewH;
self.fillScreenButton.frame =CGRectMake(fillScreenButtonX, fillScreenButtonY, fillScreenButtonW, fillScreenButtonH);
CGSize size =CGSizeMake(1000,10000);
NSDictionary *attribute =@{NSFontAttributeName:self.timeLabel.font};
CGSize labelsize = [self.timeLabel.textboundingRectWithSize:sizeoptions:NSStringDrawingTruncatesLastVisibleLine |NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeadingattributes:attributecontext:nil].size;
CGFloat timeLabelY =0;
CGFloat timeLabelW = labelsize.width +10;
CGFloat timeLabelH = bottomViewH;
CGFloat timeLabelX = bottomViewW - fillScreenButtonW - timeLabelW -10;
self.timeLabel.frame =CGRectMake(timeLabelX, timeLabelY, timeLabelW, timeLabelH);
CGFloat silderX =CGRectGetMaxX(self.playButton.frame) +15;
CGFloat silderW = timeLabelX - silderX -15;
CGFloat silderH =10;
CGFloat silderY = (self.bottomView.bounds.size.height - silderH) / 2.0;
self.playSlider.frame =CGRectMake(silderX, silderY, silderW, silderH);
}