美文网首页iOS开发
Video Streaming 教程

Video Streaming 教程

作者: _浅墨_ | 来源:发表于2021-03-02 18:52 被阅读0次

    本教程使用 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)
    
    1. 首先,获得 video model 对象。
    2. 所有 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 简介

    我们需要熟悉几个类:

    1. AVPlayerLayer: 这个特殊的 CALayer 子类可以显示 AVPlayer 对象的播放。
    2. AVAsset: 媒体资产的静态表示。资产对象包含诸如持续时间和创建日期之类的信息。
    3. 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)
      }
    }
    

    这里,我们在浏览所有剪辑:

    1. 由每个视频剪辑对象的 URL 创建一个 AVURLAsset。
    2. 然后,使用播放器可用来控制播放的 asset 创建一个 AVPlayerItem。
    3. 最后,使用 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 播放

    接下来我们实现如下两个小功能:

    1. 单击视频,静音播放
    2. 双击视频,切换一倍速 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
    }
    

    然后添加手势事件:

    相关文章

      网友评论

        本文标题:Video Streaming 教程

        本文链接:https://www.haomeiwen.com/subject/ylbufltx.html