Don't force close ffmpeg if no need
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 16 Jan 2025 10:42:41 +0000 (11:42 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 16 Jan 2025 10:42:41 +0000 (11:42 +0100)
music_assistant/helpers/audio.py
music_assistant/helpers/process.py

index ffe42da2450b180c2ad89e573c7c98c9152e9b6b..3a4aff70aa0051b63b5aefc64a6351526b5dee67 100644 (file)
@@ -383,6 +383,8 @@ async def get_media_stream(
         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
@@ -390,21 +392,26 @@ async def get_media_stream(
         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
index 21d0ef03dd073d6a37aa76c264513a306de6ed99..009e1c66b3febc333f625a5506fbb906b00fcd1a 100644 (file)
@@ -257,6 +257,10 @@ class AsyncProcess:
             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."""