swift4.0实现视频播放、屏幕旋转、倍速播放、手势调节及锁屏面板等功能实例

前言

学习swift有段时间了,原来写过一个基于 swift 3.0 的视频播放,后来有同学联系我说,在音频锁屏的情况下,无法用控制面板拖动进度条调节播放进度,所以又将原来的代码拿过来重新整理了下也顺便更新到了4.0版本。在把原来的代码拿来的时候发现原来有好多地方都是错误的,原来在 OC 项目里面已经写过一遍关于视频播放的东西所以就按照原来的逻辑写了 swift 版本,其实里面很多代码我也是通过查找资料和看文档拼凑出来的,对于 swift 的语句也是一知半解,希望各位看官多多包涵。

先来看一下实现的效果,一图胜千言(第一张是 iOS 10系统,第二张是 iOS 11系统)。

demo下载地址  (本地下载)

工程介绍

简单说一下工程结构,所有关于布局都是在Player文件夹下的MPlayerViewModel文件中,考虑到耦合度的原因,所以将视频播放的所有 UI 布局全部抽离出来,在播放器 view 里将会频繁看到一个叫viewModel的对象,它既 UI 布局也是布局控件的所有者。视频播放的布局是基于SnapKit三方库来布局了,因为在OC里用惯了Masonry所以工程里依然沿用这个库。主要代码是放到MPlayerView这个文件中的,其中还有一个由 OC 写的DeviceTool文件主要用来做页面强制旋转用的,强制旋转这一部分我现在还没有更好的解决办法只能桥接 OC 里的方法。

初始化播放器方法

视频播放界面我用的是一个单例实现的,刚开始不是用单例实现,但是为了把代码拆出来放到各自的功能区所以用单例实现是最好的方法。由于swift放弃了OC里的dispatch_once实现单例方法,swift3.0以后的单例写法:

/// 创建播放器单例
static let shared = MPlayerView()
private override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

在swift3.0之后重写init方法必须实现required init方法,这么做也是为了安全,因为在OC里init方法并不能保证子类完成初始化,增加required“这是由初始化方法的完备性需求所决定的,以保证类型的安全。在创建视频播放视图有两种创建方式:1.用单利创建。2.init 初始化 ,这两种方法都可以达到视频播放的效果。

1.单利初始化

self.playerView = MPlayerView.shared.initWithFrame(frame: self.view.frame, videoUrl: videoUrl, type: "VIDEO")

2.init 初始化

self.playerView = MPlayerView().initWithFrame(frame: CGRect.init(x: 0, y: 0, width: Screen_width, height: Screen_width * 9/16), videoUrl: videoUrl, type: "VIDEO")

手势滑动及注意事项

由于swift里面有严格的类型检查,就比如在做手势滑动的时候,手势刚开始滑动的时候肯定需要记录一下当前播放器的位置我在项目中是定义的sumTime属性是一个CMTime类型,如果在OC里大可不必这样,来看一下swift与OC代码的区别

swift写法

/// 给sumTime初值
let time = self.player?.currentTime()
self.sumTime = CMTimeMake((time?.value)!, (time?.timescale)!)

OC写法

// 给sumTime初值
CMTime time = self.player.currentTime;
self.sumTime = time.value/time.timescale;

滑动的距离是一个Double类型,而self.sumTime是CMTime类型,俩者肯定不能想加算出结束滑动的距离,所以将double类型转换成CMTime类型用以下方法:

CMTime.init(seconds: Double.init(value/200), preferredTimescale: CMTimeScale(NSEC_PER_SEC))

如果是OC的话直接括号强转类型即可实现。

知道滑动的距离和记录滑动前的距离俩者想加即是当前位置,转化成CMTime类型:

self.sumTime = CMTimeAdd(self.sumTime!, addend)

手势是滑动了,但是进度条也是要跟着一起滑动的,有人说我把进度条刷新放到player的代理里面,手势滑动完只需要把时间传给播放器,播放器根据当前时间和总时间去更新进度条,这样做也对,但是有一点就是,如果网速不好,手势已经滑动到5分钟了,而进度条还停留在1分钟的地方,播放器缓存完毕了,进度条会瞬间跳到5分钟,从而造成卡顿的假象体验也不是很好,所以解决这个方法是手势滑动的时候也更新进度条,但是手势滑动的时候都是CMTime类型,怎么转成Float类型,因为slider?.value是float类型。可以这样:通过CMTimeGetSeconds方法得到一个Float64再通过Float.init方法得到一个float类型,看一下实现:

let sliderTime = CMTimeGetSeconds(self.sumTime!)/CMTimeGetSeconds(totalMovieDuration)
self.slider?.value = Float.init(sliderTime)

想查看整个过程可以看播放器手势添加与创建这一块,我已经用MARK:标记起来了。

设置控制面板信息

在视频播放过程中,对视频的监听是必不可少的,监听播放器状态,播放器缓存...等,由于播放器比较简单,功能较少,刚开始我只监听了status属性,后来我加上来loadedTimeRanges缓存状态,缓存这部分的缓存进度计算我已经实现了,但是没有用到只是简单的打印了一下。

在对播放器status属性监听中加入了控制面板信息,是由MPNowPlayingInfoCenter来实现的,通过改变nowPlayingInfo里面对应的信息来更新面板信息,里面有好多属性,比如MPMediaItemPropertyTitle设置音频标题,MPMediaItemPropertyArtist作者、MPNowPlayingInfoPropertyElapsedPlaybackTime当前播放过的时间、MPMediaItemPropertyPlaybackDuration播放总时间等等。刚开始做的时候因为锁屏要更新时间,而nowPlayingInfo又是一个字典类型的再加上需要更新界面布局的时间和进度条,直接将播放器时间强制转换成 string 类型,所以将这一部分放到了时间观察里面,因为时间观察会一直进行所以锁屏界面信息也会一直更新,这样带来一个问题就是锁屏界面的图片如果是网络图片,每1秒就要请求一下图片而且要不断的更新这样带来的结果可想而知。后来才知道,将MPNowPlayingInfoPropertyElapsedPlaybackTime属性设置成self.player!.currentTime()播放器当前时间就会自动更新控制面板信息,调用的地方也很关键,必须放在播放器已经播放的监听里面。

配置远程控制显示的信息

响应远程控制是由MPRemoteCommandCenter来实现的,里面有很多属性,比如:playCommand播放响应事件、pauseCommand 暂停响应事件、nextTrackCommand下一曲响应事件、likeCommand喜欢按钮,类似网易云音乐的那个锁屏,如果设置了likeCommand则dislikeCommand是上一首响应事件、previousTrackCommand上一首,外部拖动进度条是changePlaybackPositionCommand,系统有一个专门的方法来出来远程拖动进度条响应事件:

open func addTarget(handler: @escaping (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus) -> Any

大概控制面板能用到的这些信息差不多也就这么多,如果想了解更多的可以看一下文档或者查阅资料。

屏幕旋转问题

一个视频播放实现起来并不困难,只要处理好player与platitem就行了。最难的就是,如果手机屏幕旋转,怎么能让视频跟着屏幕自适应呢,我在工程里面通过UIDevice变化添加的是屏幕旋转监听:

/**
* 监听设备旋转通知
*/
private func listeningRotating() {
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(onDeviceOrientationChange), name:NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}

如果用户把屏幕旋转关掉,就是控制中心那个开关,用户旋转屏幕,怎么能让画面跟着跑呢,我百度的很多资料,试了也很多方法,但是都不理想,用的还是OC的代码,因为swift里面移除了NSInvocation属性,用的依然是OC的屏幕强制旋转,只能使用桥接文件:

//这个方法是在网上找的
+ (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
// 从2开始是因为0 1 两个参数已经被selector和target占用
[invocation setArgument:&val atIndex:2];
[invocation invoke];
 }
}

因为做的是视频播放,所以进入后台后视频会暂停,这个属于正常现象,如果在视频模式下,进入后台利用控制面板是无法将视频播放的,如果在音频模式下,进入后台利用控制面板是可以让视频播放的。大概就介绍这么多,一言半句也说得不是很明白,如果还有不明白的知识点可以去demo中自己去查,我也是一个初学者里面很多东西都是查资料得来的并不能保证其内容的正确性。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • swift4.0实现视频播放、屏幕旋转、倍速播放、手势调节及锁屏面板等功能实例

    前言 学习swift有段时间了,原来写过一个基于 swift 3.0 的视频播放,后来有同学联系我说,在音频锁屏的情况下,无法用控制面板拖动进度条调节播放进度,所以又将原来的代码拿过来重新整理了下也顺便更新到了4.0版本.在把原来的代码拿来的时候发现原来有好多地方都是错误的,原来在 OC 项目里面已经写过一遍关于视频播放的东西所以就按照原来的逻辑写了 swift 版本,其实里面很多代码我也是通过查找资料和看文档拼凑出来的,对于 swift 的语句也是一知半解,希望各位看官多多包涵. 先来看一下实

  • Android6.0开发中屏幕旋转原理与流程分析

    本文实例讲述了Android6.0开发中屏幕旋转原理与流程.分享给大家供大家参考,具体如下: 从Android 系统开发开始,这里写下Android 6.0 屏幕旋转系统分析. 第一部分 Kenel Android 系统屏幕旋转得以实现,是靠从底层驱动gsensor 中获取数据,从而判断屏幕方向的.kernel sensor的驱动先就不在这里赘述,简单介绍下,gsensor 驱动注册input 事件 在/dev/input/下,可以通过adb getevent -p 可以查看系统所有的输入事件.

  • Qt音视频开发之利用ffmpeg实现倍速播放

    目录 一.前言 二.功能特点 2.1 基础功能 2.2 特色功能 2.3 视频控件 2.4 内核ffmpeg 三.体验地址 四.效果图 五.相关代码 一.前言 用ffmpeg做倍速播放,是好多年都一直没有实现的功能,有个做法是根据倍速参数,不断切换播放位置,实现效果不是很好,ffplay中的倍速就做得很好,而且声音无论倍速多少还非常柔和,有特别的降噪处理啥的,ffplay中的倍速使用的滤镜去实现,并动态调整pts/dts的值,整个处理过程看起来比较复杂,想着有没有稍微简单一点的办法,在经过一个朋

  • Android MediaPlayer 音频倍速播放 调整播放速度问题

    现在市面上的很多音视频App都有倍速播放的功能,例如把播放速度调整为0.5.1.5.2倍等等. 从Android API 23 (Android M)开始,MediaPlayer支持调整播放速度. 使用的方法是setPlaybackParams,传入一个代表播放属性的类PlaybackParams. 本文介绍如何使用MediaPlayer调整播放速度. MediaPlayer.setPlaybackParams 说明 播放速度设置在PlaybackParams对象中,再将此对象传入setPlay

  • JS实现简单控制视频播放倍速的实例代码

    引言 之前就发现一个问题:有时候看一些学习视频,总是嫌它动作太慢,老师黑板上写板书很浪费时间,要是控制合适倍速播放,这样既能提升学习效率,也能让自己看着舒服点.所以我就学着写了下面这个网页,通过Html+CSS+JavaScript实现. 提示:以下是本篇文章正文内容,下面案例可供参考 一.成品效果 二.具体实现 1.HTML+CSS实现简单布局 代码如下(示例): <!DOCTYPE html> <html lang="en"> <head> &l

  • iOS屏幕旋转与锁屏的示例代码

    在做视频开发时遇到屏幕旋转问题,其中涉及到 StatusBar. UINavigationController.UITabBarController .UIViewcontroller . 在设备锁屏下的整体效果图 iOS-旋转.gif 主要涉及以下4点: 横竖屏的旋转 屏幕旋转相应改变视图位置 旋转时状态栏的隐藏与显示 锁屏 1.横竖屏旋转 第1步: -(UIInterfaceOrientationMask)application:(UIApplication *)application su

  • Android屏幕锁屏弹窗的正确姿势DEMO详解

    在上篇文章给大家介绍了Android程序开发仿新版QQ锁屏下弹窗功能.今天通过本文给大家分享android锁屏弹窗的正确姿势. 最近在做一个关于屏幕锁屏悬浮窗的功能,于是在网上搜索了很多安卓屏幕锁屏的相关资料,鉴于网上的资料比较零碎,所以我在这里进行整理总结.本文将从以下两点对屏幕锁屏进行解析: 1. 如何监听系统屏幕锁屏 2. 如何在锁屏界面弹出悬浮窗 如何监听系统屏幕锁屏 经过总结,监听系统的锁屏可以通过以下两种方式: 1) 代码直接判定 2) 接收广播 1) 代码直接判定 代码判断方式,也

  • iOS开发中使用屏幕旋转功能的相关方法

    加速计是整个IOS屏幕旋转的基础,依赖加速计,设备才可以判断出当前的设备方向,IOS系统共定义了以下七种设备方向:   复制代码 代码如下: typedef NS_ENUM(NSInteger, UIDeviceOrientation) { UIDeviceOrientationUnknown, UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom UIDe

  • 总结iOS App开发中控制屏幕旋转的几种方式

    在iOS6之前的版本中,通常使用 shouldAutorotateToInterfaceOrientation 来单独控制某个UIViewController的方向,需要哪个viewController支持旋转,只需要重写shouldAutorotateToInterfaceOrientation方法. 但是iOS 6里屏幕旋转改变了很多,之前的 shouldAutorotateToInterfaceOrientation 被列为 DEPRECATED 方法,查看UIViewController

  • iOS开发中控制屏幕旋转的编写方法小结

    在iOS5.1 和 之前的版本中, 我们通常利用 shouldAutorotateToInterfaceOrientation: 来单独控制某个UIViewController的旋屏方向支持,比如: 复制代码 代码如下: - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  {      return (interfaceOrientation == UIInter

随机推荐