multithreadinghaskellconcurrencystreambytestring

Haskell broken pipe error when working with streams


I'm trying to build a player using streams. The main idea is to have a thread running a player that reads from bytes that come from another thread that downloads youtube audio concurrently. The code works for a while and the content is streamed correctly, but after a few seconds I always got this error: Exception: fd:13: hPutBuf: resource vanished (Broken pipe).

I guess I'm missing something, because even when using the connect function the result is the same. Here's the code (simplified):

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

Solution

  • The problem appears to be that mplayer is generating its usual verbose terminal output on stdout and stderr, while yt-dlp is similarly generating output on stderr. Since you toss these handles away and never drain them, eventually the pipe buffers fill, and the processes get stuck. I can't say precisely why one or both of the processes dies instead of just hanging, but that's what's happening. Here's a simple example that redirects the unneeded output to /dev/null and appears to work:

    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