From 0e1de4dbc382d874a41d8b6724791b635ea1542e Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 13 Jul 2022 23:37:48 +0200 Subject: [PATCH] Only allow ICY metadata on mp3 streamtype (#410) * Only allow ICY metadata on mp3 streamtype - Use default metaint of 8192 bytes --- music_assistant/controllers/streams.py | 43 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/music_assistant/controllers/streams.py b/music_assistant/controllers/streams.py index 90db29bc..98073956 100644 --- a/music_assistant/controllers/streams.py +++ b/music_assistant/controllers/streams.py @@ -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: -- 2.34.1