如何在Golang中的不同函数中终止命令执行

huangapple go评论65阅读模式
英文:

How to kill command Exec in difference Function in Golang

问题

我正在制作一个基于Web的屏幕录制程序,使用命令exec来运行FFMPEG。我创建了一个startRecording函数,但是我对于在stopRecording函数中停止命令进程感到困惑,因为命令是在startRecording函数中执行的。如何在stopRecording函数中停止已经在startRecording函数中运行的进程呢?

这是我的代码:

// 创建房间/开始录制的处理程序
func RoomCreate(c *fiber.Ctx) error {
	fileName := "out.mp4"
	fmt.Println(fileName)
	if len(os.Args) > 1 {
		fileName = os.Args[1]
	}

	errCh := make(chan error, 2)
	ctx, cancelFn := context.WithCancel(context.Background())
	// 调用startRecording函数
	go func() { errCh <- startRecording(ctx, fileName) }()

	go func() {
		errCh <- nil
	}()
	err := <-errCh
	cancelFn()
	if err != nil && err != context.Canceled {
		log.Fatalf("Execution failed: %v", err)
	}

	return c.Redirect(fmt.Sprintf("/room/%s", guuid.New().String()))
}

// 运行FFMPEG命令的函数
func startRecording(ctx context.Context, fileName string) error {
	ctx, cancelFn := context.WithCancel(ctx)
	defer cancelFn()
	// 构建ffmpeg命令
	ffmpeg := exec.Command("ffmpeg",
		"-f", "gdigrab",
		"-framerate", "30",
		"-i", "desktop",
		"-f", "mp4",
		fileName,
	)
	// 用于发送数据的标准输入
	stdin, err := ffmpeg.StdinPipe()
	if err != nil {
		return err
	}
	defer stdin.Close()
	// 在后台运行命令
	errCh := make(chan error, 1)

	go func() {
		fmt.Printf("Executing: %v\n", strings.Join(ffmpeg.Args, " "))

		if err := ffmpeg.Run(); err != nil {
			return
		}
		errCh <- err
	}()
	// 只需开始发送一堆帧
	for {
		// 检查是否完成,否则继续
		select {
		case <-ctx.Done():
			return ctx.Err()
		case err := <-errCh:
			return err
		default:
		}
	}
}

// 在这里停止录制的函数
func stopRecording(ctx context.Context) error {
	// 在这里停止录制的代码
}

提前感谢。

英文:

i'm making screen record web based using command exec to run FFMPEG. here I created a startRecording function but I am still confused about stopping the command process in the stopRecording function, because the command is executed in the startRecording function. How to stop a process that is already running in the srartRecording function in the stopRecording function?

here my code

//Handler to create room/start record
func RoomCreate(c *fiber.Ctx) error {
	fileName := &quot;out.mp4&quot;
	fmt.Println(fileName)
	if len(os.Args) &gt; 1 {
		fileName = os.Args[1]
	}

	

	errCh := make(chan error, 2)
	ctx, cancelFn := context.WithCancel(context.Background())
	// Call to function startRecording
	go func() { errCh &lt;- startRecording(ctx, fileName) }()

	go func() {
		errCh &lt;- nil
	}()
	err := &lt;-errCh
	cancelFn()
	if err != nil &amp;&amp; err != context.Canceled {
		log.Fatalf(&quot;Execution failed: %v&quot;, err)
	}
	
	return c.Redirect(fmt.Sprintf(&quot;/room/%s&quot;, guuid.New().String()))
}



//Function to run command FFMPEG
func startRecording(ctx context.Context, fileName string) error {
	ctx, cancelFn := context.WithCancel(ctx)
	defer cancelFn()
	// Build ffmpeg
	ffmpeg := exec.Command(&quot;ffmpeg&quot;,
		&quot;-f&quot;, &quot;gdigrab&quot;,
		&quot;-framerate&quot;, &quot;30&quot;,
		&quot;-i&quot;, &quot;desktop&quot;,
		&quot;-f&quot;, &quot;mp4&quot;,
		fileName,
	)
	// Stdin for sending data
	stdin, err := ffmpeg.StdinPipe()
	if err != nil {
		return err
	}
	//var buf bytes.Buffer
	defer stdin.Close()
	// Run it in the background
	errCh := make(chan error, 1)

	go func() {
		fmt.Printf(&quot;Executing: %v\n&quot;, strings.Join(ffmpeg.Args, &quot; &quot;))
		
		if err := ffmpeg.Run(); err != nil {
			return
		}
		//fmt.Printf(&quot;FFMPEG output:\n%v\n&quot;, string(out))
		errCh &lt;- err
	}()
	// Just start sending a bunch of frames
	for {
		
		// Check if we&#39;re done, otherwise go again
		select {
		case &lt;-ctx.Done():
			return ctx.Err()
		case err := &lt;-errCh:
			return err
		default:
		}
	}
}

//Here function to stop Recording
func stopRecording(ctx context.Context) error {
//Code stop recording in here
} 

Thanks for advance

答案1

得分: 1

根据评论的要求,基本思路是使用全局存储来存储活动命令。它不一定要是全局的,但你需要有更大的范围,以便你的函数可以访问它。

var commands = map[string]*exec.Cmd{}

func startRecording(fileName string) error {
    ffmpeg := exec.Command("ffmpeg",
        "-f", "gdigrab",
        "-framerate", "30",
        "-i", "desktop",
        "-f", "mp4",
        fileName,
    )
    commands[fileName] = ffmpeg
    ...
}

func stopRecording(fileName string) error {
    cmd, ok := commands[fileName]
    if !ok {
        return errors.New("command not found")
    }
    defer func() {
        delete(commands, fileName)
    }()
    return cmd.Process.Kill()
}

你可能想要使用sync.Mutexsync.RWMutex来避免并发的 map 写入

所以你的 commands 可以像这样:

type Commands struct {
    sync.RWMutex
    items map[string]*exec.Cmd
}
// 使用 Commands.Lock() 进行写入,Commands.RLock() 进行读取
英文:

As requested from comments.

The basic idea is to use global storage to store your active commands. It doesn't necessarily be global but you need to have bigger scope so that your functions can access it.

var commands = map[string]*exec.Cmd{}

func startRecording(fileName string) error {
	ffmpeg := exec.Command(&quot;ffmpeg&quot;,
		&quot;-f&quot;, &quot;gdigrab&quot;,
		&quot;-framerate&quot;, &quot;30&quot;,
		&quot;-i&quot;, &quot;desktop&quot;,
		&quot;-f&quot;, &quot;mp4&quot;,
		fileName,
	)
	commands[fileName] = ffmpeg
	...
}

func stopRecording(fileName string) error {
	cmd, ok := commands[fileName]
	if !ok {
		return errors.New(&quot;command not found&quot;)
	}
	defer func() {
		delete(commands, fileName)
	}()
	return cmd.Process.Kill()
}

You probably want to use sync.Mutex or sync.RWMutex to avoid concurrent map writes.

So your commands cloud look like:

type Commands struct {
    sync.RWMutex
    items map[string]*exec.Cmd
}
// use Commands.Lock() for writing, Commands.RLock() for reading

huangapple
  • 本文由 发表于 2022年7月24日 14:14:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/73096188.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定