yield buffer
del buffer
finished = True
+ # wait until stderr also completed reading
+ await ffmpeg_proc.wait_with_timeout(5)
except Exception as err:
if isinstance(err, asyncio.CancelledError):
# we were cancelled, just raise
logger.error("Error while streaming %s: %s", streamdetails.uri, err)
streamdetails.stream_error = True
finally:
- logger.log(VERBOSE_LOG_LEVEL, "Closing ffmpeg...")
- await ffmpeg_proc.close()
+ if not finished:
+ logger.log(VERBOSE_LOG_LEVEL, "Closing ffmpeg...")
+ await ffmpeg_proc.close()
# try to determine how many seconds we've streamed
seconds_streamed = bytes_sent / pcm_format.pcm_sample_size if bytes_sent else 0
+ if ffmpeg_proc.returncode != 0:
+ # dump the last 25 lines of the log in case of an unclean exit
+ log_tail = "\n" + "\n".join(list(ffmpeg_proc.log_history)[-25:])
+ logger.debug(log_tail)
+ else:
+ log_tail = ""
logger.debug(
- "stream %s (with code %s) for %s - seconds streamed: %s",
+ "stream %s (with code %s) for %s - seconds streamed: %s %s",
"finished" if finished else "aborted",
ffmpeg_proc.returncode,
streamdetails.uri,
seconds_streamed,
+ log_tail,
)
- if ffmpeg_proc.returncode != 0:
- log_tail = "\n".join(list(ffmpeg_proc.log_history)[:25])
- logger.debug(log_tail)
streamdetails.seconds_streamed = seconds_streamed
# store accurate duration
self._returncode = await self.proc.wait()
return self._returncode
+ async def wait_with_timeout(self, timeout: int) -> int:
+ """Wait for the process and return the returncode with a timeout."""
+ return await asyncio.wait_for(self.wait(), timeout)
+
async def check_output(*args: str, env: dict[str, str] | None = None) -> tuple[int, bytes]:
"""Run subprocess and return returncode and output."""