WWDC Quick Look 💓 By SwiftGGTeam
Explore low-latency video encoding with VideoToolbox

Explore low-latency video encoding with VideoToolbox

元の動画を見る

ハイライト

VideoToolbox には低遅延 H.264 ハードウェアエンコードモードが追加され、フレーム並べ替えをなくし、ビットレートコントローラがネットワーク変化へ素早く適応できる。720p@30fps では約 100ms の遅延を削減でき、時間方向スケーラビリティと長期参照フレームにも対応する。

主な内容

ビデオ会議アプリを開発している。ユーザーから、会話に遅延があり、互いに話を遮ってしまうというフィードバックがあった。遅延はどこから来るのだろうか。

動画エンコーダ内部には通常、フレーム並べ替えバッファがある(B-frame で圧縮効率を高めるため)。これにより 3-5 フレームの遅延が発生する。ビットレートコントローラのネットワーク変化への応答も比較的遅い。これらが重なると、エンドツーエンドの遅延は 200ms を超えることがある。

VideoToolbox の新しい低遅延モードは、この問題に特化している。

詳細

低遅延モードの仕組み

02:05

低遅延モードには 2 つの重要な最適化がある。

  1. フレーム並べ替えをなくす。1 入力 1 出力のエンコードモードを採用し、各フレームは入力後すぐにエンコードして出力され、後続フレームを待たない。
  2. 高速なビットレート適応。ビットレートコントローラがネットワーク変化へより速く応答し、輻輳による遅延を減らす。

720p@30fps の動画では、低遅延モードにより約 100ms の遅延を削減できる。これはビデオ会議では非常に重要だ。

03:14

キーポイント:

  • 低遅延モードは常にハードウェアアクセラレーションエンコードを使う
  • H.264 エンコードのみをサポートする
  • iOS と macOS の両方でサポートされる
  • 圧縮効率はデフォルトモードよりわずかに低いが、遅延は大きく下がる

低遅延モードを有効にする

05:03

有効化は簡単で、compression session の作成時に flag を 1 つ設定するだけだ。

var encoderSpecification: CFMutableDictionary = CFDictionaryCreateMutable(
    kCFAllocatorDefault,
    0,
    &kCFTypeDictionaryKeyCallBacks,
    &kCFTypeDictionaryValueCallBacks
)

// 低遅延ビットレート制御を有効にする
CFDictionarySetValue(
    encoderSpecification,
    kVTVideoEncoderSpecification_EnableLowLatencyRateControl,
    kCFBooleanTrue
)

// compression session を作成
let session = VTCompressionSessionCreate(
    allocator: kCFAllocatorDefault,
    width: 1280,
    height: 720,
    codecType: kCMVideoCodecType_H264,
    encoderSpecification: encoderSpecification,
    imageBufferAttributes: nil,
    compressedDataAllocator: nil,
    outputCallback: outputHandler,
    refcon: &context,
    compressionSessionOut: &session
)

// ビットレートを設定(通常と同じ)
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_AverageBitRate,
    value: 2000000 as CFNumber  // 2 Mbps
)

05:03

キーポイント:

  • kVTVideoEncoderSpecification_EnableLowLatencyRateControl で低遅延モードを有効にする
  • その他の設定(ビットレート、解像度)は通常と同じ
  • エンコーダは自動的にハードウェアアクセラレーションを使用する
  • 出力コールバックでエンコード済みフレームを受け取る

新しい Profile のサポート

06:10

低遅延モードには 2 つの profile が追加された。

  • Constrained Baseline Profile (CBP):低コストのアプリ向けで、互換性が最も高い
  • Constrained High Profile (CHP):圧縮効率が高く、帯域幅が限られる場面に適している
// Constrained Baseline Profile を使用
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_ProfileLevel,
    value: kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel
)

// Constrained High Profile を使用
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_ProfileLevel,
    value: kVTProfileLevel_H264_ConstrainedHigh_AutoLevel
)

07:35

キーポイント:

  • CBP は互換性が最も高く、ほぼすべてのデコーダでサポートされる
  • CHP は圧縮効率がより高い
  • 相手側のデコード能力に応じて profile を選ぶ
  • AutoLevel により、エンコーダが level を自動選択する

時間方向スケーラビリティ

07:54

多人数のビデオ会議では、受信者ごとに利用できる帯域幅が異なる。従来の方法では受信者ごとに別々のストリームをエンコードするため、効率が低い。

時間方向スケーラビリティでは 1 つのストリームだけをエンコードし、フレームを 2 つのレイヤーに分ける。

  • Base layer:半分のフレームレートを含み、単独でデコードできる
  • Enhancement layer:補完フレームを含み、base layer と組み合わせて完全なフレームレートを提供する
// base layer のフレームレート比率を設定
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_BaseLayerFrameRateFraction,
    value: 0.5 as CFNumber  // フレームの半分を base layer へ割り当てる
)

// base layer のビットレート比率を設定
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_BaseLayerBitRateFraction,
    value: 0.6 as CFNumber  // ビットレートの 60% を base layer へ割り当てる
)

11:15

キーポイント:

  • base layer のフレームレートは入力の半分
  • base layer のビットレートは総ビットレートの 60%-80% が推奨される
  • 低帯域幅の受信者は base layer だけを受け取る
  • 高帯域幅の受信者は base + enhancement layer を受け取る
  • enhancement layer のフレーム損失は他のフレームに影響しない

最大フレーム量子化パラメータ

12:26

フレーム QP(Quantization Parameter)は画質とビットレートを制御する。QP が低いほど画質は高くなり、ビットレートは大きくなる。

低遅延モードでは、許可する最大 QP を設定し、複雑なシーンでエンコーダが画質を過度に下げるのを防げる。

VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_MaxAllowedFrameQP,
    value: 30 as CFNumber  // QP の上限は 30
)

14:22

キーポイント:

  • QP の範囲は 1-51
  • 上限を設定すると、エンコーダはそれより高い QP を使用しない
  • ビットレート予算が不足した場合、エンコーダは画質を下げるのではなくフレームを落とす
  • 画面共有など、文字の鮮明さが必要な場面に適している

長期参照フレーム(LTR)

14:45

ネットワークでパケットロスが発生すると、デコーダはリフレッシュフレームを要求する必要がある。従来の方法では I-frame(キーフレーム)を送信するが、I-frame は大きく、ネットワーク状態が悪いと輻輳を悪化させる可能性がある。

LTR は、より小さな予測フレーム(LTR-P)を使って I-frame の代わりにリフレッシュを行う。

// LTR を有効にする
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_EnableLTR,
    value: kCFBooleanTrue
)

// エンコーダは出力内で確認が必要な LTR フレームをマークする
// アプリケーション層が確認を受け取ったら、AcknowledgedLTRTokens でエンコーダへ通知する

// リフレッシュフレームを要求する
VTSessionSetProperty(
    session,
    key: kVTCompressionPropertyKey_ForceLTRRefresh,
    value: kCFBooleanTrue
)

17:07

キーポイント:

  • LTR フレームは通常の P-frame より少し大きいが、I-frame よりははるかに小さい
  • アプリケーション層はフレーム確認とリトライロジックを担当する
  • 確認済みの LTR がない場合、エンコーダは I-frame にフォールバックする
  • ネットワークが不安定な場面に適している

重要ポイント

  1. ビデオ会議アプリで低遅延エンコードを有効にする。1 行の設定で遅延を約 100ms 削減できる。主な API:kVTVideoEncoderSpecification_EnableLowLatencyRateControl

  2. 時間方向スケーラビリティで多人数会議を最適化する。1 つのストリームで帯域幅の異なる受信者に対応でき、複数ストリームをエンコードするより省電力。主な API:kVTCompressionPropertyKey_BaseLayerFrameRateFraction

  3. 画面共有向けに最大 QP を設定する。複雑なシーンで文字や UI がぼやけるのを防ぐ。主な API:kVTCompressionPropertyKey_MaxAllowedFrameQP

  4. LTR で低品質ネットワークからの回復力を高める。パケットロス後は LTR-P フレームでリフレッシュし、I-frame より早く到達する。主な API:kVTCompressionPropertyKey_EnableLTR

  5. 相手側のデコード能力に応じて profile を選ぶ。ローエンドデバイスとの通信には CBP、ハイエンドデバイスとの通信には CHP を使う。主な API:kVTCompressionPropertyKey_ProfileLevel

関連セッション

コメント

GitHub Issues · utterances