Visual Store を更新した。
前回の記事「Visual Storeに判断結果を残せるようにした」では、画像を開く前の判断と確認結果を保存するJudgment Layerについて書いた。今回は新しい機能ではなく、Debian 12 ARM64でVP9を有効にした既定ビルドが通らない問題と、Visual Store Skillの不具合報告手順を直した話になる。
Debian 12 ARM64で既定ビルドが通らなかった
Visual Storeは連続したPNGをVP9で時間方向に圧縮するため、Rustからsystem libvpxへ直接つないでいる。FFmpegを呼び出す構成ではなく、pkg-configで見つけたlibvpxとリンクする。
これまで開発に使っていたのは、主にmacOS ARM64とlibvpx 1.16.0だった。Debian 12 ARM64に入っているlibvpx 1.12.0で、READMEどおりにlocked installするとコンパイルで止まった。
cargo install --path . --locked
主なエラーは次の3点。
vpx_codec_error:
expected *mut vpx_codec_ctx, found &vpx_codec_ctx
vpx_codec_error_detail:
expected *mut vpx_codec_ctx, found &vpx_codec_ctx
slice::from_raw_parts:
length expected usize, found u64 (frame.sz)
--no-default-featuresを付ければインストールできた。PNGのinit、put、features、judgment、verifyも使える。
ただし、これはVP9 backendを無効にしているだけなので、完了したrunをpack --codec vp9できない。Visual Storeの既定手順がDebian 12で完走できないことは変わらない。
この問題はIssue #23「Debian 12のlibvpx bindingsで既定VP9 buildが型エラーになる」とIssue #24「Debian 12 libvpx 1.12で既定VP9 featureがcompileできない」で確認した。
同じcrateでもsystem headerによって型が変わる
Visual Storeはlibvpx-native-sys 5.0.17を固定している。しかし、実際に使うFFI bindingsは、接続先となるsystem libvpxのheaderや環境によって型に差が出る。
今回、Debian 12側のbindingsでは、エラー文字列を取得するvpx_codec_errorとvpx_codec_error_detailが*mut vpx_codec_ctx_tを要求していた。既存の実装は共有参照の&vpx_codec_ctx_tを渡していたため、型が一致しない。
単にraw pointerへcastすればコンパイルだけは通せる。ただ、共有参照から可変pointerを作ると、Rust側で保証しているaliasingとの関係を説明しにくくなる。
そこで、エラー処理へ渡すcontext自体を排他的な可変参照に変更した。
fn check(
operation: &'static str,
status: ffi::vpx_codec_err_t,
context: Option<&mut ffi::vpx_codec_ctx_t>,
) -> Result<()>
呼び出し元はencoderまたはdecoderのcontextをすでに可変で扱っている。その借用を診断処理まで維持すれば、古いheaderが要求する可変pointerにも、新しい環境のsignatureにも、constnessを無理に外すcastなしで渡せる。
取得したエラー文字列はlibvpx側が所有しているため、その場でRustの文字列へコピーする。contextの寿命を越えてC側のpointerを保持しない構成はそのままにした。
frame.szをそのままslice長にしない
もう一つは、encoderが返すpacketサイズの型。
新しい環境では既存コードのまま通っていたが、Debian 12 ARM64側ではframe.szがu64になり、std::slice::from_raw_partsが要求するusizeへそのまま渡せなかった。
ここもas usizeで変換すればコンパイルは通る。ただし、表現できない値が来たときに切り詰められる可能性がある。さらにRustのsliceは、全体のbyte数がisize::MAXを超えてはいけない。
修正後は、packetサイズを次の順で検査する。
libvpxのframe.sz
↓ TryInto<usize>
usizeで表現できなければエラー
↓
isize::MAXを超えていればエラー
↓
確認済みの長さでsliceを作る
実際に通常のpacketがこの上限へ達することは考えにくい。それでも、FFIから受け取った整数をmemory sliceの長さへ使う場所なので、コンパイラーを黙らせるためだけのcastにはしなかった。
Debian 12 ARM64をCIへ追加した
修正はPR #26「Fix Debian 12 ARM64 libvpx build and validate VP9」で入れた。
CIにはubuntu-24.04-arm runner上で、公式のrust:1.98.1-bookworm containerを使うjobを追加した。ここへDebian 12のlibvpx-dev 1.12.0とpkg-configを入れる。
確認するのはコンパイルだけではない。
cargo install --path . --locked
VP9 codecのunit test
時間方向codecのtest
packのatomicityと再試行test
VP9 round-trip example
CLIでinit → put×3 → pack → verify
最後のCLI確認では、同じPNGを3 frame登録し、実際にVP9 segmentへpackしてからstore全体をverifyする。bindingsがコンパイルできてもencoderやdecoderの呼び出しが壊れていれば、ここで止まる。
対応するsystem libvpxの範囲は、READMEとADRへ1.12.0から1.16.0までと明記した。下限のDebian 12 ARM64と、開発時に使っている1.16.0だけでなく、UbuntuとmacOSのsystem packageを使った既存CIも引き続き動かしている。
同じ不具合のIssueが二つできた
今回、ほぼ同じ再現条件とエラーを扱うIssue #23と#24が作られた。
コード側の問題とは別に、Visual Store Skillの不具合報告手順にも穴があった。Issueを作る前にopen・closed両方を検索し、タイトルだけでなく本文まで読むことを明示していなかった。
Skillへ次の手順を追加した。
再現可能な不具合か確認する
↓
エラーコード、コマンド、症状、再現条件の表記を変えて検索する
↓
open・closed両方の候補本文を読む
↓
同じ原因または同じ再現条件なら既存Issueを案内する
↓
該当がない場合だけ新しいIssueを作る
修正済みIssueと症状が似ていても、別versionで再発しているなら新しいIssueにする余地は残している。ただし、その場合はversion差など、既存Issueと何が違うかを先に確認する。
Issueへ載せるログ、パス、画像、metadataから秘密情報を除くことと、仕様どおりの拒否や未確認の推測を確定した不具合として報告しないことも追加した。
合わせてVisual Store SkillとCLI reference、VP9 codecのADRを英語へ統一した。CLI本体の機能が変わる更新ではないが、コーディングエージェントへ渡す手順として、判断条件を読み違えにくい形へ揃えている。
まとめ
今回の実装変更は小さく、contextの借用方法とpacket長の変換が中心だった。ただ、system libraryへ直接つなぐ以上、開発機にある新しいlibvpxだけで通っていても対応できたことにはならない。
Debian 12 ARM64とlibvpx 1.12.0をCIへ固定したことで、古い安定版distributionでも、既定機能のインストールからVP9のpack・verifyまで確認できるようになった。
同時に、同じ不具合のIssueを二つ作ったことで、エージェント用SkillにもIssue検索の手順が必要だと分かった。実装だけでなく、不具合を見つけて報告するところも運用の一部として直した。