Only allow ICY metadata on mp3 streamtype (#410)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Wed, 13 Jul 2022 21:37:48 +0000 (23:37 +0200)
committerGitHub <noreply@github.com>
Wed, 13 Jul 2022 21:37:48 +0000 (23:37 +0200)
* Only allow ICY metadata on mp3 streamtype

- Use default metaint of 8192 bytes

music_assistant/controllers/streams.py

index 90db29bc7ff3adb0d465e203a2b478a236018962..98073956b1dae6f0f019c40dcc629d72176a6a51 100644 (file)
@@ -37,6 +37,8 @@ from music_assistant.models.queue_item import QueueItem
 if TYPE_CHECKING:
     from music_assistant.mass import MusicAssistant
 
+ICY_CHUNKSIZE = 8192
+
 
 class StreamsController:
     """Controller to stream audio to players."""
@@ -186,13 +188,24 @@ class StreamsController:
         headers = {
             "Content-Type": f"audio/{queue_stream.output_format.value}",
             "transferMode.dlna.org": "Streaming",
-            "icy-name": "Streaming from Music Assistant",
-            "icy-pub": "0",
             "Cache-Control": "no-cache",
-            "icy-metaint": str(queue_stream.output_chunk_size),
-            "contentFeatures.dlna.org": "DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000",
         }
 
+        # for now, only support icy metadata on MP3 streams to prevent issues
+        # https://github.com/music-assistant/hass-music-assistant/issues/603
+        # in the future we could expand this support:
+        # by making exceptions for players that do also support ICY on other content types
+        # and/or metaint value such as Kodi.
+        # another future expansion is to just get the PCM frames here and encode
+        # for each inidvidual player with or without ICY...
+        if queue_stream.output_format == ContentType.MP3:
+            # use the default/recommended metaint size of 8192
+            # https://cast.readme.io/docs/icy
+            headers["icy-name"] = "Music Assistant"
+            headers["icy-pub"] = "1"
+            # use the default/recommended metaint size of 8192
+            headers["icy-metaint"] = str(ICY_CHUNKSIZE)
+
         resp = web.StreamResponse(headers=headers)
         try:
             await resp.prepare(request)
@@ -350,12 +363,6 @@ class QueueStream:
         self.signal_next: bool = False
         self._runner_task: Optional[asyncio.Task] = None
         self._prev_chunk: bytes = b""
-        self.output_chunk_size = get_chunksize(
-            output_format,
-            pcm_sample_rate,
-            pcm_bit_depth,
-            pcm_channels,
-        )
         if autostart:
             self.mass.create_task(self.start())
 
@@ -465,7 +472,21 @@ class QueueStream:
 
             # Read bytes from final output and send chunk to child callback.
             chunk_num = 0
-            async for chunk in ffmpeg_proc.iter_chunked(self.output_chunk_size):
+            if self.output_format == ContentType.MP3:
+                # use the icy compatible static chunksize (iter_chunks of x size)
+                get_chunks = ffmpeg_proc.iter_chunked(ICY_CHUNKSIZE)
+            else:
+                # all other: prefer chunksize that fits 1 second belonging to output type
+                # but accept less (iter any chunk of max chunk size)
+                get_chunks = ffmpeg_proc.iter_any(
+                    get_chunksize(
+                        self.output_format,
+                        self.pcm_sample_rate,
+                        self.pcm_bit_depth,
+                        self.pcm_channels,
+                    )
+                )
+            async for chunk in get_chunks:
                 chunk_num += 1
 
                 if len(self.connected_clients) == 0: