HLS再生のパフォーマンスを計測する
概要
下記ビデオの内容をまとめます。
概要
Apple が 2009 年に Internet-Draft を公開したマルチメディアのストリーミング配信用プロトコル。クライアントが現在のネットワーク環境に応じて適切な種類のメディアデータを逐次要求することが可能な pull 型の配信方式であり、HTTP Adaptive Streaming の一種と言える。 HLS では、マルチメディアデータの情報は Playlist というテキストに列挙される。これには、ユーザの環境に合わせた Adaptive な再生を可能にするために、同一のコンテンツに対して複数の別バージョンのデータが記載されている。特に、再生対象の音声や映像について、異なるフォーマットや解像度、ビットレートでエンコードされた別バージョンは Variant Stream と呼ばれる。Master Playlist に Variant Stream が列挙され、特定の Variant Stream に対応する Media Playlist が存在する。Media Playlist には実際に再生可能なデータ片である Media Segment が、指定時間毎に区切られて列挙されている。 実際の再生時の大まかな手順は、以下のようになる。
1. Master Playlist を DL する
2. その時のユーザ環境に適切な Variant Stream を決定する
3. 対象の Variant Stream に対応する Media Playlist を DL する
4. Media Playlist に記載されたMedia Segment 群を DL し、再生する
https://gyazo.com/1af7df16fdae73f1b044026fef4510f4
AVPlayer が HLS 再生を進める手順の概要を示す。AVPlayer がコンテンツの URL を与えられ Master Playlist を DL すると、HLS 再生であることが認識され手順が開始される。下図は、 WWDC 2018 の発表内容に注釈を加えて書き直したもの。ここでは、選択可能な bit rate として 2Mbps と 1Mbps が存在する場合を示している。 まずは、高画質な 2Mbps の Media Playlist が DL され、さらに Media Segment 群が逐次 DL されていく。再生可能な状態になるまでバッファが十分たまった段階で、動画の描画が開始する。この、動画の読み込み開始〜映像の描画開始 までにかかる時間は 再生開始時間 と見なすことができる。
その後、Media Segment が逐次 DL されていく。再生が開始し順調に読み込みが進めば、AVPlayerItem の時間と経過時間が合致する状態が続く。が、動画の再生に対して Media Segment の DL が追いつかなくなる場合がある。その場合、まず AVPlayer は再生対象の bit rate を下げた Media Playlist を DL し直し、改めて Media Segment を DL し直していく。それでも DL が追いつかなくなってしまった場合には、Stall が発生し、動画の再生が停止してしまうことになる (Stall 時間 が発生する)。その後、AVPlayerItem のバッファが充分にたまった場合には、改めて再生が開始できるようになる。 https://gyazo.com/9a831d19bcdd855786002d9448db16da
再生開始時間 の長さ
Stall 時間 の発生頻度
Stall 時間 の長さ
動画再生全体の品質
Stall を避けたければ、低品質にスイッチさせるしかない
とはいえ、高品質な映像を提供し続けたい
Stall のリスクとメディアの質のトレードオフになる
そのため、メディアの質を図るための指標が必要になる
再生エラーにより再生が中断されてしまう頻度
どのように KPI を計測するか?
再生開始時間を計測する
問題のある方法
AVPlayer.status が .readyToPlay になっていても、再生が開始しているとは限らない
誤差のある方法
が、数ミリ sec の誤差が生じてしまう
が、こちらも数ミリ sec の誤差が生じてしまう
誤差の少ない方法
timeControlStatus
再生開始前は .waitingToPlayAtSpecifiedRate、再生開始されたタイミングで .playing に変更されるので、KVO で監視すると良い。また、.waitingToPlayAtSpecifiedRate の状態の場合は、停止中の理由を reasonForWaitingToPlay で確認することもできる。 timebase
code:swift
// TODO: サンプル実装する
NotificationCenter.default.addObserfver(self,
selector: /* ... */
name: kCMTimebaseNotification_EffectiveRateChanged,
object: item.timebase)
func didUpdateTimebase() {
if CMTimebaseGetRate(item.timebase) > 0 {
print("Played")
} else {
print("Paused")
}
}
Matt Neuburg. (2017-12-07). Programming iOS 11 Dive Deep into Views, View Controllers, and Frameworks. "O'Reilly Media, Inc.", 1172p. p.829
Stallの発生頻度を計測する
code:swift
Stall が発生したら、その Stall がどの程度継続したか?も知りたいはず。これを知りたい場合、Stall の発生直後から AVPlayerItem の timebase を監視して、Rate が 1 に戻るまでの間隔を計測すれば良い。そうすれば、Stall の継続時間 が算出できる。 code:swift
code:swift
var totalDuration = 0.0
if let accessLog = playerItem.accessLog() {
for event in accessLog.events {
if event.durationWatched > 0 {
totalDuration += event.durationWatched
}
}
}
総視聴の品質 を計測したい場合もある。各ビットレート (Variant Stream) 毎に、下記のように品質を割り出す。全ビットレートの品質の和を、視聴全体の品質とみなすことができる。accessLog を利用すれば、各ビットレートの視聴時間とビットレート自体がわかるので、割り出せる。 code:text
(<視聴時間> / <総視聴時間>) * N Mbps
code:swift
var timeWeightedIBR = 0.0
if let accessLog = playerItem.accessLog(), totalDurationWatched > 0 {
for event in accessLog.events {
if event. durationWatched > 0 && event.indicatedBitrate > 0 {
let eventTimeWeight = event.durationWatched / totalDurationWatched
timeWeightedIBR += event.indicatedBitrate * eventTimeWeight
}
}
}
再生の失敗 を計測する。再生が失敗したかどうか?は、AVPlayerItem の status を確認すれば良い。これが false に変わると、回復不可能なエラーとなったことがわかる。さらに、error プロパティを見ると、エラーの詳細を確認できる。 If a problem was encountered while attempting to load the player item’s media, the status will be AVPlayerItem.Status.failed. You can get the NSError providing the details of the failure by querying the player item’s error property.
~ 09:01
しかし、発生した Stall 毎にその発生時刻および継続時間を割り出して、総視聴時間との比率から分析を行いたいかもしれない。
例えば、以下のような比率を知りたいはず。
table:KPI
KPI 単位
Stall 発生率 Stall 数 / 総視聴時間 (h)
Stall 継続率 総 Stall 時間 (h) / 総視聴時間 (h)
Stall の発生時間を計測する
Stall が発生してから、AVPlayerItem の timebase が 1 に戻るまでの間隔を測る\
AVPlayerItemPlaybackStalled ~ kCMTimebaseNotification_EffectiveRateChanged
総 stall 時間 / 総視聴時間
=== TODO ===
視聴時間
Variant の更新時
再生のシーク時
code:swift