AVPlayerでHLSを再生する

by

@wapa5pow

AVPlayerでHLSを再生することを試していたのですがオプションがたくさんあるのでそのオプションを設定したときにどのような動作をするかデバッグしてみました。

デバッグの仕方

movie debugger

上記のビューを作ってAVPlayerやAVPlayerItemの各パラメータがどのように変化するのか確かめてみました。WWDC 2018 Measuring and Optimizing HLS Performanceのビデオを見て同じように確認してみたくて作ってみました。それぞれの値は以下のように対応します。

  • canUseNetworkWhilePaused: AVPlayerItem.canUseNetworkResourcesForLiveStreamingWhilePaused
  • currentTime: AVPlayer.currentTime().seconds
  • duration: AVPlayerItem.duration
  • isPlaybackLikelyToKeepUp: AVPlayerItem.isPlaybackLikelyToKeepUp
  • loadedTimeRanges: AVPlayerItem.loadedTimeRanges
  • preferredForwardBufferDuration: AVPlayerItem.preferredForwardBufferDuration
  • preferredPeakBitRate: AVPlayerItem.preferredPeakBitRate
  • presentationSize: AVPlayerItem: presentationSize
  • status: AVPlayerItem.Status

ソースコードは抜粋して単純化してますが、以下のようにplayerViewというViewに動画を表示するようにしています。

let avPlayer = AVPlayer()
let avPlayerItem = AVPlayerItem()
let avPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.frame = playerView.bounds
playerView.layer.addSublayer(avPlayerLayer)

let avAsset = AVAsset(url: URL(string: "https://video-dev.github.io/streams/x36xhzz/x36xhzz.m3u8")!)
playerItem = AVPlayerItem(asset: avAsset)

// 以下にオプションを試すためのコードを設定する
player.play()

HLSはvideo-dev/streamsにあるhttps://video-dev.github.io/streams/x36xhzz/x36xhzz.m3u8を使います。Safariの場合はリンクをブラウザで開くと再生します。

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720"
url_0/193039199_mp4_h264_aac_hd_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=246440,CODECS="mp4a.40.5,avc1.42000d",RESOLUTION=320x184,NAME="240"
url_2/193039199_mp4_h264_aac_ld_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=460560,CODECS="mp4a.40.5,avc1.420016",RESOLUTION=512x288,NAME="380"
url_4/193039199_mp4_h264_aac_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=836280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=848x480,NAME="480"
url_6/193039199_mp4_h264_aac_hq_7.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=6221600,CODECS="mp4a.40.2,avc1.640028",RESOLUTION=1920x1080,NAME="1080"
url_8/193039199_mp4_h264_aac_fhd_7.m3u8

x36xhzz.m3u8をみてみると5種類のBandwidthに合わせてプレイリストが用意されています。試しにurl_0/193039199_mp4_h264_aac_hd_7.m3u8を見てみます。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:11
#EXTINF:10.000,
url_462/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
url_463/193039199_mp4_h264_aac_hd_7.ts
#EXTINF:10.000,
...

10秒ごとにセグメント化されているようです。

それではいろいろ試してみます。

各種オプションを試す

まずそのままplayer.play()だけ設定して再生してみます。

youtube: IpYRsP5Jzgg

player.play()で開始

player.play()としているのでisPlaybackLikelyToKeepUpがtrueになった段階で再生が始まります。loadedTimeRangesをみると10秒くらいバッファしてから始まっているようです。presentationSizeを見ると最初は320x184のサイズで始まります。しばらく再生すると一定時間バッファしながら再生しています。だいたい120-140秒くらいでしょうか。presentationSizeも帯域に応じて大きく大きくなっています。

player.pause()で開始

player.pause()にして試してみるとpresentationSizeが320x184でどんどんバッファしていき最終的にloadedTimeRangesが(0,642)というふうになりました。それから再生してみると帯域におうじて解像度が代わり再度またバッファしだしました。

バッファ時間を短く設定する

無駄にバッファさせたくないという場合は以下のように設定します。するとloadedTimeRangesが(0,10)あたりで止まります。

playerItem.preferredForwardBufferDuration = 10
player.pause()

その状態で10秒あとにseekさせると以下のようになります。

youtube: HnCCuez2h1E

+10ボタンを押して10秒先にシークさせてみます。するとシークさせたあとにもバッファしてisPlaybackLikelyToKeepUpがtrueになってから再生させていることがわかります。

なおpreferredForwardBufferDurationを1など小さい値を設定しても必ずしもこの値以上バッファするわけではなく10秒くらいバッファしていたので強制しているわけではないようです。

プレイヤー側でビットレートや解像度を調整する

AVPlayer側で最大のビットレートを設定したいときはAVPlayerItem.preferredPeakBitRateを設定します。

解像度を設定したいときはAVPlayer.preferredMaximumResolutionを設定します。

まとめ

AVPlayerやAVPlayerItemは多くのパラメータがあるのですが、数値をビューに表示しながらデバッグすることにより各パラメータの設定がどのような影響を及ぼすかわかりやすくなりました。なにか困ったらまたデバッグしながら確認したいと思います。