few small optimizations
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 26 Mar 2024 08:13:32 +0000 (09:13 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 26 Mar 2024 08:13:32 +0000 (09:13 +0100)
music_assistant/server/controllers/streams.py
music_assistant/server/helpers/process.py

index d713115862f9041c406e40efd131315c53704444..70762a12449718a4c223ad83ccdeefba8411ea9b 100644 (file)
@@ -898,7 +898,8 @@ class StreamsController(CoreController):
                 # no crossfade enabled, just yield the buffer last part
                 bytes_written += len(buffer)
                 yield buffer
-                del buffer
+            # make sure the buffer gets cleaned up
+            del buffer
 
             # update duration details based on the actual pcm data we sent
             # this also accounts for crossfade and silence stripping
@@ -930,13 +931,13 @@ class StreamsController(CoreController):
         fmt = announcement_url.rsplit(".")[-1]
         audio_format = AudioFormat(content_type=ContentType.try_parse(fmt))
         extra_args = []
-        filter_params = ["loudnorm=I=-10:LRA=7:tp=-2:offset=-0.5"]
+        filter_params = ["loudnorm=I=-10:LRA=11:TP=-2"]
         if use_pre_announce:
             extra_args += [
                 "-i",
                 ANNOUNCE_ALERT_FILE,
                 "-filter_complex",
-                "[1:a][0:a]concat=n=2:v=0:a=1,loudnorm=I=-10:LRA=7:tp=-2:offset=-0.5",
+                "[1:a][0:a]concat=n=2:v=0:a=1,loudnorm=I=-10:LRA=11:TP=-2",
             ]
             filter_params = []
         async for chunk in get_ffmpeg_stream(
@@ -1026,12 +1027,11 @@ class StreamsController(CoreController):
             stderr_data = ""
             async for line in ffmpeg_proc.iter_stderr():
                 line = line.decode().strip()  # noqa: PLW2901
-                if not line:
-                    continue
                 if stderr_data or "loudnorm" in line:
                     stderr_data += line
-                else:
+                elif line:
                     self.logger.log(VERBOSE_LOG_LEVEL, line)
+                del line
 
             # if we reach this point, the process is finished (finish or aborted)
             if ffmpeg_proc.returncode == 0:
@@ -1073,6 +1073,8 @@ class StreamsController(CoreController):
                 )
                 if music_prov := self.mass.get_provider(streamdetails.provider):
                     self.mass.create_task(music_prov.on_streamed(streamdetails, seconds_streamed))
+            # cleanup
+            del stderr_data
 
         async with AsyncProcess(
             ffmpeg_args,
@@ -1119,8 +1121,9 @@ class StreamsController(CoreController):
                 # collect this chunk for next round
                 prev_chunk = chunk
             # if we did not receive any data, something went (terribly) wrong
-            # raise here to prevent an endless loop elsewhere
+            # raise here to prevent an (endless) loop elsewhere
             if state_data["bytes_sent"] == 0:
+                del prev_chunk
                 raise AudioError(f"stream error on {streamdetails.uri}")
 
             # all chunks received, strip silence of last part if needed and yield remaining bytes
@@ -1135,11 +1138,7 @@ class StreamsController(CoreController):
             else:
                 final_chunk = prev_chunk
 
-            # yield final chunk to output (in chunk_size parts)
-            while len(final_chunk) > chunk_size:
-                yield final_chunk[:chunk_size]
-                final_chunk = final_chunk[chunk_size:]
-                state_data["bytes_sent"] += len(final_chunk)
+            # yield final chunk to output (as one big chunk)
             yield final_chunk
             state_data["bytes_sent"] += len(final_chunk)
             state_data["finished"].set()
index 080fbe99e6b5c0be99cef52e367b26a759c95295..4f2bf61ee4c5f52be235cb75a6cd2c09f4bfde26 100644 (file)
@@ -109,16 +109,16 @@ class AsyncProcess:
         """Yield chunks of n size from the process stdout."""
         while self.returncode is None:
             chunk = await self.readexactly(n)
-            if len(chunk) == 0:
-                break
+            if chunk == b"":
+                raise StopAsyncIteration
             yield chunk
 
     async def iter_any(self, n: int = DEFAULT_CHUNKSIZE) -> AsyncGenerator[bytes, None]:
         """Yield chunks as they come in from process stdout."""
         while self.returncode is None:
             chunk = await self.read(n)
-            if len(chunk) == 0:
-                break
+            if chunk == b"":
+                raise StopAsyncIteration
             yield chunk
 
     async def readexactly(self, n: int) -> bytes:
@@ -226,12 +226,11 @@ class AsyncProcess:
     async def iter_stderr(self) -> AsyncGenerator[bytes, None]:
         """Iterate lines from the stderr stream."""
         while self.returncode is None:
-            if self.proc.stderr.at_eof():
-                break
             try:
-                yield await self.proc.stderr.readline()
-                if self.proc.stderr.at_eof():
-                    break
+                line = await self.proc.stderr.readline()
+                if line == b"":
+                    raise StopAsyncIteration
+                yield line
             except ValueError as err:
                 # we're waiting for a line (separator found), but the line was too big
                 # this may happen with ffmpeg during a long (radio) stream where progress