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

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

How to kill command Exec in difference Function in Golang

问题

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

这是我的代码:

  1. // 创建房间/开始录制的处理程序
  2. func RoomCreate(c *fiber.Ctx) error {
  3. fileName := "out.mp4"
  4. fmt.Println(fileName)
  5. if len(os.Args) > 1 {
  6. fileName = os.Args[1]
  7. }
  8. errCh := make(chan error, 2)
  9. ctx, cancelFn := context.WithCancel(context.Background())
  10. // 调用startRecording函数
  11. go func() { errCh <- startRecording(ctx, fileName) }()
  12. go func() {
  13. errCh <- nil
  14. }()
  15. err := <-errCh
  16. cancelFn()
  17. if err != nil && err != context.Canceled {
  18. log.Fatalf("Execution failed: %v", err)
  19. }
  20. return c.Redirect(fmt.Sprintf("/room/%s", guuid.New().String()))
  21. }
  22. // 运行FFMPEG命令的函数
  23. func startRecording(ctx context.Context, fileName string) error {
  24. ctx, cancelFn := context.WithCancel(ctx)
  25. defer cancelFn()
  26. // 构建ffmpeg命令
  27. ffmpeg := exec.Command("ffmpeg",
  28. "-f", "gdigrab",
  29. "-framerate", "30",
  30. "-i", "desktop",
  31. "-f", "mp4",
  32. fileName,
  33. )
  34. // 用于发送数据的标准输入
  35. stdin, err := ffmpeg.StdinPipe()
  36. if err != nil {
  37. return err
  38. }
  39. defer stdin.Close()
  40. // 在后台运行命令
  41. errCh := make(chan error, 1)
  42. go func() {
  43. fmt.Printf("Executing: %v\n", strings.Join(ffmpeg.Args, " "))
  44. if err := ffmpeg.Run(); err != nil {
  45. return
  46. }
  47. errCh <- err
  48. }()
  49. // 只需开始发送一堆帧
  50. for {
  51. // 检查是否完成,否则继续
  52. select {
  53. case <-ctx.Done():
  54. return ctx.Err()
  55. case err := <-errCh:
  56. return err
  57. default:
  58. }
  59. }
  60. }
  61. // 在这里停止录制的函数
  62. func stopRecording(ctx context.Context) error {
  63. // 在这里停止录制的代码
  64. }

提前感谢。

英文:

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

  1. //Handler to create room/start record
  2. func RoomCreate(c *fiber.Ctx) error {
  3. fileName := &quot;out.mp4&quot;
  4. fmt.Println(fileName)
  5. if len(os.Args) &gt; 1 {
  6. fileName = os.Args[1]
  7. }
  8. errCh := make(chan error, 2)
  9. ctx, cancelFn := context.WithCancel(context.Background())
  10. // Call to function startRecording
  11. go func() { errCh &lt;- startRecording(ctx, fileName) }()
  12. go func() {
  13. errCh &lt;- nil
  14. }()
  15. err := &lt;-errCh
  16. cancelFn()
  17. if err != nil &amp;&amp; err != context.Canceled {
  18. log.Fatalf(&quot;Execution failed: %v&quot;, err)
  19. }
  20. return c.Redirect(fmt.Sprintf(&quot;/room/%s&quot;, guuid.New().String()))
  21. }
  22. //Function to run command FFMPEG
  23. func startRecording(ctx context.Context, fileName string) error {
  24. ctx, cancelFn := context.WithCancel(ctx)
  25. defer cancelFn()
  26. // Build ffmpeg
  27. ffmpeg := exec.Command(&quot;ffmpeg&quot;,
  28. &quot;-f&quot;, &quot;gdigrab&quot;,
  29. &quot;-framerate&quot;, &quot;30&quot;,
  30. &quot;-i&quot;, &quot;desktop&quot;,
  31. &quot;-f&quot;, &quot;mp4&quot;,
  32. fileName,
  33. )
  34. // Stdin for sending data
  35. stdin, err := ffmpeg.StdinPipe()
  36. if err != nil {
  37. return err
  38. }
  39. //var buf bytes.Buffer
  40. defer stdin.Close()
  41. // Run it in the background
  42. errCh := make(chan error, 1)
  43. go func() {
  44. fmt.Printf(&quot;Executing: %v\n&quot;, strings.Join(ffmpeg.Args, &quot; &quot;))
  45. if err := ffmpeg.Run(); err != nil {
  46. return
  47. }
  48. //fmt.Printf(&quot;FFMPEG output:\n%v\n&quot;, string(out))
  49. errCh &lt;- err
  50. }()
  51. // Just start sending a bunch of frames
  52. for {
  53. // Check if we&#39;re done, otherwise go again
  54. select {
  55. case &lt;-ctx.Done():
  56. return ctx.Err()
  57. case err := &lt;-errCh:
  58. return err
  59. default:
  60. }
  61. }
  62. }
  63. //Here function to stop Recording
  64. func stopRecording(ctx context.Context) error {
  65. //Code stop recording in here
  66. }

Thanks for advance

答案1

得分: 1

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

  1. var commands = map[string]*exec.Cmd{}
  2. func startRecording(fileName string) error {
  3. ffmpeg := exec.Command("ffmpeg",
  4. "-f", "gdigrab",
  5. "-framerate", "30",
  6. "-i", "desktop",
  7. "-f", "mp4",
  8. fileName,
  9. )
  10. commands[fileName] = ffmpeg
  11. ...
  12. }
  13. func stopRecording(fileName string) error {
  14. cmd, ok := commands[fileName]
  15. if !ok {
  16. return errors.New("command not found")
  17. }
  18. defer func() {
  19. delete(commands, fileName)
  20. }()
  21. return cmd.Process.Kill()
  22. }

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

所以你的 commands 可以像这样:

  1. type Commands struct {
  2. sync.RWMutex
  3. items map[string]*exec.Cmd
  4. }
  5. // 使用 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.

  1. var commands = map[string]*exec.Cmd{}
  2. func startRecording(fileName string) error {
  3. ffmpeg := exec.Command(&quot;ffmpeg&quot;,
  4. &quot;-f&quot;, &quot;gdigrab&quot;,
  5. &quot;-framerate&quot;, &quot;30&quot;,
  6. &quot;-i&quot;, &quot;desktop&quot;,
  7. &quot;-f&quot;, &quot;mp4&quot;,
  8. fileName,
  9. )
  10. commands[fileName] = ffmpeg
  11. ...
  12. }
  13. func stopRecording(fileName string) error {
  14. cmd, ok := commands[fileName]
  15. if !ok {
  16. return errors.New(&quot;command not found&quot;)
  17. }
  18. defer func() {
  19. delete(commands, fileName)
  20. }()
  21. return cmd.Process.Kill()
  22. }

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

So your commands cloud look like:

  1. type Commands struct {
  2. sync.RWMutex
  3. items map[string]*exec.Cmd
  4. }
  5. // 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:

确定