nativeaudio

audio playback for Go
Log | Files | Refs | README | LICENSE

commit 1c44b1ad59b8a59f377a2d7397b62bde4269dc80
parent 7a22adb29e362e75cfb59bf41eb933daa9df1b11
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Mon,  9 May 2022 17:31:06 +0800

audio_ffmpeg: threadsafe decoding

Uniquely identify each tmp file so that multiple threads
trying to decode don't race.

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
Maudio_ffmpeg.go | 35+++++++++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/audio_ffmpeg.go b/audio_ffmpeg.go @@ -12,8 +12,10 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "strconv" "strings" + "sync" ) // FFmpegPlay an audio file with ffplay. @@ -48,6 +50,7 @@ func FFmpegLoad(path string) ([]byte, Format, error) { return nil, f, fmt.Errorf("probing file for metadata: %w", err) } buffer := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) cmd := exec.Command( "ffmpeg", "-i", path, @@ -55,12 +58,33 @@ func FFmpegLoad(path string) ([]byte, Format, error) { "-", ) cmd.Stdout = buffer + cmd.Stderr = stderr if err := cmd.Run(); err != nil { - return nil, Format{}, fmt.Errorf("ffmpeg: %w", err) + return nil, Format{}, fmt.Errorf("ffmpeg: %w: %s", err, stderr.String()) } return buffer.Bytes(), f, nil } +var count = counter{} + +type counter struct { + count int + m sync.Mutex +} + +func (c *counter) Next() int { + c.m.Lock() + defer c.m.Unlock() + c.count++ + return c.count +} + +func (c *counter) Done() { + c.m.Lock() + defer c.m.Unlock() + c.count-- +} + // FFmpegDecode raw PCM with ffmpeg. // // ffmpeg -f m4a -i pipe: -f s16le - @@ -71,11 +95,14 @@ func FFmpegLoad(path string) ([]byte, Format, error) { // NOTE(jfm): unfortunately, some formats cannot be piped, so we will // create a temporary file instead. func FFmpegDecode(by []byte) ([]byte, Format, error) { - if err := os.WriteFile("tmp", by, 0644); err != nil { + // id ensures that multiple concurrent tmp files do not collide. + id := count.Next() + tmp := filepath.Join(os.TempDir(), fmt.Sprintf("nativeaudio-%d", id)) + if err := os.WriteFile(tmp, by, 0644); err != nil { return nil, Format{}, fmt.Errorf("creating tmp file: %w", err) } - defer os.Remove("tmp") - return FFmpegLoad("tmp") + defer os.Remove(tmp) + return FFmpegLoad(tmp) } // probe queries the format information for a given audio file by parsing