[Xcode/Swift] AVFoundation完全理解への道④ (落とし穴)

この記事で学ぶこと:

AVFoundationのよくある落とし穴

①を読んでなくても問題ないですが、概念が曖昧というか方は①から読まれるのをおすすめします。👇

[Xcode/Swift] AVFoundation完全理解への道①

AVFoundationのよくある落とし穴

5.1 permission地獄(Info.plist注意)

😇 問題:

カメラ・マイク・フォトライブラリなど、Info.plistに抜けがあると即クラッシュ
しかもXcode上はビルド通るのに、実機で爆死するパターン。

🩹 解決法:

Info.plist に以下を忘れず追加:

<key>NSCameraUsageDescription</key>
<string>カメラを使います</string>
<key>NSMicrophoneUsageDescription</key>
<string>マイクを使います</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>写真ライブラリにアクセスします</string>

さらに、アクセス許可は非同期なので、ユーザーが拒否した場合のUI制御も忘れずに。

AVCaptureDevice.requestAccess(for: .video) { granted in
    DispatchQueue.main.async {
        if !granted {
            // アラート出すなり設定誘導するなり
        }
    }
}

5.2 セッションの切り替えでクラッシュ

💥 問題

複数セッション(録画と写真など)を切り替えようとすると、

  • Input/Outputの切り替えタイミング
  • Sessionの破棄 or 再構築
  • 非同期処理がかぶる

これらでクラッシュ or 動作不良になる。

🩹 解決法:

  • session.beginConfiguration()commitConfiguration()構成変更を囲む
  • セッションの切り替えはバックグラウンドQueueで処理
  • .startRunning() / .stopRunning()重複呼び出し禁止
DispatchQueue.global(qos: .userInitiated).async {
    self.session.beginConfiguration()
    
    // input/output差し替え
    self.session.removeInput(oldInput)
    self.session.addInput(newInput)
    
    self.session.commitConfiguration()
}

セッションは「リアルタイム処理」なので、メインスレッドでいじりすぎると破滅 🛏️


5.3 バッファ切れで再生停止 → 復旧処理

💥 問題:

HLS再生中にネットワークが切れたり弱くなったりすると、
AVPlayerが自動で止まって、そのままフリーズすることがある。

🩹 解決法:

  • isPlaybackBufferEmptyisPlaybackLikelyToKeepUpKVOで監視
  • バッファ切れ → ローディング表示、回復したら再開!
playerItem.observe(\.isPlaybackBufferEmpty, options: [.new]) { item, _ in
    if item.isPlaybackBufferEmpty {
        // ローディングUIを出す
    }
}

playerItem.observe(\.isPlaybackLikelyToKeepUp, options: [.new]) { item, _ in
    if item.isPlaybackLikelyToKeepUp {
        // バッファ回復 → 再生再開
        self.player.play()
    }
}

5.4 seekが思った通りに動かない理由

😵 問題:

HLSのシークで、秒数指定してもピッタリその時間に飛ばない
さらに、seekしても再生が始まらない or フレームが変わらないことも。

🩹 解決法:

  • シークは Iフレーム単位でしか飛べない(ピンポイント無理)
  • .toleranceBefore / .toleranceAfter を調整して「妥協」を加えると精度が上がる
  • プレイヤーが readyToPlay になる前にシークすると失敗する
let cmTime = CMTime(seconds: 15.0, preferredTimescale: 600)

player.seek(to: cmTime, toleranceBefore: .zero, toleranceAfter: .zero) { finished in
    if finished {
        print("✅ シーク完了")
    }
}

どうしても正確なフレーム画像が欲しい時は→ AVAssetImageGeneratorでサムネイルを抽出


まとめ

落とし穴やらかすと対策
Info.plist忘れ実機即クラッシュ事前チェックと動的Permission
セッション切替ミスクラッシュ/映らないQueueとConfigurationの使い方
バッファ切れ放置永遠に止まるKVO監視+自動再開処理
シーク精度ミス時間ズレ or 動かないIフレーム意識+tolerance調整

次回: AVFoundation内部処理雑学編

[Xcode/Swift] AVFoundation完全理解への道⑤ (内部処理雑学編)