Mirrativ Tech Blog

株式会社ミラティブの開発者(バックエンド,iOS,Android,Unity,機械学習,インフラ, etc.)によるブログです

iOS16.1 以上でピクチャ・イン・ピクチャが CPU を異常に消費する問題の応急処置

こんにちは、エンジニアのちぎら @_naru_jpn です。ミラティブの iOS アプリでは 配信コメントバー 機能の実現のためにピクチャ・イン・ピクチャを活用しています。ピクチャ・イン・ピクチャは iOS16.1 以上の端末において、特定のシチュエーション *1 で異常に CPU リソースを消費することが知られています。コードの調整によってこの現象が抑制できることが分かり、ミラティブの iOS アプリではバージョン 10.0.1 で応急処置を行ったのですが、今回はその詳細とトレードオフについて解説します。

加えて、この問題は既に Apple へバグレポートとして報告済みですが、Apple への事象の説明のために問題を簡単に再現できるプロジェクトを作成したので、そちらも併せてご紹介しています。

ピクチャ・イン・ピクチャが CPU を異常に消費する問題

iOS 16.1 以上の端末でピクチャ・イン・ピクチャを利用した際に、端末が熱くなる、バッテリーの減りが異常に早くなるといった問題があり、海外のサイトでも複数の類似した報告が上がっています。

端末が熱くなる、バッテリーの減りが異常に早くなるといった問題は、ピクチャ・イン・ピクチャ使用時に特定の状況で CPU 使用率が異常に高くなることに起因しています。実際、応急対応をする前のミラティブアプリ上でピクチャ・イン・ピクチャを活用した配信コメントバー機能を有効にした際の CPU 使用率を Instruments でプロファイルしてみると、許容できないほど大きい CPU 使用率が継続していることが分かります。

ピクチャ・イン・ピクチャ使用時のCPU使用率(iOS16/iPhone14Pro)

ピクチャ・イン・ピクチャ起動直後のピークは無視するとして(これも大きいですが)、100%を超えた CPU 使用率が継続している様子が分かります。比較として、iOS15 で配信コメントバー機能を有効にした際の CPU 使用率を調べてみると、差は歴然です。

ピクチャ・イン・ピクチャ使用時のCPU使用率(iOS15.6/iPhone11Pro)

問題が発生するより以前の iOS バージョンでは、ピクチャ・イン・ピクチャ(配信コメントバー機能)の使用前後で目にみえる CPU 使用率の変化はありません。配信中に使用する機能であることを踏まえ、できるだけ CPU に優しい設計にしているからです。*2

iOS16.1 以上の端末では意図せずユーザーの CPU に負荷をかけてしまう状況が発生し、アプリの安定性からも、単に無駄にユーザーのリソースを消費してしまっているという観点からも、大問題でした。

CPU 使用率の上昇に関係するコードと応急処置

関係するコード

色々と調べているうちに、ある実装が CPU 使用率の上昇が発生するかしないかに関係していることが分かりました。具体的には AVPictureInPictureSampleBufferPlaybackDelegate に定義されている、以下のデリゲートメソッドでfalse を返すと、CPU の使用率が異常に上がります。

func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
  // ピクチャ・イン・ピクチャ上のコンテンツが '停止' 状態であるかどうかを返す
  // iOS16.1以降、ここで false を返すと CPU 使用率が異常に上がる
  return isPaused
}

上のメソッドで返す値は、ピクチャ・イン・ピクチャ上で再生マークを表示するか停止マークを表示するか、の判断に使用されています。 '停止' 状態である(= true を返す)とすると、ピクチャ・イン・ピクチャ上では三角の再生マークが表示され、ユーザーに '停止' 状態から '再生' 状態への操作を促します。

ピクチャ・イン・ピクチャ上に表示される停止(再生)マーク

そしてややこしいのですが、ピクチャ・イン・ピクチャ上で表示されているマークが再生状態か停止状態かということと、実際に動画が再生状態か停止状態かということは関係がありません。上記のメソッドで '停止' 状態を表す true を返しても、マークが変わるだけでコンテンツの再生はし続けることができます。

応急処置とトレードオフ

ここまでの議論から、CPU 使用率の上昇を防ぐためには、該当のメソッドで常に true を返却するという修正をする必要があります。

func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
  // iOS16.1以降、ここで false を返すと CPU 使用率が異常に上がるので、常に true を返すようにする
  return true
}

この応急処置により、iOS16.1 以上であっても CPU 使用率が上がることなくピクチャ・イン・ピクチャを動作させることができるようになりました。

対策後:ピクチャ・イン・ピクチャ使用時のCPU使用率(iOS16/iPhone14Pro)

トレードオフは、コンテンツの再生状態であるにもかかわらず、ピクチャ・イン・ピクチャ上のマークが再生状態への移行を表す三角のマークのままになるというものです。

コンテンツは再生状態だが再生ボタンが表示されている状態

ピクチャ・イン・ピクチャの任意の領域をタップするとこのマークは消えるので、常に表示されているわけではなく致命的ではないのですが、ユーザーに混乱を与えかねません。しかし、ミラティブの配信コメントバーは幸い頻繁に再生と停止を切り替えるような機能ではありませんし*3、CPU 使用率の異常な上昇を抑えられるメリットと比較すると、致し方ないのかなと思います。

問題の再現ができるサンプルプロジェクト

ピクチャ・イン・ピクチャはそれほど多く使用されている機能ではないこともありますし、バグレポートを送るにしても簡単に再現して事象を確認できるプロジェクトを作成して報告時に Apple に共有することは有用です。Apple へ報告する際にも使用した簡単なサンプルアプリを GitHub にて公開しています。

naru-jpn/pip-battery-drain

スイッチをONにすると異常にCPU使用率が上がるサンプルアプリ

ピクチャ・イン・ピクチャを活用しているプロダクトもそれほど多くはないですが、今回の事象はネットの検索結果によると世の中の複数のプロダクトが影響を受けているのではないかと思います。情報も少ない中、この記事によってなにかが解決するプロダクトがあればいいと思い記事にさせていただきました。ピクチャ・イン・ピクチャを安全に使用できる環境が早く戻ってきて欲しいものです。

We are hiring!

ミラティブでは『好きでつながり、自分の物語(ナラティブ)が生まれる居場所』を実現するエンジニアを募集中です!

www.mirrativ.co.jp

speakerdeck.com

mirrativ.notion.site

*1:把握している限りでは AVPictureInPictureSampleBufferPlaybackDelegate を使用している場合のみ、今回の問題が発生する可能性があります。AVSampleBufferDisplayLayer で独自の描画処理を行うようなケースで使用します。

*2:パフォーマンスへの配慮については、iOSDC 2022 で話した こちらのスライド を参照ください。

*3:一部のユーザーさんは、例えばマルチプレイの募集などの際にIDが記載されたコメントが流れないように停止したりしています。早く根本的に問題が解消されることを願うばかりです。