TECHNICAL CASE NOTE DirectShow動画 · 音声途切れ · winegstreamer / GStreamer 解決済み・2026-06-27 実機確認

高ビットレート動画で音声だけが途切れる

winegstreamer の multiqueue 上限を 8 MB から 64 MB へ

§1

映像は動き続け、音声だけが約1秒途切れる

約90秒の動画を再生すると、毎回同じ地点で音声供給が途切れた。映像は30 fpsで継続し、音声が戻った後は途切れた時間分だけA/V同期がずれた。

映像は普通に動く

観測: 問題区間でも映像の present は約33 ms間隔で継続した。

音だけ ~1秒 無音

観測: 音声の send_sample が1.092秒途絶した。

復帰後ずっとA/Vがズレる

観測: 供給再開後も、失った時間分の同期ずれが残った。

毎回、映像PTS 22.5秒付近で発生

観測: 偶発的な負荷ではなく、同じデータ区間で再現した。
対象の分離: 本稿で扱うのは、長い動画の途中で発生する音声供給の途絶である。起動直後の短いロゴ動画で観測したA/V同期問題は、再生位置も経路も異なるため別件として切り分けた。
§2

検証した仮説

処理負荷、音声レンダラ、ストリーム受け渡しを順に検証した。推測ではなく、ベンチマークと待機スタックで成立条件を確認した。

2026-06-26 ・ 前セッション由来

単一バッファの受け渡しで詰まる棄却

Wine 側の単一バッファ受け渡しを疑ったが、GStreamer の multiqueue にはすでに約2.3秒分が蓄積されていた。音声コーデックも別動画の情報と混同していた。

棄却した根拠: 実際のキュー残量とコーデックを測ると、想定した受け渡し層は停止地点ではなかった。
2026-06-26 → 27

音声レンダラのバッファを深くする棄却・撤回

dsoundrender のバッファを拡張したところ、停止やスキップ時の状態遷移と噛み合わず、再生パイプラインがデッドロックした。

判断: 症状を隠すために出力側を深くする方法は撤回し、供給が止まる上流を調べ直した。
2026-06-27

映像デコードがCPUを使い切る棄却

高ビットレートのWMV3をRosetta上で処理するため、デコード負荷を疑った。実ファイルを測るとx86単スレッドでも実時間の16倍、arm64でも22.7倍だった。

棄却した根拠: どちらも再生に必要な速度を大幅に上回り、native化しても本症状の停止地点は変わらない。
2026-06-27 ・ targeted trace

映像キューのbyte上限がdemuxerを止める採用

映像キューは8,423,616 byteまで増えて8 MB上限を超過し、音声キューは0 byteになった。同時にdemuxerは gst_multi_queue_chain で待機していた。

確認した機構: A/Vを交互に読むdemuxerが満杯の映像キューへのpushで停止し、次の音声パケットを読めなくなっていた。
§3

8 MB上限が音声供給を止める

変更したのは decodebin 内部の multiqueue 上限である。修正前後を切り替えると、映像キューの満杯が demuxer と音声供給へ伝わる経路を比較できる。

asf
demux
A/V インターリーブ供給
queue_1 映像
17 Mbps
上限 8 MB
queue_0 音声
 
 
dsound → スピーカー
§4

調査手順

症状を再生位置と処理段階で分け、デコード性能、キュー残量、スレッドの待機先を順に測定した。

発生地点の異なる症状を分離する

起動ロゴの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 smax-size-bytes = 64 MBmax-size-buffers = 0 とし、同じ動画を全編再生した。音声出力の0.3秒超gapが0件になったことを確認した。

§5

観測結果

観測 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できず待機していた。キュー残量のトレースと停止スタックが一致した。

§6

再利用できる知見

実装前に処理能力を測る

高ビットレートという特徴だけで負荷を断定せず、実ファイルのベンチマークでデコード性能を確認する。

CPU使用量より待機先を見る

供給が止まったスレッドの待機先を確認すると、処理が遅いのか、下流でブロックされているのかを分けられる。

ログとプロセスsampleを突き合わせる

キュー残量と実スタックが同じ停止地点を示すことで、単一の観測方法に依存せず原因を確認できる。

内部カウンタと可聴症状を分ける

overrunや空キューの回数ではなく、音声出力のgapと実際の再生結果を受け入れ指標にする。

修正は停止している層に限定する

音声レンダラの状態機械を広く変えず、demux を止めていた multiqueue 上限だけを調整した。

§7

現在の状態と適用範囲

確認できていること

  • 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をフルリビルドするときは、設定値と同じ動画による回帰確認を再実施する。