From f6c4eef2f10fd63ba57a835a7ace75c47d426fd7 Mon Sep 17 00:00:00 2001 From: Hiroki KUMAZAKI Date: Sat, 3 Oct 2026 06:19:14 +0900 Subject: [PATCH] internal/mux: preserve buffered audio on an unsupported seek --- internal/mux/mux.go | 11 ++++++----- internal/mux/mux_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/internal/mux/mux.go b/internal/mux/mux.go index 9d8675a..23a9227 100644 --- a/internal/mux/mux.go +++ b/internal/mux/mux.go @@ -361,6 +361,12 @@ func (p *Player) Seek(offset int64, whence int) (int64, error) { } func (p *playerImpl) Seek(offset int64, whence int) (int64, error) { + // Check if the source implements io.Seeker. + s, ok := p.src.(io.Seeker) + if !ok { + return 0, errors.New("mux: the source must implement io.Seeker") + } + defer p.mux.signal() p.m.Lock() @@ -387,11 +393,6 @@ func (p *playerImpl) Seek(offset int64, whence int) (int64, error) { p.reportSourceErrorIfDrainedImpl() } - // Check if the source implements io.Seeker. - s, ok := p.src.(io.Seeker) - if !ok { - return 0, errors.New("mux: the source must implement io.Seeker") - } return s.Seek(offset, whence) } diff --git a/internal/mux/mux_test.go b/internal/mux/mux_test.go index 44e9e8c..9f3c097 100644 --- a/internal/mux/mux_test.go +++ b/internal/mux/mux_test.go @@ -1003,3 +1003,35 @@ func TestPartialSampleAtEOFStopsPlayer(t *testing.T) { t.Errorf("IsRegistered after EOF: got %v; want false", got) } } + +func TestSeekNonSeekerPreservesBuffer(t *testing.T) { + for _, playing := range []bool{false, true} { + t.Run(fmt.Sprintf("playing=%t", playing), func(t *testing.T) { + src := []byte{0, 64, 128, 192, 255} + m := mux.New(48000, 1, mux.FormatUnsignedInt8) + p := newPlayer(t, m, bytes.NewBuffer(src)) + p.Play() + waitForBufferedSize(t, p, len(src)) + if !playing { + p.PauseAndStopReading() + } + if _, err := p.Seek(0, io.SeekStart); err == nil { + t.Fatal("Seek on a non-seekable source succeeded") + } + if got := p.BufferedSize(); got != len(src) { + t.Fatalf("BufferedSize after failed Seek: got %d; want %d", got, len(src)) + } + if got := p.IsPlaying(); got != playing { + t.Fatalf("IsPlaying: got %t; want %t", got, playing) + } + p.Play() + buf := make([]float32, len(src)) + m.ReadFloat32s(buf) + for i, got := range buf { + if want := float32(src[i])/128 - 1; got != want { + t.Errorf("sample %d: got %v; want %v", i, got, want) + } + } + }) + } +}