本教程使用 AVKit 和 AVFoundation frameworks 制作一个 Video Streaming app。
AVKit 介绍
AVKit 位于 AVFoundation 顶部,它提供了用于与视频交互的所有必要 UI。
本 demo 的开始界面如下:
我们最后要达到的效果是,当用户点击 cell,将弹出 video player 并播放在线视频。
添加 Local Playback
可播放的视频,分为本地视频,以及存放在 server 端的视频。
接下来,我们一起开始编码实现起来。
首先,打开 VideoFeedViewController.swift,添加引用:
import AVKit
然后,在 tableView(_ tableView:didSelectRowAt:) 方法内,添加如下代码:
//1
let video = videos[indexPath.row]
//2
let videoURL = video.url
let player = AVPlayer(url: videoURL)
- 首先,获得 video model 对象。
- 所有 Video 对象都有 url 属性。我们使用 URL 创建一个 AVPlayer 对象。
AVPlayer 对象可以启动和停止视频,更改视频播放速率,以及调高或调低音量等。 我们可以将 AVPlayer 视为管理媒体资产(media asset)的控制器。
在该方法底部继续添加代码:
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true) {
player.play()
}
AVPlayerViewController 是一个使用 player 对象的简洁的视图控制器。
当 presentation 动画结束后,我们可以通过调用 play() 方法来播放视频。效果如下:
AVPlayerViewController 有一组基本控件,包括一个播放器按钮,一个静音按钮以及一个向前和向后前进 15s 的快进按钮。
添加远程播放功能
打开 AppDelegate.swift,找到设置 feed.videos 那一行,替换为如下代码:
feed.videos = Video.allVideos()
进入 Video.swift 文件,查看 allVideos() 方法,我们可以看到,区别是它的 url 属性为网络链接,而不是文件路径。
在 allVideos() 内,替换如下代码
let videoURLString =
"https://wolverine.raywenderlich.com/content/ios/tutorials/video_streaming/foxVillage.mp4"
为
let videoURLString =
"https://wolverine.raywenderlich.com/content/ios/tutorials/video_streaming/foxVillage.m3u8"
Build and run
这两个的区别是,第二个 URL 是 HLS 实时流媒体(Livestream)类型。
HLS 实时流媒体通过将视频分成 10秒的块来工作,然后将它们一次提供给客户端。对比可知,该视频开始播放的速度比使用 MP4 格式播放快得多。
Adding a Looping Video Preview
看看右下角浮动的黑框播放器,现在我们需要添加手势事件,当单击的时候打开或者关闭声音,双击的时候使播放速度增加或者减少 2 倍。
AVFoundation 简介
我们需要熟悉几个类:
- AVPlayerLayer: 这个特殊的 CALayer 子类可以显示 AVPlayer 对象的播放。
- AVAsset: 媒体资产的静态表示。资产对象包含诸如持续时间和创建日期之类的信息。
- AVPlayerItem: AVAset 的动态副本,代表可播放视频的当前状态。
使用 AVPlayerLayer 写一个自定义 Video View
我们首先需要考虑的是 AVPlayerLayer,此 CALayer 子类与其他任何层一样:在屏幕上显示其 content 属性中的内容。
打开 VideoPlayerView.swift,添加以下属性,记得使用 AVPlayerLayer 而不是普通的 CALayer。
import AVFoundation
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
Build and run,可以看到
编写 Looping Video View
打开 VideoLooperView.swift,添加如下属性:
private let player = AVQueuePlayer()
此类允许我们提供将要播放对象的队列。
继续添加代码:
private func initializePlayer() {
videoPlayerView.player = player
}
这里,我们将播放器传递到 videoPlayerView,以将其连接到 AVPlayerLayer。
现在是时候将视频片段列表添加到播放器,以便它开始播放了。
添加以下方法:
private func addAllVideosToPlayer() {
for video in clips {
//1
let asset = AVURLAsset(url: video.url)
let item = AVPlayerItem(asset: asset)
//2
player.insert(item, after: player.items().last)
}
}
这里,我们在浏览所有剪辑:
- 由每个视频剪辑对象的 URL 创建一个 AVURLAsset。
- 然后,使用播放器可用来控制播放的 asset 创建一个 AVPlayerItem。
- 最后,使用 insert(_after:) 方法将每个项目添加到队列中。
现在,在 initializePlayer() 中调用该方法。
addAllVideosToPlayer()
player.volume = 0.0
player.play()
最后,在 init(clips:) 方法中添加调用:
initializePlayer()
Build and run:
美中不足的是,当最后一个短视频播放完毕,视频播放器会逐渐变黑。
添加循环播放
苹果编写了一个漂亮的新类,AVPlayerLooper。此类包含一个播放器项目,并负责循环播放该项目所需的所有逻辑。不幸的是,这里 AVPlayerLooper 对我们没有帮助。
我们想要的是能够循环播放所有视频,我们必须手动执行操作了。
我们需要做的是跟踪播放器及当前正在播放的 playing item。当播放到最后一个视频时,我们将所有剪辑(clips)再次添加到队列中。
我们可以使用 Key-Value Observing 跟踪 player’s information。
Key-Value Observing 是 Apple 提供的一种出色 API。此外,它还是一种实时观察和响应状态变化的有效方法。 如果你还不熟悉 KVO,这里可以快速了解下。其基本思想是,只要特定属性的值发生改变,就可以注册通知。
这里我们想知道 player 的 currentItem 何时更改。每次收到通知时,我们知道播放器已前进到下一个视频了。
代码走起来:
@objc private let player = AVQueuePlayer()
区别是我们添加了@objc 指令。这告诉 Swift,我们想将属性公开给 Objective-C 环境下的东西,例如 KVO。要在 Swift中 使用 KVO(比在Objective-C中更好),我们需要保留对观察者的引用。 在 player 之后添加以下属性:
private var token: NSKeyValueObservation?
回到 initializePlayer() 方法,添加如下代码:
token = player.observe(\.currentItem) { [weak self] player, _ in
if player.items().count == 1 {
self?.addAllVideosToPlayer()
}
}
这里,我们注册一个 block,以在播放器的 currentItem 属性每次更改时运行。 当前视频更改后,需要检查播放器是否已移至最终视频。如果是最后一个,此时需要将所有视频片段(video clips)重新添加到队列中了。
Build and run,看看循环播放效果:
高效播放视频
需要注意的是,播放视频是一项资源密集型任务。实际上,即使我们开始观看全屏视频,右下角的播放器也将继续播放这些剪辑。
为了解决此问题,我们在 VideoLooperView.swift 的底部添加以下两个方法:
func pause() {
player.pause()
}
func play() {
player.play()
}
这里我们暴露了 play() 和 pause() 两个方法。
打开 VideoFeedViewController.swift 文件,找到 viewWillDisappear(_:) 方法,添加如下代码:
videoPreviewLooper.pause()
在 viewWillAppear(_:) 方法添加执行播放命令:
videoPreviewLooper.play()
Build and run,进入全屏播放时,右下角的 preview 将暂停播放。
使用 Player Controls 播放
接下来我们实现如下两个小功能:
- 单击视频,静音播放
- 双击视频,切换一倍速 1x和二倍速 2x
打开 VideoLooperView.swift,在 play and pause 方法下,添加代码:
@objc func wasTapped() {
player.volume = player.volume == 1.0 ? 0.0 : 1.0
}
@objc func wasDoubleTapped() {
player.rate = player.rate == 1.0 ? 2.0 : 1.0
}
然后添加手势事件:
网友评论