5252from .audio_buffer import AudioBuffer
5353from .datetime import utc
5454from .dsp import filter_to_ffmpeg_params
55- from .ffmpeg import FFMpeg , get_ffmpeg_stream
55+ from .ffmpeg import FFMpeg , get_ffmpeg_args , get_ffmpeg_stream
5656from .playlists import IsHLSPlaylist , PlaylistItem , fetch_playlist , parse_m3u
5757from .process import AsyncProcess , communicate
5858from .util import detect_charset
@@ -421,7 +421,7 @@ async def get_stream_details(
421421 return streamdetails
422422
423423
424- async def get_media_stream_with_buffer (
424+ async def get_buffered_media_stream (
425425 mass : MusicAssistant ,
426426 streamdetails : StreamDetails ,
427427 pcm_format : AudioFormat ,
@@ -436,8 +436,8 @@ async def get_media_stream_with_buffer(
436436 seek_position ,
437437 )
438438
439- # checksum based on pcm_format and filter_params
440- checksum = f"{ pcm_format } - { filter_params } "
439+ # checksum based on filter_params
440+ checksum = f"{ filter_params } "
441441
442442 async def fill_buffer_task () -> None :
443443 """Background task to fill the audio buffer."""
@@ -528,6 +528,25 @@ async def fill_buffer_task() -> None:
528528 task = mass .loop .create_task (fill_buffer_task ())
529529 audio_buffer .attach_fill_task (task )
530530
531+ # special case: pcm format mismatch, resample on the fly
532+ # this may happen in some special situations such as crossfading
533+ # and its a bit of a waste to throw away the existing buffer
534+ if audio_buffer .pcm_format != pcm_format :
535+ LOGGER .info (
536+ "buffered_media_stream: pcm format mismatch, resampling on the fly for %s - "
537+ "buffer format: %s - requested format: %s" ,
538+ streamdetails .uri ,
539+ audio_buffer .pcm_format ,
540+ pcm_format ,
541+ )
542+ async for chunk in get_ffmpeg_stream (
543+ audio_input = audio_buffer .iter (seek_position = seek_position ),
544+ input_format = audio_buffer .pcm_format ,
545+ output_format = pcm_format ,
546+ ):
547+ yield chunk
548+ return
549+
531550 # yield data from the buffer
532551 chunk_count = 0
533552 try :
@@ -631,7 +650,7 @@ async def get_media_stream(
631650 first_chunk_received = True
632651 streamdetails .audio_format .codec_type = ffmpeg_proc .input_format .codec_type
633652 logger .debug (
634- "First chunk received after %s seconds (codec detected: %s)" ,
653+ "First chunk received after %.2f seconds (codec detected: %s)" ,
635654 mass .loop .time () - stream_start ,
636655 ffmpeg_proc .input_format .codec_type ,
637656 )
@@ -1209,23 +1228,19 @@ async def get_silence(
12091228
12101229
12111230async def resample_pcm_audio (
1212- input_audio : bytes | AsyncGenerator [ bytes , None ] ,
1231+ input_audio : bytes ,
12131232 input_format : AudioFormat ,
12141233 output_format : AudioFormat ,
1215- ) -> AsyncGenerator [ bytes , None ] :
1234+ ) -> bytes :
12161235 """Resample (a chunk of) PCM audio from input_format to output_format using ffmpeg."""
1217- LOGGER .debug (f"Resampling audio from { input_format } to { output_format } " )
1218-
1219- async def _yielder () -> AsyncGenerator [bytes , None ]:
1220- yield input_audio # type: ignore[misc]
1221-
1222- async for chunk in get_ffmpeg_stream (
1223- audio_input = _yielder () if isinstance (input_audio , bytes ) else input_audio ,
1224- input_format = input_format ,
1225- output_format = output_format ,
1226- raise_ffmpeg_exception = True ,
1227- ):
1228- yield chunk
1236+ if input_format == output_format :
1237+ return input_audio
1238+ LOGGER .log (VERBOSE_LOG_LEVEL , f"Resampling audio from { input_format } to { output_format } " )
1239+ ffmpeg_args = get_ffmpeg_args (
1240+ input_format = input_format , output_format = output_format , filter_params = []
1241+ )
1242+ _ , stdout , _ = await communicate (ffmpeg_args , input_audio )
1243+ return stdout
12291244
12301245
12311246def get_chunksize (
0 commit comments