dndmusicbot/ffmpeg/ffmpeg.go

100 lines
1.8 KiB
Go
Raw Normal View History

2022-11-18 12:29:39 +00:00
package ffmpeg
import (
"bytes"
"context"
"log"
"os"
"os/exec"
"strconv"
"sync"
"time"
"dndmusicbot/ytdl"
)
type FFmpeg struct {
Out *bytes.Buffer
Cmd *exec.Cmd
Started bool
Cancel context.CancelFunc
Len time.Duration
Title string
Channel string
PMutex *sync.RWMutex
ProcessState *os.ProcessState
err chan error
2022-11-18 21:05:41 +00:00
fb chan bool
2022-11-18 12:29:39 +00:00
}
func NewFFmpeg(uri string, sampleRate int, channels int) (ff *FFmpeg, err error) {
var yt *ytdl.YTdl
for {
yt, err = ytdl.NewYTdl(uri)
if err != nil {
log.Println("Something went wrong, trying again.", err)
continue
}
if yt.Url != "" {
break
}
log.Println("Something went wrong, trying again.")
time.Sleep(500 * time.Millisecond)
}
2022-11-18 21:05:41 +00:00
RETRY:
2022-11-18 12:29:39 +00:00
ff = new(FFmpeg)
ff.PMutex = &sync.RWMutex{}
ff.Len = yt.Len
ff.Title = yt.Title
ff.Channel = yt.Channel
ctx, cancel := context.WithCancel(context.Background())
ff.Cancel = cancel
ff.Cmd = exec.CommandContext(
ctx,
"ffmpeg",
2022-11-18 21:05:41 +00:00
"-xerror",
2022-11-18 12:29:39 +00:00
"-i", yt.Url,
"-f", "s16le",
"-v", "error",
// "-stats",
//"-progress", "pipe:2",
"-ar", strconv.Itoa(sampleRate),
"-ac", strconv.Itoa(channels),
"-af", "loudnorm=I=-16:LRA=11:TP=-1.5",
"pipe:1",
)
ff.Cmd.Stderr = os.Stdin
// FFmpeg requires a certain buffer size to start writing. This seems to be enough?
// This will grow big enough to fit the whole song.
ff.Out = bytes.NewBuffer(make([]byte, 43920))
ff.Cmd.Stdout = ff.Out
2022-11-18 21:05:41 +00:00
exitctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
2022-11-18 12:29:39 +00:00
go func() {
ff.Cmd.Start()
ff.err <- ff.Cmd.Wait()
}()
2022-11-18 21:05:41 +00:00
select {
case <-exitctx.Done():
ff.Started = true
case <-ff.err:
log.Println("ffmpeg exited early, retry...")
goto RETRY
}
return
2022-11-18 12:29:39 +00:00
}
func (ff *FFmpeg) Close() error {
ff.Cancel()
return nil
}