我正在尝试使用流构建一个播放器。主要想法是让一个线程运行一个播放器,读取来自另一个线程同时下载youtube音频的字节。代码工作了一段时间,内容正确流式传输,但几秒钟后,我总是得到这个错误:Exception: fd:13: hPutBuf: resource vanished (Broken pipe)
.
我想我错过了一些东西,因为即使使用connect
函数的结果是相同的。下面是代码(简化):
import Control.Concurrent
import System.IO.Streams
import Data.ByteString
main = do
(sink,_,_,_) <- runInteractiveCommand "mplayer -novideo - cache 5096 -"
mainSink <- lockingOutputStream sink -- main audio stream, goes straight to player
(_,source,_,_) <- runInteractiveCommand "yt-dlp "https://www.youtube.com/watch?v=uFtfDK39ZhI" -f bv+ba -o -"
loop mainSink source
loop :: OutputStream ByteString -> InputStream ByteString -> IO ()
loop sink src = do
sourceBytes <- peek src
case sourceBytes of
Nothing -> do loop sink src
Just _ -> do
audioBytes <- read src
write audioBytes sink
loop sink src
问题似乎是mplayer
在stdout和stderr上生成其通常的详细终端输出,而yt-dlp
在stderr上类似地生成输出。由于您丢弃了这些句柄,并且从未吸干它们,因此最终管道缓冲区会被填满,进程就会卡住。我不能确切地说为什么一个或两个进程死亡而不是挂起,但这就是正在发生的事情。下面是一个简单的示例,将不需要的输出重定向到/dev/null
,并且看起来有效:
import System.IO.Streams
main = do
(sink,_,_,_) <- runInteractiveCommand "mplayer -cache 5096 - 2>/dev/null >&2"
(_,source,_,_) <- runInteractiveCommand "yt-dlp "https://www.youtube.com/watch?v=uFtfDK39ZhI" -f bv+ba -o - 2>/dev/null"
connect source sink