用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。
来源:互联网 发布:校园网络电视台方案 编辑:程序博客网 时间:2024/05/01 11:53
转自:
用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。
支持HLS协议和HTTP协议的视频。
#import "AppDelegate.h"#import "PlayViewController.h"@interface AppDelegate ()@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { PlayViewController *player=[[PlayViewController alloc]init]; //HLS协议视屏 //http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8 //HTTP视频 //http://www.modrails.com/videos/passenger_nginx.mov player.urlString=@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; player.title=@"播放测试"; self.window.rootViewController=player; return YES;}@end
#import "PlayViewController.h"#import <AVFoundation/AVFoundation.h>#import <MediaPlayer/MediaPlayer.h>#import "Tools.h"#import "FGGReachability.h"@interface PlayViewController ()@property(nonatomic,strong)AVPlayer *player;@property(nonatomic,strong)AVPlayerItem *item;@property(nonatomic,strong)NSString *totalTime;@property(nonatomic,assign)BOOL isPlaying;@property(nonatomic,strong)id timeObserver;@property(nonatomic,strong)UIProgressView *progress;@property(nonatomic,strong)UISlider *slider; //进度slider@property(nonatomic,strong)UISlider *volumeSlider;//音量slider@property(nonatomic,strong)UIButton *playBtn;@property(nonatomic,strong)UILabel *timeLabel;@property(nonatomic,strong)id playbackTimeObserver;//快进@property(nonatomic,strong)UIImageView *forward;//快退@property(nonatomic,strong)UIImageView *backward;@property(nonatomic,strong)UILabel *volumeLabel;//调节音量时显示当前音量@property(nonatomic,strong)UIButton *shareBtn;//分享//加载指示器@property(nonatomic,strong)UIActivityIndicatorView *indicator;//判断网络@property(nonatomic,strong)Reachability *reach;@end#define kWidth ([UIScreen mainScreen].bounds.size.width)#define kHeight ([UIScreen mainScreen].bounds.size.height)@implementation PlayViewController-(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated];// 隐藏导航栏 self.navigationController.navigationBarHidden=YES;}-(void)showIndicator{ _indicator=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; _indicator.center=CGPointMake(kHeight/2, kWidth/2); [self.view addSubview:_indicator]; [_indicator startAnimating];}//隐藏状态栏- (BOOL)prefersStatusBarHidden{ return YES;}- (void)viewDidLoad{ [super viewDidLoad]; self.view.backgroundColor=[UIColor blackColor]; //显示网络记载指示器 [self showIndicator]; NSLog(@"\n-->%@\n-->%@",self.urlString,self.videoTitle); //通知中心注册一条监听网络状态的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(estimateNetworkStatus) name:kReachabilityChangedNotification object:nil]; _reach=[Reachability reachabilityForInternetConnection]; [_reach startNotifier]; [self estimateNetworkStatus]; _item=[[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:self.urlString]]; _player=[[AVPlayer alloc]initWithPlayerItem:_item]; [_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性 [_item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];// 监听loadedTimeRanges属性 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_item]; AVPlayerLayer *layer=[AVPlayerLayer playerLayerWithPlayer:_player]; layer.videoGravity=AVLayerVideoGravityResizeAspect; layer.frame=CGRectMake(0, 0, kHeight, kWidth); layer.backgroundColor=[UIColor clearColor].CGColor; [self.view.layer addSublayer:layer]; // 标题 [self createTitleView]; // 界面 [self createUI]; //旋转视图 //self.view.transform=CGAffineTransformMakeRotation(3*M_PI_2); //layer.transform=CATransform3DMakeRotation(M_PI/2, 0, 0, 1); self.view.layer.transform=CATransform3DMakeRotation(3*M_PI_2, 0, 0, 1); [self addGestures];}/** * 添加手势 */-(void)addGestures{ //添加一个手势 隐藏与显示进度条 UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(shiftStatus)]; [self.view addGestureRecognizer:tap]; //增加音量的手势 UISwipeGestureRecognizer *increaseGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(increaseVolume:)]; increaseGesture.direction=UISwipeGestureRecognizerDirectionUp; [self.view addGestureRecognizer:increaseGesture]; //减小音量的手势 UISwipeGestureRecognizer *decreaseGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(decreaseVolume:)]; decreaseGesture.direction=UISwipeGestureRecognizerDirectionDown; [self.view addGestureRecognizer:decreaseGesture]; //快进 UISwipeGestureRecognizer *stepForward=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(stepForward10Seconds)]; stepForward.direction=UISwipeGestureRecognizerDirectionRight; //快退 UISwipeGestureRecognizer *stepBackward=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(stepBackward10Senconds)]; stepBackward.direction=UISwipeGestureRecognizerDirectionLeft; [self.view addGestureRecognizer:stepForward]; [self.view addGestureRecognizer:stepBackward];}/** * 评估网络状态 */-(void)estimateNetworkStatus{ FGGNetWorkStatus status=[FGGReachability networkStatus]; if(status==FGGNetWorkStatusNotReachable) { UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示" message:@"当前网络不可用" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]; [alert show]; } else if(status!=FGGNetWorkStatusWifi) { UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示" message:@"非Wifi环境播放视屏会耗费流量!" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]; [alert show]; }}/** * 向上滑动增加音量 * * @param sender 滑动手势 */-(void)increaseVolume:(UISwipeGestureRecognizer *)sender{ if(sender.direction==UISwipeGestureRecognizerDirectionUp) { if(_player.volume>=1.0) return; _player.volume+=0.1;// NSLog(@"+++++音量:%f++++++",10*_player.volume); weak PlayViewController *weakSelf=self; _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceilf(_player.volume*10)]; [UIView animateWithDuration:0.2 animations:^{ weakSelf.volumeLabel.alpha=1.0; } completion:^(BOOL finished) { [UIView animateWithDuration:0.2 animations:^{ weakSelf.volumeLabel.alpha=0.0; }]; }]; }}/** * 向下滑动减小音量 * * @param sender 滑动手势对象 */-(void)decreaseVolume:(UISwipeGestureRecognizer *)sender{ if(sender.direction==UISwipeGestureRecognizerDirectionDown) { if(_player.volume<=0.0) return; _player.volume-=0.1;// NSLog(@"------音量:%f------",10*_player.volume); weak PlayViewController *weakSelf=self; _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceilf(_player.volume*10)]; [UIView animateWithDuration:0.3 animations:^{ weakSelf.volumeLabel.alpha=1.0; } completion:^(BOOL finished) { [UIView animateWithDuration:0.3 animations:^{ weakSelf.volumeLabel.alpha=0.0; }]; }]; }}#pragma mark -#pragma mark - 快退快进//@qustion:seekToTime和seekToTime:toleranceBefore:toleranceAfter的区别是什么?//快进10秒-(void)stepForward10Seconds{ if(_isPlaying) { [_item seekToTime:CMTimeMakeWithSeconds(_item.currentTime.value/_item.currentTime.timescale+10, _item.currentTime.timescale) toleranceBefore:CMTimeMake(1, _item.currentTime.timescale) toleranceAfter:CMTimeMake(1, _item.currentTime.timescale)]; weak typeof(self) weakSelf=self; [UIView animateWithDuration:0.3 animations:^{ weakSelf.forward.alpha=1.0; }completion:^(BOOL finished) { [UIView animateWithDuration:0.3 animations:^{ weakSelf.forward.alpha=0.0; }]; }]; }}//快退10秒-(void)stepBackward10Senconds{ if(_isPlaying) { [_item seekToTime:CMTimeMakeWithSeconds(_item.currentTime.value/_item.currentTime.timescale-10, _item.currentTime.timescale)]; weak typeof(self) weakSelf=self; [UIView animateWithDuration:0.3 animations:^{ weakSelf.backward.alpha=1.0; }completion:^(BOOL finished) { [UIView animateWithDuration:0.3 animations:^{ weakSelf.backward.alpha=0.0; }]; }]; }}//界面-(void)createUI{ _playBtn=[[UIButton alloc]initWithFrame:CGRectMake(50, 20, 60, 40)]; [_playBtn setTitle:@"Play" forState:UIControlStateNormal]; [_playBtn setTitle:@"Pause" forState:UIControlStateSelected]; [_playBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; _playBtn.titleLabel.font=[UIFont boldSystemFontOfSize:16]; [_playBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; [_playBtn addTarget:self action:@selector(playAction:) forControlEvents:UIControlEventTouchUpInside]; _playBtn.backgroundColor=[UIColor clearColor]; [self.view addSubview:_playBtn]; _progress=[[UIProgressView alloc]initWithFrame:CGRectMake(110, 41, kHeight-230, 2)]; // 设置轨道颜色,将要走到的路径的颜色 _progress.trackTintColor=[UIColor whiteColor]; // 设置进度颜色,已经走过的路径 _progress.progressTintColor=[UIColor blueColor]; _slider=[[UISlider alloc]initWithFrame:CGRectMake(110, 26,kHeight-230, 31)]; [_slider setThumbImage:[UIImage imageNamed:@"thumb"] forState:UIControlStateNormal]; _slider.minimumTrackTintColor=[UIColor blueColor];//设置左边颜色 _slider.maximumTrackTintColor=[UIColor clearColor];//设置右边颜色 _slider.continuous=NO; [_slider addTarget:self action:@selector(sliderDidSlided:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:_progress]; [self.view addSubview:_slider]; _timeLabel=[Tools createLabelWithFrame:CGRectMake(kHeight-130, 30, 110, 20) text:nil textColor:[UIColor whiteColor] textAligment:NSTextAlignmentRight andBgColor:[UIColor clearColor] font:[UIFont systemFontOfSize:14]]; _timeLabel.text=@"--:--/--:--"; [self.view addSubview:_timeLabel]; // 音量label _volumeLabel=[[UILabel alloc]initWithFrame:CGRectMake(kHeight-100, kWidth/2-20, 80, 40)]; _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceil(_player.volume)*10]; _volumeLabel.alpha=0.0; _volumeLabel.backgroundColor=[UIColor clearColor]; _volumeLabel.font=[UIFont boldSystemFontOfSize:22]; _volumeLabel.textColor=[UIColor whiteColor]; [self.view addSubview:_volumeLabel]; //快进 _forward=[[UIImageView alloc]initWithFrame:CGRectMake(kHeight-164, kWidth/2-32, 64, 64)]; _forward.image=[UIImage imageNamed:@"stepforward"]; [self.view addSubview:_forward]; _forward.alpha=0.0; //快退 _backward=[[UIImageView alloc]initWithFrame:CGRectMake(100, kWidth/2-32, 64, 64)]; _backward.image=[UIImage imageNamed:@"stepbackward"]; [self.view addSubview:_backward]; _backward.alpha=0.0;}//标题视图-(void)createTitleView{ UILabel *titleLabel=[Tools createLabelWithFrame:CGRectMake(50, 10, kHeight-100, 20) text:self.videoTitle textColor:[UIColor whiteColor] textAligment:NSTextAlignmentCenter andBgColor:[UIColor clearColor] font:[UIFont boldSystemFontOfSize:12]]; [self.view addSubview:titleLabel]; titleLabel.tag=111; // 返回按钮 UIButton *backBtn=[[UIButton alloc]initWithFrame:CGRectMake(10, 20, 40, 40)]; [backBtn setBackgroundImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; [backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:backBtn];}//返回-(void)backAction{ [self.navigationController popViewControllerAnimated:YES]; [self dismissViewControllerAnimated:YES completion:nil];}/** * 滑动进度条时执行该方法 * * @param sender slider对象 */-(void)sliderDidSlided:(UISlider *)sender{ if(_player.status==AVPlayerStatusReadyToPlay) { CGFloat value=sender.value; [_item seekToTime:CMTimeMake(value, 1)]; }}//隐藏上方的条条-(void)hideStatusLabels{ weak PlayViewController *weakSelf=self; if(!_isPlaying) return; UILabel *label=(UILabel *)[self.view viewWithTag:111]; [UIView animateWithDuration:0.5 animations:^{// 自动播放视频 [weakSelf.player play]; weakSelf.isPlaying=YES; label.alpha=0; weakSelf.timeLabel.alpha=0; weakSelf.slider.alpha=0; weakSelf.progress.alpha=0; weakSelf.playBtn.alpha=0; } completion:^(BOOL finished) { }];}//显示上方的条条-(void)showStatusLabels{ weak PlayViewController *weakSelf=self; UILabel *label=(UILabel *)[self.view viewWithTag:111]; [UIView animateWithDuration:0.5 animations:^{ weakSelf.timeLabel.alpha=1; weakSelf.slider.alpha=1; weakSelf.progress.alpha=1; weakSelf.playBtn.alpha=1; label.alpha=1; } completion:^(BOOL finished) { }];}//手势点击屏幕触发的方法-(void)shiftStatus{ if(_playBtn.alpha==0) {// 显示上方条条 [self performSelector:@selector(showStatusLabels) withObject:nil];// 6秒后 自动隐藏上方条条 [self performSelector:@selector(hideStatusLabels) withObject:nil afterDelay:6]; } else// 隐藏条条 [self performSelector:@selector(hideStatusLabels) withObject:nil];}/** * 点击播放按钮时执行的方法 * * @param sender 播放按钮 */-(void)playAction:(UIButton *)sender{ sender.selected=!sender.isSelected; if(_isPlaying) { [_player pause]; } else { [_player play]; } _isPlaying=!_isPlaying;}/** * 收到播放完毕的通知时执行该方法 * * @param notification 通知 */- (void)moviePlayDidEnd:(NSNotification *)notification{ NSLog(@"Play end"); [_player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) { [self updateVideoSlider:0.0]; [_playBtn setTitle:@"Play" forState:UIControlStateNormal]; }];}- (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) { NSLog(@"AVPlayerStatusReadyToPlay");// 开始播放视频 [_player play];// 播放状态置为YES _isPlaying=YES; _playBtn.selected=YES;// 隐藏上方的条条 [self hideStatusLabels];// 隐藏加载指示器 [_indicator stopAnimating]; [_indicator removeFromSuperview]; CMTime duration = _item.duration;// 获取视频总长度 CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒 _totalTime = [self convertTime:totalSecond];// 转换成播放时间 [self customVideoSlider:duration]; NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration)); [self monitoringPlayback:_item];// 监听播放状态 } else if ([playerItem status] == AVPlayerStatusFailed) { //移除网络加载指示器 [_indicator stopAnimating]; [_indicator removeFromSuperview]; //NSLog(@"AVPlayerStatusFailed"); UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示" message:@"加载失败" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil]; [alert show]; [alert dismissWithClickedButtonIndex:0 animated:YES]; } } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度// NSLog(@"Time Interval:%f",timeInterval); CMTime duration = _item.duration; CGFloat totalDuration = CMTimeGetSeconds(duration);// 设置进度处理器的进度值 [_progress setProgress:timeInterval / totalDuration animated:YES]; }}/** * 获取缓冲总进度 * * @return 缓冲总进度 */- (NSTimeInterval)availableDuration{ NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度 return result;}/** * 将视频播放的当前时间和总时间,格式化显示在界面上 * * @param second 播放器的当前时间 * * @return 格式化后的显示时间 */- (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;}- (void)monitoringPlayback:(AVPlayerItem *)playerItem{ __weak PlayViewController *weakSelf=self; self.playbackTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) { CGFloat currentSecond = playerItem.currentTime.value/playerItem.currentTime.timescale;// 计算当前在第几秒 [weakSelf updateVideoSlider:currentSecond]; NSString *timeString = [weakSelf convertTime:currentSecond]; weakSelf.timeLabel.text = [NSString stringWithFormat:@"%@/%@",timeString,weakSelf.totalTime]; }];}- (void)customVideoSlider:(CMTime)duration{ _slider.maximumValue = CMTimeGetSeconds(duration); UIGraphicsBeginImageContextWithOptions((CGSize){ 1, 1 }, NO, 0.0f); UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [_slider setMinimumTrackImage:transparentImage forState:UIControlStateNormal]; [_slider setMaximumTrackImage:transparentImage forState:UIControlStateNormal];}/** * 更新滑块的值 * * @param currentSecond 当前的播放时间 */- (void)updateVideoSlider:(CGFloat)currentSecond{ [_slider setValue:currentSecond animated:YES];}/*** 视图即将消失时移除观察者对象,注销通知,如果播放器在播放,让其暂停,让播放器指向nil * * @param animated 动画效果 */- (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; [_item removeObserver:self forKeyPath:@"status" context:nil]; [_item removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_item]; [_player removeTimeObserver:self.playbackTimeObserver]; if(_isPlaying) { [_player pause]; _player=nil; }}@end
附上GitHub代码地址:AVFoundationPlayer
作者:CGPointZero
链接:http://www.jianshu.com/p/dad62fecd5df
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
阅读全文
0 0
- 用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。
- SuperPlayer 是一个基于 IjkPlayer 的控制器,支持手势操作,滑动快进,快退,支持,上滑音量亮度的变化,支持指点位置播放,播放源的切换
- Android------视频播放器(包含全屏播放,快退,快进,腾讯新闻的列表播放等)
- AVFoundation编程指南2-用AVPlayer播放视频
- AVFoundation编程指南2-用AVPlayer播放视频
- AVPlayer 实现视频播放器的开发
- 视频播放AVPlayer的使用
- iOS开发 - 用AVPlayer封装一个视频播放器
- 小程序scroll-view上下滑的内部加左右滑,scroll-x生效的情况
- iOS中AVPlayer的在线播放和播放进度条,音量条以及AVPlayer的后台播放
- iOS中AVPlayer的在线播放和播放进度条,音量条以及AVPlayer的后台播放
- 视频播放器AVPlayer
- 视频播放器 AVPlayer
- 用AVPlayer播放视频
- swift AVFoundation AVPlayer 视频播放器——swift学习(十)
- AVPlayer视频播放的使用简介
- iOS视频播放AVPlayer的视频内容拉伸设置
- [绍棠] iOS视频播放AVPlayer的视频内容拉伸设置
- BZOJ 2438: [中山市选2011]杀人游戏
- win10添加任务计划执行Python脚本
- pthread一个最简单的挂起,恢复的例子
- 测试之Bug
- 学习笔记:Android彻底组件化(二)-Demo发布——Android技术杂货铺
- 用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。
- c++基础(6)
- Python模块卸载
- JDBC对分类表的增删改查
- jpeg图片格式详解
- LeetCode-Reverse Integer
- Spark编译与部署(中)--Hadoop编译安装
- Android系统介绍与框架
- 汽车如果有异响就说明存在问题,具体参考以下网址。