プロジェクト監査。「監査して」「audit」「/audit」で発火。設計/UX/品質/テストなどの観点でコードベース全体を監査し、課題をissuesディレクトリに書き出す。実行ログで重複実行を防止。特定の差分・コミットのレビューには使わない(codex-review / cross-review を使う)。
avfoundation-reference
AVFoundation (AVPlayer / AVPlayerLayer / AVPlayerItemVideoOutput / AVAsset) の落とし穴・文書化されていない実装挙動・debugging チェックリストを集約した reference skill。
Swift / Objective-C で AVPlayer を使った動画再生 / seek / scrub / frame stepping を実装・debug するときに発火。VLCKit ではなく **AVFoundation 系** の問題に特化。
含まれるファイル(1)
- SKILL.md10.4 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
AVFoundation Reference & Debugging Skill
AVPlayer / AVPlayerLayer / AVPlayerItemVideoOutput / AVAsset まわりの 公式仕様だけでは 読み取れない実装挙動 と debug 手順 を集約する skill。Apple Developer Documentation を引いても載っていない「やってみないと分からない」落とし穴が中心。
発火条件
以下のいずれかに該当したら本 skill を必ず一度通す:
- AVPlayer の seek / scrub / frame stepping を実装する
- AVPlayer の playback rate / pause / play 挙動を変える
- AVPlayerLayer の表示 frame と playback clock の関係を扱う
- AVPlayerItemVideoOutput の attach / detach lifecycle を変える
- AVPlayer の completion handler / KVO observer の thread モデルを扱う
- 動画の「コマ送り」「scrub」「click seek」「frame-stepping」体感が想定と異なる
鉄則
1. 観測 first、推測 second
ユーザーが見ている現象 (= 映像 / UI / 音声) を 直接観測 する手段を、修正コードを書く 前に必ず通す。log の数値 (= playback clock / cached currentTime) だけでは「ユーザーが見て いる frame」と乖離していることが頻繁にある。
具体的:
- Instruments の Core Animation ツール で
AVPlayerLayerの表示 frame 更新タイミングを観測 AVPlayerItemVideoOutput.copyPixelBuffer(forItemTime:itemTimeForDisplay:)のitemTimeForDisplayを log に出して、AVPlayer の playback clock との乖離を測る- 1 click では一般化しない。複数サンプル (= 5-10 click) で挙動を確認してから判断する
2. Apple 公式 doc を必ず引く
AVFoundation 系の問題に当たったら、最低限以下を WebFetch で取得 / 確認する:
- 該当 API の Apple Developer Documentation (=
https://developer.apple.com/documentation/avfoundation/...) - WWDC sessions (= 「AVPlayer best practices」「Advances in AVPlayer」等の年度別 session)
- AVPlayer / AVPlayerItem の Sample Code
経験則 / Stack Overflow ベースで進めると、Apple が後から仕様を変えた時に振り回される。
落とし穴カタログ
A. AVPlayer.seek(to:tolerance:) の I-frame 着地は 実装依存
- tolerance > 0 の場合、AVPlayer は target ± tolerance の範囲内の I-frame を選ぶ
- 「最近接 I-frame に必ず着地」と保証されているわけではない。Apple 実装依存で、 I-frame 配置 / 再生中の最適化 / playback rate 等で変わる
- 観測例 (実機ログ、2026-05-09):
- 同じ tolerance=5s で、ある click では
landedMinusTarget=21ms(= ジャスト)、 別 click ではlandedMinusTarget=-712ms(= 0.7 秒ズレ) と挙動が分かれた
- 同じ tolerance=5s で、ある click では
- 対策: tolerance を小さくする (= frame-accurate に近づく) と着地は安定するが、 seek 速度が遅くなる + decoder 負荷が上がる
B. AVPlayerItemVideoOutput.copyPixelBuffer の itemTimeForDisplay は GOP 由来
forItemTimeで要求した時刻に対して、itemTimeForDisplayは 「forItemTime以前で 取得可能な最も近い frame の時刻」 を返す- AVPlayer が target=4551.513s に正確に seek しても、
itemTimeForDisplay=4551.233s(= 着地点 -280ms = GOP 内の前 I-frame) が返ることがある - これは AVPlayerLayer が表示している frame の時刻 とほぼ一致する
- 重要:
AVPlayer.currentTime()と「実際に画面に表示されている frame の時刻」は 別物。click seek が「動かなかったように見える」原因はここに集約することが多い
C. AVPlayer.currentTime() は Main thread から呼ぶと deadlock しうる
- macOS で観測例: Main thread から
avPlayer.currentTime()を呼ぶと MediaToolbox 内部の pthread_mutex に詰まって UI 完全停止 - 対策:
addPeriodicTimeObserverの closure 内で cached value を保持し、Main thread からは cached を参照する - 詳細は VLCMultiVideoPlayer プロジェクトの
AVFoundationVideoPlayer.swiftのcurrentTimeプロパティ周辺コメント参照
D. AVPlayerItemVideoOutput を attach すると pipeline が変わる
- Apple 公式仕様には明示されていないが、実装上
AVPlayerItemVideoOutputが attach されて いると AVPlayer pipeline が frame display を駆動する mode になる - 永続 attach を撤廃すると、シークバー drag のコマ送り (= frame-stepping scrub) が動か
なくなる事例あり (VLCMultiVideoPlayer の commit
c845249で確認) - 対策: drag scrub を機能させたいなら永続 attach を維持する。通常再生中の負荷が 気になる場合は probe lifetime に scope 限定したくなるが、scrub 機能と両立しない
E. KVO observer / completion handler は任意スレッドで fire
AVPlayerItem.status/AVPlayer.timeControlStatus等の KVO callback、AVPlayer.seek(to:completionHandler:)の completion は 任意スレッド で fire する- Main thread でない可能性が常にある
- 対策: closure 内で
Task { @MainActor [weak self] in ... }で MainActor へ hop する - 例外:
addPeriodicTimeObserver(forInterval:queue:using:)でqueue: .mainを指定した closure は確実に main thread で fire する →MainActor.assumeIsolatedで hop なし可
F. addPeriodicTimeObserver は seek 中も fire する
- seek の途中の中間時刻も periodic observer に流れてくる
- seek completion 時の cached currentTime は 「最後の periodic tick」の値 で、
AVPlayer 実着地点と乖離していることがある (
completionCachedAgeMsで測ること)
G. automaticallyWaitsToMinimizeStalling = true (default) の挙動
- buffer underrun 検知時に AVPlayer 内部で
rate=0で待機 → buffer 戻り → resume - rate ≥ 2.5x で buffer 消費が早いと underrun → pause → resume の発振が「コマ送り」 として観測される 可能性がある (= ただし確定していない、観測例では rate と無関係に 発生したケースあり)
falseに設定しても改善しない事例も観測されている (VLCMultiVideoPlayer issue 332 Step 2)
Debugging チェックリスト (= 修正前に通す)
映像 / 再生問題が出たら、修正コードを書く前に以下を確認する:
- 問題はユーザーが見ている "何"?: 映像 / UI / 音声 / 時間表示 のどれか特定
- 複数サンプルで再現する: 5-10 click / drag で挙動が一貫するか確認
-
displayfield を log に出している?:AVPlayerItemVideoOutput.copyPixelBufferのitemTimeForDisplayを log に追加。current(= playback clock) と乖離しているか - Apple 公式 doc を引いた?: 該当 API の WebFetch + WWDC session 確認
- Instruments で AVPlayerLayer を観測した?: Core Animation ツールで表示更新を見る
- 既存の落とし穴カタログ (上記) に該当しないか?: 1 つでも該当したら推測の前に検証
- 修正方針はユーザー意図と合致?: 「ずらして再生でいい」など曖昧な指示は 1 確認入れる
過去事例: VLCMultiVideoPlayer の click seek 問題 (2026-05-09)
ユーザー報告: 「シークバーを click すると一瞬戻ってから同じ位置で再生される」
失敗した推測 (= 3 回 revert)
- snap back 対策 (= cached value 渡し) → UI 段差消したが体感変わらず
- seek 中 pause + 250ms delay → AVPlayer 内部で paused → playing 遷移したが体感悪化
- click seek tolerance 分離 (= 0.5s) → AVPlayer は target ジャスト着地するが体感変わらず
真の原因 (= 最後の最後で判明)
- AVPlayer は target に正確に着地 (
landedMinusTarget=0ms) - しかし
AVPlayerLayerが表示する frame は GOP 内の前 I-frame (=display=4551.233s、 着地 4551.513s から -280ms 前) - これは 落とし穴カタログ B に該当
- log には最初から
displayfield が出ていたが、私 (Claude) は最後まで見落とした
学び
- log の 数値だけ で原因推定すると、表示パイプラインの別側面 (= GOP / itemTimeForDisplay) を見落とす
AVPlayer.currentTimeと「実表示 frame」は別物- Apple 公式 doc を引いていれば
itemTimeForDisplayの意味は最初から分かった - 詳細は VLCMultiVideoPlayer プロジェクトの issue 333
関連プロジェクト / リソース
-
VLCMultiVideoPlayer リポジトリ(旧配置
~/src/my-products/apps/vlc-multi-video-player/は 2026-07-03 時点でこのマシンに存在しない。参照する前にmdfind -name AVFoundationVideoPlayer.swift等で所在を確認すること)VLCMultiVideoPlayer/VideoPlayer/AVFoundationVideoPlayer.swift(= AVPlayer 実装本体)VLCMultiVideoPlayer/VideoPlayer/AVPlayerSeekDiagnostics.swift(= 計測 log)issues/pending/332-bug-high-rate-playback-frame-stutter.md(= rate ≥ 2.5x の stutter 問題)issues/done/333-bug-click-seek-perceived-no-op.md(= click seek 体感問題、本 skill の主要事例)
-
Apple Developer Documentation:
-
関連 skill:
swift-vlc-player: 削除済み (= VLCKit 専用、本プロジェクトでは未使用)ios-app-developer: iOS 全般 (= AVFoundation 個別の落とし穴は本 skill 側に集約)crash-log-analyzer: AVPlayer 系 crash の解析
改訂履歴
- 2026-05-09: 初版。VLCMultiVideoPlayer の issue 333 (click seek 体感問題) の learning を
落とし穴 B (= itemTimeForDisplay の GOP 由来挙動) として収録。
swift-vlc-playerskill を 削除して本 skill に置き換え。
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
このセッションで行った変更をコミットする。「コミットして」「commit」「/c」で発火。push はしない/クレデンシャルは混入させない。
ディレクトリ・レイヤーごとに置いた CLAUDE.md と README.md が実体 (コード・コマンド・構成) とずれていないかを点検し、裏の取れた乖離だけを直す。引数で対象のディレクトリを任意に指定できる (省略時は repo 全体)。「CLAUDE.md を refresh して」「README が古くないか見て」「claude-md-refresh」「/claude-md-refresh」で発火。issues/ の更新漏れは issue-writeback / issue-sync の担当で、この skill は扱わない。
Codex が設計・実装を担い、Claude が要件確定・成果物の検証・反復・commit/push を管理する。「codex に書かせて」「codex メインで実装」「codex-drive」で発火。大きめの機能・移植・プロトコル実装向け。余剰トークンを厚く使う運用も選べる。
タスク着手時に codex にリードしてもらうワークフロー。codex に設計/方針を主導(リード)させ、その方針に沿って Claude が実装し、実装後は codex で設計適合・実装正当性・敵対的(red team)の 3 観点でレビューする。余っている codex トークンを積極的に消費したい時に使う。「codexにリードしてもらって」「設計から codex に任せて」「タスクを codex 主導で」「codex-lead」「/codex-lead」で発火。
codex exec review を基本に、必要なら codex exec を使ってコード変更のレビューを依頼し、指摘事項を報告する。通常 / 厳しめ (--strict) / 敵対的 (--adversarial、実装を壊しにいく red team) の 3 モードを持つ。「codexでレビューして」「codex-review」「/codex-review」「敵対的にレビューして」で発火。Codex 単体での単独レビュー用途。複数エージェント並行レビューは cross-review、差分ではなくコードベース全体の監査は audit を使う。