Update torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py

This commit is contained in:
Metroid_Skittles 2026-02-07 07:49:36 +01:00
parent ff3dff14a6
commit 6715d2a07c

View File

@ -60,6 +60,9 @@ class FFmpegAudioPlayer():
def PlayURI(self, uri, position, rubberband = None, dec_params = None, bitrate = None, def PlayURI(self, uri, position, rubberband = None, dec_params = None, bitrate = None,
backwards = None, *args): backwards = None, *args):
if self.Playing:
self.Stop()
if position: if position:
PosStr = str(datetime.timedelta(seconds = position)) PosStr = str(datetime.timedelta(seconds = position))
#Command = ["/usr/bin/ffmpeg", "-ss", PosStr, "-i", uri, "-acodec", "pcm_s16le", "-ac", "1", "-ar", str(int(self.SampleRate)), "-f", "s16le", "-vn", *args] #Command = ["/usr/bin/ffmpeg", "-ss", PosStr, "-i", uri, "-acodec", "pcm_s16le", "-ac", "1", "-ar", str(int(self.SampleRate)), "-f", "s16le", "-vn", *args]
@ -71,7 +74,13 @@ class FFmpegAudioPlayer():
if args: if args:
Command += list(args) Command += list(args)
# Reset per-play counters/state in case this instance is reused.
self.Seconds = 0.0
self.StartedPlaying = None
self.StoppedPlaying = None
self._byte_remainder = 0
self.Playing = True self.Playing = True
if dec_params: if dec_params:
Command += dec_params Command += dec_params
@ -172,7 +181,7 @@ class FFmpegAudioPlayer():
if SecondsElapsed >= self.Seconds: if SecondsElapsed >= self.Seconds:
if not self.StoppedPlaying: if not self.StoppedPlaying:
print("BUFFER UNDERRUN!") self.Master.Logger.warning("BUFFER UNDERRUN")
self.Stop(False) self.Stop(False)
return return
@ -180,55 +189,65 @@ class FFmpegAudioPlayer():
async def _read_stream(self, stream, writer): async def _read_stream(self, stream, writer):
Started = False Started = False
try:
while stream and self.Playing:
Data = await stream.read(65536)
while stream and self.Playing: if Data:
Data = await stream.read(65536) try:
writer.write(Data)
await writer.drain()
except (ConnectionError, BrokenPipeError, OSError) as ex:
self.Master.Logger.warning("Voice socket write failed: %s", ex)
break
if Data: Bytes = len(Data)
writer.write(Data) frame_size = SAMPLEBYTES * self.Channels
await writer.drain()
Bytes = len(Data) # Guard against invalid configuration and use integral frame counting
frame_size = SAMPLEBYTES * self.Channels if frame_size <= 0 or self.SampleRate <= 0:
self.Master.Logger.error(
"Invalid audio configuration: Channels=%r, SampleRate=%r",
self.Channels,
self.SampleRate,
)
else:
# Accumulate remainder bytes to avoid systematic undercounting
total_bytes = getattr(self, "_byte_remainder", 0) + Bytes
Frames = total_bytes // frame_size
self._byte_remainder = total_bytes % frame_size
Seconds = Frames / self.SampleRate
self.Seconds += Seconds
# Guard against invalid configuration and use integral frame counting if not Started:
if frame_size <= 0 or self.SampleRate <= 0: Started = True
self.Master.Logger.error( self.Callback("Play")
"Invalid audio configuration: Channels=%r, SampleRate=%r", self.StartedPlaying = time.time()
self.Channels, asyncio.ensure_future(self._updater())
self.SampleRate,
)
else: else:
# Accumulate remainder bytes to avoid systematic undercounting self.Process = None
total_bytes = getattr(self, "_byte_remainder", 0) + Bytes break
Frames = total_bytes // frame_size finally:
self._byte_remainder = total_bytes % frame_size self.StoppedPlaying = time.time()
Seconds = Frames / self.SampleRate
self.Seconds += Seconds
if not Started:
Started = True
self.Callback("Play")
self.StartedPlaying = time.time()
asyncio.ensure_future(self._updater())
else:
self.Process = None
break
self.StoppedPlaying = time.time()
async def _stream_subprocess(self, cmd): async def _stream_subprocess(self, cmd):
if not self.Playing: if not self.Playing:
return return
try:
_, self.Writer = await asyncio.open_connection(self.Host[0], self.Host[1])
_, self.Writer = await asyncio.open_connection(self.Host[0], self.Host[1]) Process = await asyncio.create_subprocess_exec(*cmd,
stdout = asyncio.subprocess.PIPE, stderr = asyncio.subprocess.DEVNULL)
self.Process = Process
Process = await asyncio.create_subprocess_exec(*cmd, await self._read_stream(Process.stdout, self.Writer)
stdout = asyncio.subprocess.PIPE, stderr = asyncio.subprocess.DEVNULL) await Process.wait()
self.Process = Process
await self._read_stream(Process.stdout, self.Writer) if self.Playing:
await Process.wait() self.Stop(False)
except asyncio.CancelledError:
if self.Seconds == 0.0: raise
self.Stop() except Exception:
self.Master.Logger.error(traceback.format_exc())
if self.Playing:
self.Stop()