diff --git a/ffmpeg_test.go b/ffmpeg_test.go index 4b1fb39..749bcae 100644 --- a/ffmpeg_test.go +++ b/ffmpeg_test.go @@ -277,6 +277,13 @@ func TestCompile(t *testing.T) { assert.Equal(t, out.Compile().Args, []string{"ffmpeg", "-i", "dummy.mp4", "dummy2.mp4"}) } +func TestCompileWithOptions(t *testing.T) { + out := Input("dummy.mp4").Output("dummy2.mp4") + cmd := out.Compile(SeparateProcessGroup()) + assert.Equal(t, cmd.SysProcAttr.Pgid, 0) + assert.True(t, cmd.SysProcAttr.Setpgid) +} + func TestPipe(t *testing.T) { width, height := 32, 32 diff --git a/run.go b/run.go index f657d67..6d386bb 100644 --- a/run.go +++ b/run.go @@ -9,6 +9,7 @@ import ( "os/exec" "sort" "strings" + "syscall" "time" ) @@ -91,7 +92,7 @@ func _allocateFilterStreamNames(nodes []*Node, outOutingEdgeMaps map[int]map[Lab // todo sort for _, l := range _getAllLabelsSorted(om) { if len(om[l]) > 1 { - panic(fmt.Sprintf(`encountered %s with multiple outgoing edges + panic(fmt.Sprintf(`encountered %s with multiple outgoing edges with same upstream label %s; a 'split'' filter is probably required`, n.name, l)) } streamNameMap[fmt.Sprintf("%d%s", n.Hash(), l)] = fmt.Sprintf("s%d", sc) @@ -236,8 +237,19 @@ func (s *Stream) ErrorToStdOut() *Stream { return s.WithErrorOutput(os.Stdout) } +type CompilationOption func(s *Stream, cmd *exec.Cmd) + +// SeparateProcessGroup ensures that the command is run in a separate process +// group. This is useful to enable handling of signals such as SIGINT without +// propagating them to the ffmpeg process. +func SeparateProcessGroup() CompilationOption { + return func(s *Stream, cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0} + } +} + // for test -func (s *Stream) Compile() *exec.Cmd { +func (s *Stream) Compile(options ...CompilationOption) *exec.Cmd { args := s.GetArgs() cmd := exec.CommandContext(s.Context, "ffmpeg", args...) if a, ok := s.Context.Value("Stdin").(io.Reader); ok { @@ -249,11 +261,14 @@ func (s *Stream) Compile() *exec.Cmd { if a, ok := s.Context.Value("Stderr").(io.Writer); ok { cmd.Stderr = a } + for _, option := range options { + option(s, cmd) + } log.Printf("compiled command: ffmpeg %s\n", strings.Join(args, " ")) return cmd } -func (s *Stream) Run() error { +func (s *Stream) Run(options ...CompilationOption) error { if s.Context.Value("run_hook") != nil { hook := s.Context.Value("run_hook").(*RunHook) go hook.f() @@ -264,5 +279,5 @@ func (s *Stream) Run() error { <-hook.done }() } - return s.Compile().Run() + return s.Compile(options...).Run() }