代码地址如下:
http://www.demodashi.com/demo/11685.html

AVPlayer 是一个强大的视频播放器,可以播放多种格式的视频,缺点是没有控制界面,需要自己去实现。

一、效果图

二、实现过程

先看下它的结构


首先初始化播放器,设置播放URL。

self.avPlayerView = [[XYAVPlayerView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, VIDEO_HEIGHT)];
self.avPlayerView.videoUrl = m3u;
[self.view addSubview:self.avPlayerView];

初始化方法,添加一个视频控制器。


- (instancetype)initWithFrame:(CGRect)frame
{if ([super initWithFrame:frame]) {self.backgroundColor = [UIColor blackColor];[self addSubview:self.playControlView];}return self;
}

设置视频URL


- (void)setVideoUrl:(NSString *)videoUrl
{_videoUrl = videoUrl.copy;[self createAVPlayer];}

初始化AVPlayer,给_playControlView 引用AVPlayer,方便进行控制, [_playControlView addObserver];[_playControlView playerTimerAction];会在后面说明。


- (void)createAVPlayer
{NSURL *url = [NSURL URLWithString:self.videoUrl];if (!url) {return;}/***  AVPlayer*/_avPlayerItem = [AVPlayerItem playerItemWithURL:url];_avPlayer = [AVPlayer playerWithPlayerItem:_avPlayerItem];_avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:_avPlayer];_avPlayerLayer.frame = self.bounds;_avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspect;//[self.layer addSublayer:_avPlayerLayer];[_avPlayer play];self.autoresizesSubviews = YES; //子视图Size自适应_playControlView.avPlayer = self.avPlayer;_playControlView.avPlayerItem = self.avPlayerItem;_playControlView.avPlayerLayer = self.avPlayerLayer;[_playControlView addObserver];[_playControlView playerTimerAction];[self bringSubviewToFront:self.playControlView];
}

到这里只是创建了一个View,上面加载了一个AVPlayer,一个视频控制器视图。

视频控制器代码:
我用的xib创建的View,所以初始化方法是awakeFromNib


- (void)awakeFromNib
{[super awakeFromNib];[self configureVolume];UITapGestureRecognizer * screenTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(screenTap)];[self addGestureRecognizer:screenTap];UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRecognizerAction:)];pan.delegate = self;[_adjustView addGestureRecognizer:pan];[self.slider setThumbImage:[UIImage imageNamed:@"verify_code_button"] forState:UIControlStateNormal];// slider开始滑动事件[_slider addTarget:self action:@selector(progressSliderTouchBegan:) forControlEvents:UIControlEventTouchDown];// slider滑动中事件[_slider addTarget:self action:@selector(progressSliderValueChanged:) forControlEvents:UIControlEventValueChanged];// slider结束滑动事件[_slider addTarget:self action:@selector(progressSliderTouchEnded:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchUpOutside];// slider 添加点击手势UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(sliderTap:)];[_slider addGestureRecognizer:tap];[self hiddenViews];}

/***  获取系统音量*/
- (void)configureVolume {MPVolumeView *volumeView = [[MPVolumeView alloc] init];_volumeSlider = nil;for (UIView *view in [volumeView subviews]){if ([view.class.description isEqualToString:@"MPVolumeSlider"]){_volumeSlider = (UISlider *)view;break;}}// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音NSError *setCategoryError = nil;BOOL success = [[AVAudioSession sharedInstance]setCategory: AVAudioSessionCategoryPlaybackerror: &setCategoryError];if (!success) { /* handle the error in setCategoryError */ }}

第一个手势screenTap,是控制下方控制条的显示与隐藏的

- (void)screenTap {self.controlStripView.hidden = !self.controlStripView.hidden;
}

第二个手势则是用来控制快进、快退、音量、亮度调节的

- (void)panGestureRecognizerAction: (UIPanGestureRecognizer *)sender {//根据在view上Pan的位置,确定是调音量还是亮度CGPoint locationPoint = [sender locationInView:self];CGPoint veloctyPoint = [sender velocityInView:self];switch (sender.state) {case UIGestureRecognizerStateBegan:{// 使用绝对值来判断移动的方向CGFloat x = fabs(veloctyPoint.x);CGFloat y = fabs(veloctyPoint.y);if (x > y) { //水平移动[self showViews];[self playerPause];self.pandirection = PanDirectionHorizontalMoved;}else { //垂直移动self.pandirection = PanDirectionVerticalMoved;}break;}case UIGestureRecognizerStateChanged:{switch (_pandirection) {case PanDirectionHorizontalMoved:{[self horizontalMoved:veloctyPoint.x];break;}case PanDirectionVerticalMoved:{if (locationPoint.x > self.bounds.size.width / 2) {//音量调节-右侧[self verticalMovedForVolume:veloctyPoint.y];}else {//亮度调节-左侧[self verticalMovedForBrightness:veloctyPoint.y];}break;}}break;}case UIGestureRecognizerStateEnded:{switch (_pandirection) {case PanDirectionHorizontalMoved:{[self hiddenViews];[self playerPlay];break;}case PanDirectionVerticalMoved:{break;}}break;}default:break;}
}

对于slider的方法,在开始手势的时候暂停视频播放,显示时间label


- (void)progressSliderTouchBegan: (UISlider *)sender {self.changeTimeLabel.hidden = NO;[self playerPause];NSLog(@" --- began touch");
}

在开始手势的时候seek到slider对应时间的时间点,然后开始视频播放,隐藏时间label


- (void)progressSliderTouchEnded: (UISlider *)sender {CMTime durationTime = self.avPlayerItem.duration;NSTimeInterval currentTime = CMTimeGetSeconds(durationTime) * sender.value;[self seekWithTime:currentTime];self.changeTimeLabel.hidden = YES;[self playerPlay];NSLog(@" ---- end touch");
}

slider滑动的时候,只是改变label上显示的时间


- (void)progressSliderValueChanged: (UISlider *)sender {CGFloat currentTime = sender.value * CMTimeGetSeconds(self.avPlayerItem.duration);NSString *tempCurrentTime = [self timeFormatterForServiceWithTimeStamp:currentTime];//当前播放时间self.currentTimeLabel.text = tempCurrentTime;//屏幕中间时间self.changeTimeLabel.text = tempCurrentTime;CGFloat sliderProgress = sender.value / sender.maximumValue;if (self.progressView.progress < sliderProgress) {self.progressView.progress = sender.value / sender.maximumValue;}NSLog(@" --- event touch  %f",sender.value);}

slider的单击方法则是直接seek到对应时间点,AVPlayer会自动处理的。


/***  Slider Tap*/
- (void)sliderTap: (UITapGestureRecognizer *)sender {if ([sender.view isKindOfClass:[UISlider class]]) {UISlider *slider = (UISlider *)sender.view;CGPoint point = [sender locationInView:slider];CGFloat length = slider.frame.size.width;CGFloat tempValue = point.x / length;NSTimeInterval currentTime = CMTimeGetSeconds(self.avPlayerItem.duration) * tempValue;CGFloat progress = currentTime/CMTimeGetSeconds(self.avPlayerItem.duration);if (progress > slider.value) {self.progressView.progress = progress;}[self seekWithTime:currentTime];}}

前面用的 addObserver方法,则是给播放器添加观察者,用来检测播放器状态,还有APP的状态。


- (void)addObserver
{//监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态[self.avPlayerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];//监控网络加载情况属性[self.avPlayerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];/***  进入后台  暂停播放**/[kNotificationCenter addObserver:self selector:@selector(applicationDidEnterBackground_Notification) name:ApplicationDidEnterBackground_Notification object:nil];/***  进入活跃状态  继续播放**/[kNotificationCenter addObserver:self selector:@selector(applicationDidBecomeActive_Notification) name:ApplicationDidBecomeActive_Notification object:nil];[kNotificationCenter addObserver:self selector:@selector(playerReset) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];}/** * 通过KVO监控播放器状态 ** @param keyPath 监控属性* @param object 监视器* @param change 状态改变* @param context 上下文 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{AVPlayerItem *playerItem=object;if ([keyPath isEqualToString:@"status"]) {AVPlayerStatus status= [[change objectForKey:@"new"] intValue];if(status==AVPlayerStatusReadyToPlay){//总时长self.allTimeLabel.text = [self timeFormatterForServiceWithTimeStamp:CMTimeGetSeconds(playerItem.duration)];self.currentTimeLabel.text = @"00:00:00";NSLog(@"正在播放...,视频总长度:%.2f",CMTimeGetSeconds(playerItem.duration));}}else if([keyPath isEqualToString:@"loadedTimeRanges"]){NSArray *array=playerItem.loadedTimeRanges;CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围float startSeconds = CMTimeGetSeconds(timeRange.start);float durationSeconds = CMTimeGetSeconds(timeRange.duration);NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度NSLog(@"共缓冲:%.2f",totalBuffer);double ableScale = totalBuffer / CMTimeGetSeconds(playerItem.duration);if (ableScale <= 1) {self.progressView.progress = ableScale;}}
}

playerTimerAction方法则是制定每秒进行一次返回,返回当前播放进度。


- (void)playerTimerAction
{__weak XYAVControlView * weakSelf = self;[self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {float current=CMTimeGetSeconds(time);if (current) {//当前播放进度weakSelf.currentTimeLabel.text = [weakSelf timeFormatterForServiceWithTimeStamp:CMTimeGetSeconds(weakSelf.avPlayerItem.currentTime)];//滑块进度double totalTempTime = CMTimeGetSeconds(weakSelf.avPlayerItem.duration);double scale = CMTimeGetSeconds(weakSelf.avPlayerItem.currentTime) / totalTempTime;weakSelf.slider.value = scale;}}];
}

最后在 ViewController 里面监控屏幕方向的变化,来处理全屏效果


[kNotificationCenter  addObserver:selfselector:@selector(onDeviceOrientationChange)name:UIDeviceOrientationDidChangeNotificationobject:nil];/***  屏幕方向发生变化会调用这里*/
- (void)onDeviceOrientationChange
{UIDeviceOrientation orientation             = [UIDevice currentDevice].orientation;UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;switch (interfaceOrientation) {case UIInterfaceOrientationPortraitUpsideDown:case UIInterfaceOrientationPortrait:{self.view.frame = (CGRect){0,0,ScreenWidth,ScreenHeight};self.avPlayerView.frame = CGRectMake(0, 0,ScreenWidth,VIDEO_HEIGHT);self.avPlayerView.playControlView.isFullScreen = NO;break;}case UIInterfaceOrientationLandscapeLeft:case UIInterfaceOrientationLandscapeRight:{self.view.frame = (CGRect){0,0,ScreenWidth,ScreenHeight};self.avPlayerView.frame = self.view.bounds;self.avPlayerView.playControlView.isFullScreen = YES;break;}default:break;}
}

到这里一个简单的视频播放器就做完了。

三、项目代码截图

第一层目录结构

第二层目录结构


使用AVPlayer制作一个播放器

代码地址如下:
http://www.demodashi.com/demo/11685.html

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

使用AVPlayer制作一个播放器相关推荐

  1. 制作一个播放器(二)

    制作一个播放器(一) 制作一个播放器(二) 接着上一章的热身,咱们继续写播放器.上一篇中咱们用的是句柄的方式来播放视频.实际开发中,我们更多的是把数据给回调出来,这样更好的去显示视频,处理视频.这期, ...

  2. 制作一个播放器(一)

    制作一个播放器(一) 制作一个播放器(二) 开发前准备 开发平台:windows 开发语言:C++(Qt) 开发工具:Visual Studio 2019 cmake  Qt5.15.2 做个小广告 ...

  3. iOS开发 - 用AVPlayer封装一个播放器

    因为GIF上传的时候一直失败,所以大家到Github查看效果图 https://github.com/codeliu6572/LHHVideoPlayer 由于亮度和音量只能在真机上显现效果,所以GI ...

  4. 树莓派3B qt+mplayer制作音乐播放器(10)

    内容 树莓派3B qt+mplayer制作音乐播放器:播放.暂停.上一曲.下一曲,音量调节. 平台:树莓派+qt+mplayer 1.配置 qt安装见此: https://blog.csdn.net/ ...

  5. winform制作音乐播放器

    winform制作音乐播放器 本文利用C# 调用Windows自带的Windows Media Player 打造一款属于自己的音乐播放器,以供学习分享使用,如有不足之处,还请指正. 概述 Windo ...

  6. flash制作swf播放器

    AS3的 LoaderInfo 类为我们加载外部资源提供了更多的可控信息,以前制作SWF播放器的两大难题终于可以得到解决: ◦获得加载SWF的舞台大小以缩放到适合尺寸显示 LoaderInfo 的 w ...

  7. 利用python制作简单播放器

    在这里可以制作小播放器的界面,objectName 是每一个图片,图标每一个部位的名字,它们每个地方都有一个属于自己的小名字. windowtitle 可以为自己的小播放器取一个好听又可爱的名字. w ...

  8. 用Python制作简易播放器(电子钢琴) mac系统

    用Python制作简易播放器(电子钢琴) 开发环境:Python3.7 Mac OS 思路: 先根据需要设计GUI的样式,并思考需要定义什么功能 把功能写出来 把功能填入GUI之中 用曲子测试完整的程 ...

  9. 基于Arduino Uno开发板制作音乐播放器

    基于Arduino Uno开发板制作音乐播放器 本文将基于Arduino开发板实现一个音乐播放器. 利用Arduino Uno读取sd卡模块中内存卡的音乐,传输信号到扬声器进行播放. 一.项目软硬件简 ...

最新文章

  1. struts2学习 - action -3 动态方法调用 DMI
  2. 深度学习手势识别带你玩转神庙逃亡
  3. 计算机系统结构研究分支,“计算机系统结构” 课程教学探讨[J] 电子科技大学.doc...
  4. 【C++ Primer学习笔记】第1章:快速入门
  5. opencv学习笔记20:图像轮廓
  6. (转)SQL Case when 的使用方法
  7. ieee754浮点数转换工具_关于JS浮点数运算不精确的原因和解决方案
  8. 图片热点的使用,html area 的用法
  9. 微信公众号开发基本流程
  10. AT24C32、AT24C64、AT24C128、AT24C256、AT24C512系列EEPROM芯片单片机读写驱动程序
  11. 特征选择之互信息(mutual information)算法思想及其python代码实现
  12. python:textwrap --文本自动换行与填充
  13. 判断和循环——实战收尾篇1(二分法、抛硬币等)
  14. 前端程序员拿到新电脑第一天,该做些什么?
  15. python数据分列_Python pandas 数据无法正常分列
  16. 网络通信——下载管理器DownloadManager——在通知栏显示下载进度
  17. 【Sparse-to-Dense】《Sparse-to-Dense:Depth Prediction from Sparse Depth Samples and a Single Image》
  18. iverilog 提示 Unknown module type 解决办法
  19. Access2013 Mssql2012 数据库连接字符串
  20. 开源Wi-Fi芯片/FPGA设计以及背后的中国开发者(转载)

热门文章

  1. 【C语言】单词个数统计(库函数第一次运用)
  2. KG—ARM-Thumb子程序调用规则—ATPCS
  3. 嵌入式Linux系统编程学习之十九标准管道流
  4. redis cluster集群模式简述
  5. java 8和jdk区别_java-8 – JDK 6和JDK8之间的Java Collection差异
  6. 《深入理解 Spring Cloud 与微服务构建》第一章 微服务简介
  7. 死锁 操作系统第二章知识点归纳总结
  8. Redis面试之传统五大数据类型的落地应用详解
  9. Java面试之线程池详细
  10. P1959 遗址_NOI导刊2009普及(6)