install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// Command like exec.Cmd, but with support:
|
||||
// - io.Closer interface
|
||||
// - Wait from multiple places
|
||||
// - Done channel
|
||||
type Command struct {
|
||||
*exec.Cmd
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
err error
|
||||
}
|
||||
|
||||
func NewCommand(s string) *Command {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
args := QuoteSplit(s)
|
||||
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
|
||||
cmd.SysProcAttr = procAttr
|
||||
return &Command{cmd, ctx, cancel, nil}
|
||||
}
|
||||
|
||||
func (c *Command) Start() error {
|
||||
if err := c.Cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
c.err = c.Cmd.Wait()
|
||||
c.cancel() // release context resources
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) Wait() error {
|
||||
<-c.ctx.Done()
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *Command) Run() error {
|
||||
if err := c.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Wait()
|
||||
}
|
||||
|
||||
func (c *Command) Done() <-chan struct{} {
|
||||
return c.ctx.Done()
|
||||
}
|
||||
|
||||
func (c *Command) Close() error {
|
||||
c.cancel()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build !linux
|
||||
|
||||
package shell
|
||||
|
||||
import "syscall"
|
||||
|
||||
var procAttr *syscall.SysProcAttr
|
||||
@@ -0,0 +1,6 @@
|
||||
package shell
|
||||
|
||||
import "syscall"
|
||||
|
||||
// will stop child if parent died (even with SIGKILL)
|
||||
var procAttr = &syscall.SysProcAttr{Pdeathsig: syscall.SIGTERM}
|
||||
@@ -0,0 +1,43 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func QuoteSplit(s string) []string {
|
||||
var a []string
|
||||
|
||||
for len(s) > 0 {
|
||||
switch c := s[0]; c {
|
||||
case '\t', '\n', '\r', ' ': // unicode.IsSpace
|
||||
s = s[1:]
|
||||
case '"', '\'': // quote chars
|
||||
if i := strings.IndexByte(s[1:], c); i > 0 {
|
||||
a = append(a, s[1:i+1])
|
||||
s = s[i+2:]
|
||||
} else {
|
||||
return nil // error
|
||||
}
|
||||
default:
|
||||
i := strings.IndexAny(s, "\t\n\r ")
|
||||
if i > 0 {
|
||||
a = append(a, s[:i])
|
||||
s = s[i:]
|
||||
} else {
|
||||
a = append(a, s)
|
||||
s = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func RunUntilSignal() {
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
println("exit with signal:", (<-sigs).String())
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestQuoteSplit(t *testing.T) {
|
||||
s := `
|
||||
python "-c" 'import time
|
||||
print("time", time.time())'
|
||||
`
|
||||
require.Equal(t, []string{"python", "-c", "import time\nprint(\"time\", time.time())"}, QuoteSplit(s))
|
||||
|
||||
s = `ffmpeg -i "video=FaceTime HD Camera" -i "DeckLink SDI (2)"`
|
||||
require.Equal(t, []string{"ffmpeg", "-i", `video=FaceTime HD Camera`, "-i", "DeckLink SDI (2)"}, QuoteSplit(s))
|
||||
}
|
||||
Reference in New Issue
Block a user