映像は動き続け、音声だけが約1秒途切れる
約90秒の動画を再生すると、毎回同じ地点で音声供給が途切れた。映像は30 fpsで継続し、音声が戻った後は途切れた時間分だけA/V同期がずれた。
映像は普通に動く
音だけ ~1秒 無音
send_sample が1.092秒途絶した。復帰後ずっとA/Vがズレる
毎回、映像PTS 22.5秒付近で発生
検証した仮説
処理負荷、音声レンダラ、ストリーム受け渡しを順に検証した。推測ではなく、ベンチマークと待機スタックで成立条件を確認した。
単一バッファの受け渡しで詰まる棄却
Wine 側の単一バッファ受け渡しを疑ったが、GStreamer の multiqueue にはすでに約2.3秒分が蓄積されていた。音声コーデックも別動画の情報と混同していた。
音声レンダラのバッファを深くする棄却・撤回
dsoundrender のバッファを拡張したところ、停止やスキップ時の状態遷移と噛み合わず、再生パイプラインがデッドロックした。
映像デコードがCPUを使い切る棄却
高ビットレートのWMV3をRosetta上で処理するため、デコード負荷を疑った。実ファイルを測るとx86単スレッドでも実時間の16倍、arm64でも22.7倍だった。
映像キューのbyte上限がdemuxerを止める採用
映像キューは8,423,616 byteまで増えて8 MB上限を超過し、音声キューは0 byteになった。同時にdemuxerは gst_multi_queue_chain で待機していた。
8 MB上限が音声供給を止める
変更したのは decodebin 内部の multiqueue 上限である。修正前後を切り替えると、映像キューの満杯が demuxer と音声供給へ伝わる経路を比較できる。
demux
17 Mbps
調査手順
症状を再生位置と処理段階で分け、デコード性能、キュー残量、スレッドの待機先を順に測定した。
発生地点の異なる症状を分離する
起動ロゴのA/V同期と、長い動画の途中で起きる音声途絶を別件にした。本稿では映像PTS 22.5秒付近の再現だけを対象にした。
動画の実体とデコード速度を測る
ffprobe でWMV3・約17 Mbpsと確認し、ffmpeg でx86とarm64を比較した。x86単スレッドでも16倍速だったため、CPU飽和を除外した。
音声供給とキュー残量を同時に記録する
WineとGStreamerのトレースを同じ時間軸へ揃え、send_sample の途絶と、映像・音声キューの残量を比較した。
プロセスsampleで待機先を確認する
別に採取したsampleでも、demuxerが gst_multi_queue_chain の条件変数で待機していた。ログ上の満杯と実際の停止スタックが一致した。
上限だけを変えて再検証する
max-size-time = 5 s、max-size-bytes = 64 MB、max-size-buffers = 0 とし、同じ動画を全編再生した。音声出力の0.3秒超gapが0件になったことを確認した。
観測結果
観測 A ── multiqueue残量(修正前)
gap @ 映像 PTS 22.5 s queue_1(映像): "filled, signalling overrun" bytes 8423616 / 8388608 ← 8 MB超過 queue_0(音声): visible 0/5 bytes 0 / 8388608 ← 完全に空 音声 send_sample: 1054084.195 → 1054085.287 = 1.092 s 途絶
映像キューが満杯になった時点でdemuxerが停止し、次の音声パケットを読めず、音声キューが空になった。
観測 B ── ffmpegデコードベンチ
対象動画 先頭30s・映像のみ decode x86 (Rosetta) threads=1 : speed= 16x x86 (Rosetta) threads=0 : speed= 17x arm64 (native) threads=1 : speed=22.7x → decode は元からボトルネックでない → native化でも停止地点は説明できない
x86単スレッドでも再生速度を十分に上回ったため、デコード性能は停止原因ではなかった。
観測 C ── 停止中のプロセスsample
Thread asfdemux5:sink (880/880)
gst_pad_push
gst_multi_queue_chain
_pthread_cond_wait ← 満杯キュー待ち
Thread multiqueue5:src_1 (映像)
gst_video_decoder … present 継続
Thread 017c (音声renderer)
次サンプル待ちで sleep
demuxerが満杯の映像キューへpushできず待機していた。キュー残量のトレースと停止スタックが一致した。
再利用できる知見
実装前に処理能力を測る
高ビットレートという特徴だけで負荷を断定せず、実ファイルのベンチマークでデコード性能を確認する。
CPU使用量より待機先を見る
供給が止まったスレッドの待機先を確認すると、処理が遅いのか、下流でブロックされているのかを分けられる。
ログとプロセスsampleを突き合わせる
キュー残量と実スタックが同じ停止地点を示すことで、単一の観測方法に依存せず原因を確認できる。
内部カウンタと可聴症状を分ける
overrunや空キューの回数ではなく、音声出力のgapと実際の再生結果を受け入れ指標にする。
修正は停止している層に限定する
音声レンダラの状態機械を広く変えず、demux を止めていた multiqueue 上限だけを調整した。
現在の状態と適用範囲
確認できていること
- 2026-06-27の実機検証で、修正版は対象動画をPTS 0から89.5秒まで再生し、約1秒の無音が解消した。
- 修正後は音声出力の0.3秒超gapが0件となり、映像キューは最大約16.2 MBまで吸収した。
- 現在の同梱runtimeにも、当時検証した64 MB版の
winegstreamer.soが残っている。
この結果から断定しないこと
- 確認範囲はWMV3・WMA音声を含む1本の高ビットレート動画であり、すべての動画音切れに当てはまるわけではない。
- 瞬間的なoverrunや空キューだけでは可聴不具合と判定せず、音声出力gapと実再生を確認する。
- 起動ロゴのA/V同期やゲーム本編のSEノイズは別経路の問題であり、本修正の対象外である。
- runtimeをフルリビルドするときは、設定値と同じ動画による回帰確認を再実施する。