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:
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