Terminate file playback process groups
This commit is contained in:
committed by
Brandon McGinty
parent
1d6ab79ed0
commit
cf9e1c6d0a
@@ -21,6 +21,7 @@ type Player struct {
|
||||
stopChan chan struct{}
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
cmd *exec.Cmd
|
||||
mutex sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
playing bool
|
||||
@@ -99,6 +100,7 @@ func (p *Player) Stop() error {
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
terminateProcessGroup(p.cmd)
|
||||
}
|
||||
p.mutex.Unlock()
|
||||
|
||||
@@ -106,7 +108,7 @@ func (p *Player) Stop() error {
|
||||
// worker have exited, otherwise old audio can enter the new playback.
|
||||
p.wg.Wait()
|
||||
p.mutex.Lock()
|
||||
p.playing, p.stopping, p.cancel = false, false, nil
|
||||
p.playing, p.stopping, p.cancel, p.cmd = false, false, nil, nil
|
||||
localPlayback := p.localPlayback
|
||||
p.mutex.Unlock()
|
||||
if localPlayback != nil {
|
||||
@@ -159,6 +161,7 @@ func (p *Player) readFileAudio() {
|
||||
ctx := p.ctx
|
||||
p.mutex.Unlock()
|
||||
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
|
||||
configureProcessGroup(cmd)
|
||||
pipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
p.mutex.Lock()
|
||||
@@ -175,6 +178,9 @@ func (p *Player) readFileAudio() {
|
||||
p.reportError(errors.New("failed to start ffmpeg: " + err.Error()))
|
||||
return
|
||||
}
|
||||
p.mutex.Lock()
|
||||
p.cmd = cmd
|
||||
p.mutex.Unlock()
|
||||
|
||||
// Stereo has 2 channels, so we need twice the buffer size
|
||||
byteBuffer := make([]byte, frameSize*2*2) // frameSize * 2 channels * 2 bytes per sample
|
||||
@@ -185,7 +191,7 @@ func (p *Player) readFileAudio() {
|
||||
for {
|
||||
select {
|
||||
case <-p.stopChan:
|
||||
cmd.Process.Kill()
|
||||
terminateProcessGroup(cmd)
|
||||
cmd.Wait()
|
||||
return
|
||||
case <-ticker.C:
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build !(aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris)
|
||||
|
||||
package fileplayback
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func configureProcessGroup(cmd *exec.Cmd) {}
|
||||
|
||||
func terminateProcessGroup(cmd *exec.Cmd) {
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
_ = cmd.Process.Kill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
|
||||
package fileplayback
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func configureProcessGroup(cmd *exec.Cmd) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
}
|
||||
|
||||
func terminateProcessGroup(cmd *exec.Cmd) {
|
||||
if cmd == nil || cmd.Process == nil {
|
||||
return
|
||||
}
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
|
||||
package fileplayback
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigureProcessGroupCreatesSeparateGroup(t *testing.T) {
|
||||
cmd := exec.Command("true")
|
||||
configureProcessGroup(cmd)
|
||||
if cmd.SysProcAttr == nil || !cmd.SysProcAttr.Setpgid {
|
||||
t.Fatal("ffmpeg process was not configured to lead its own process group")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user