if TYPE_CHECKING:
from music_assistant.mass import MusicAssistant
+ICY_CHUNKSIZE = 8192
+
class StreamsController:
"""Controller to stream audio to players."""
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)
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())
# 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: