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{}
|
stopChan chan struct{}
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
cmd *exec.Cmd
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
playing bool
|
playing bool
|
||||||
@@ -99,6 +100,7 @@ func (p *Player) Stop() error {
|
|||||||
if p.cancel != nil {
|
if p.cancel != nil {
|
||||||
p.cancel()
|
p.cancel()
|
||||||
}
|
}
|
||||||
|
terminateProcessGroup(p.cmd)
|
||||||
}
|
}
|
||||||
p.mutex.Unlock()
|
p.mutex.Unlock()
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ func (p *Player) Stop() error {
|
|||||||
// worker have exited, otherwise old audio can enter the new playback.
|
// worker have exited, otherwise old audio can enter the new playback.
|
||||||
p.wg.Wait()
|
p.wg.Wait()
|
||||||
p.mutex.Lock()
|
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
|
localPlayback := p.localPlayback
|
||||||
p.mutex.Unlock()
|
p.mutex.Unlock()
|
||||||
if localPlayback != nil {
|
if localPlayback != nil {
|
||||||
@@ -159,6 +161,7 @@ func (p *Player) readFileAudio() {
|
|||||||
ctx := p.ctx
|
ctx := p.ctx
|
||||||
p.mutex.Unlock()
|
p.mutex.Unlock()
|
||||||
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
|
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
|
||||||
|
configureProcessGroup(cmd)
|
||||||
pipe, err := cmd.StdoutPipe()
|
pipe, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
@@ -175,6 +178,9 @@ func (p *Player) readFileAudio() {
|
|||||||
p.reportError(errors.New("failed to start ffmpeg: " + err.Error()))
|
p.reportError(errors.New("failed to start ffmpeg: " + err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
p.mutex.Lock()
|
||||||
|
p.cmd = cmd
|
||||||
|
p.mutex.Unlock()
|
||||||
|
|
||||||
// Stereo has 2 channels, so we need twice the buffer size
|
// Stereo has 2 channels, so we need twice the buffer size
|
||||||
byteBuffer := make([]byte, frameSize*2*2) // frameSize * 2 channels * 2 bytes per sample
|
byteBuffer := make([]byte, frameSize*2*2) // frameSize * 2 channels * 2 bytes per sample
|
||||||
@@ -185,7 +191,7 @@ func (p *Player) readFileAudio() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.stopChan:
|
case <-p.stopChan:
|
||||||
cmd.Process.Kill()
|
terminateProcessGroup(cmd)
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
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