在使用 Qt 的 QProcess
以調用外部 ffmpeg/ffprobe 進行音視頻處理時,常見的工作流程是:
-
gatherParams:通過
ffprobe
同步獲取媒體文件的參數(分辨率、采樣率、聲道數、碼率等)。 -
reencode:逐個文件調用
ffmpeg -crf
或者ffmpeg -c:a libmp3lame
,異步重新編碼到統一格式。 -
concat:生成
concat_list.txt
后,調用ffmpeg -f concat
將中間文件拼接成最終輸出。
在 Debug 模式下一切運行正常,但切換到 Release(尤其是打包到生產環境)后,卻經常出現程序卡在“開始拼接…”或無法結束 waitForFinished()
,不拋錯也不返回的怪異現象。本文將深入剖析其背后的根因,并提供簡單可靠的解決方案。
一、問題重現
-
視頻合并:
VideoMerger
在startConcat()
調用ffmpeg -f concat
后,永遠等不到finished()
,進度條卡住。 -
音頻合并:同理,
AudioMerger
在拼接階段也陷入“死循環”。 -
Debug 模式:IDE 輸出面板能看到 ffmpeg 日志,流程正常結束。
-
Release 模式:IDE 不在;也沒有 QProcess 輸出日志;程序停在那不動。
二、為什么會卡死?
操作系統對每個子進程的 stdout 和 stderr 都設有管道緩沖區(通常約 4–64?KB 不等)。當 ffmpeg/ffprobe 輸出日志到 stderr(ffmpeg 默認把進度和警告都輸出到 stderr)時:
-
如果 沒人讀取,緩沖區一旦寫滿,子進程再寫就被阻塞。
-
被阻塞的子進程卡在寫日志上,無論它是否執行到“結束邏輯”,都無法調用
exit()
,因此 Qt 收不到finished()
信號。 -
程序就好像“掛住”了:既不成功也不報錯。
三、為什么 Debug 下不出問題?
-
IDE 幫你讀管道
在 Qt Creator、Visual?Studio 等調試器中,子進程的 stderr/stdout 會被自動轉發到“應用輸出”視圖——等于在后臺不斷做read()
。 -
執行速度慢
Debug 編譯跑得更慢,ffmpeg 輸出日志的速度往往跟不上緩沖區填滿的節奏;另外,IDE 讀緩沖區的效率也幫忙拉低了寫入速度。
四、兩個簡潔可靠的解決方案
1. 持續 drain 輸出
-
優點:不修改 ffmpeg 參數,兼容所有場景。
-
做法:
// 對于每個 QProcess,都這樣設置并連接: proc->setProcessChannelMode(QProcess::MergedChannels); connect(proc, &QProcess::readyReadStandardError, this, [=](){proc->readAllStandardError(); // 或者 readAllStandardOutput() });
-
原理:將 stdout 和 stderr 合并到一條管道,只需持續讀取一次就能清空所有日志,避免緩沖區寫滿。
2. 直接轉發到父進程
-
優點:無需在代碼里手動讀;可以在控制臺直接看到 ffmpeg 輸出。
-
做法:
-
proc->setProcessChannelMode(QProcess::ForwardedChannels);