100 lines
1.8 KiB
Go
100 lines
1.8 KiB
Go
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
|
|
fb chan bool
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
RETRY:
|
|
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",
|
|
"-xerror",
|
|
"-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
|
|
|
|
exitctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
go func() {
|
|
ff.Cmd.Start()
|
|
ff.err <- ff.Cmd.Wait()
|
|
}()
|
|
|
|
select {
|
|
case <-exitctx.Done():
|
|
ff.Started = true
|
|
case <-ff.err:
|
|
log.Println("ffmpeg exited early, retry...")
|
|
goto RETRY
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (ff *FFmpeg) Close() error {
|
|
ff.Cancel()
|
|
return nil
|
|
}
|