golang通过CMD来调用FFmpeg


概述

在golang中调用FFmpeg,一种方法是使用cgo来使用FFmpeg,这样就可以在go程序里面编写转码语句,进行错误处理等, 但是这样的方法貌似有点复杂,或许得不偿失。

这里使用CMD来直接调用FFmpeg进行转码,在使用较多的复杂参数的情况下,可能反而简单许多。


使用记录

调用:

1
2
3
cmd := exec.Command("/bin/bash", "-c", param)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.start()

这里的param就是整个FFmpeg转码语句,例如/usr/bin/ffmpeg -i 1.mp4 1.flv

这里使用Setpgid来设置进程组pid,这样在后面kill时直接杀掉整个进程组,无残留。

另外cmd.start()表示非阻塞,转码开始后就放在后台(因为直播相关,所以会一直转码),等待结束命令。

转码日志记录:

1
2
stdout, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY, 0644)
cmd.Stderr = stdout

注意ffmpeg使用的日志输出是Stderrerr

停止:

1
err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)

加上一个负号,直接杀掉整个进程组。

PS:一定要在kill之后调用cmd.Wait(),否则会变成僵尸进程。


完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func Exec(param, logFile string) (*exec.Cmd, error) {
cmd := exec.Command("/bin/bash", "-c", param)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
logOut, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
// 将标准输出和标准错误都写到log中
cmd.Stdout = logOut
cmd.Stderr = logOut
log.Info("Exce cmd: %s", param)
if err := cmd.Start(); err != nil {
_ = logOut.Close()
return nil, err
}
return cmd, nil
}
func KillCmd(cmd *exec.Cmd) error {
if cmd == nil || cmd.Process == nil {
return errors.New("process not found or already stopped")
}
log.Info("Kill CMD. pid: %d", cmd.Process.Pid)
err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
if err != nil {
return err
}
// 如果不wait,则会产生僵尸进程
go cmd.Wait() // 为了不阻塞,另外起一个线程去Wait
return nil
}

2019-12-30日修改记录

实际使用的时候发现出现了defunct进程,也就是僵尸进程,重新看了一下cmd.Start()的使用方法, 发现自己没有调用cmd.Wait()


参考

Go语言中Kill子进程的正确姿势

---------------------------------END---------------------------------