Golang中如何在不同函数终止Exec启动的FFmpeg进程
解决方案:终止Go中通过exec启动的FFmpeg录屏进程
问题分析
你的现有代码存在两个核心问题:
RoomCreate中启动录制后立刻调用cancelFn(),导致录制刚启动就被终止。- 没有存储录制任务的上下文和FFmpeg进程引用,
stopRecording无法定位并终止目标进程。
核心解决思路
- 用并发安全的存储结构(如
sync.Map)关联每个房间ID与对应的录制任务(上下文取消函数、FFmpeg进程)。 - 给FFmpeg发送
q命令实现优雅停止,避免直接杀死进程导致视频文件损坏。 - 分离进程启动与阻塞等待,用
Start()替代Run(),让录制在后台运行并监听上下文取消信号。
完整修改代码示例
1. 定义全局录制任务存储
import ( "context" "os/exec" "sync" "fmt" "github.com/gofiber/fiber/v2" "github.com/google/uuid" ) type RecordingTask struct { CancelFn context.CancelFunc Process *exec.Cmd } var recordingTasks sync.Map // key: roomID string, value: *RecordingTask
2. 修改RoomCreate处理函数
func RoomCreate(c *fiber.Ctx) error { fileName := "out.mp4" if len(os.Args) > 1 { fileName = os.Args[1] } roomID := uuid.NewString() ctx, cancelFn := context.WithCancel(context.Background()) // 启动录制并获取FFmpeg进程 ffmpegCmd, err := startRecording(ctx, fileName) if err != nil { cancelFn() return c.Status(fiber.StatusInternalServerError).SendString("启动录制失败") } // 将任务存储到全局Map recordingTasks.Store(roomID, &RecordingTask{ CancelFn: cancelFn, Process: ffmpegCmd, }) return c.Redirect(fmt.Sprintf("/room/%s", roomID)) }
3. 重写startRecording函数
func startRecording(ctx context.Context, fileName string) (*exec.Cmd, error) { ffmpeg := exec.Command("ffmpeg", "-f", "gdigrab", "-framerate", "30", "-i", "desktop", "-f", "mp4", fileName, ) // 获取stdin管道,用于发送停止命令 stdin, err := ffmpeg.StdinPipe() if err != nil { return nil, err } // 启动进程(非阻塞) if err := ffmpeg.Start(); err != nil { stdin.Close() return nil, err } // 监听上下文取消信号,优雅停止FFmpeg go func() { <-ctx.Done() // 发送"q"命令让FFmpeg正常收尾 _, _ = stdin.Write([]byte("q")) stdin.Close() // 等待进程完全退出 _ = ffmpeg.Wait() }() return ffmpeg, nil }
4. 实现stopRecording函数
func stopRecording(c *fiber.Ctx) error { roomID := c.Params("roomID") if roomID == "" { return c.Status(fiber.StatusBadRequest).SendString("缺少房间ID") } // 从Map中删除并获取任务 task, ok := recordingTasks.LoadAndDelete(roomID) if !ok { return c.Status(fiber.StatusNotFound).SendString("未找到录制任务") } recordingTask := task.(*RecordingTask) // 触发上下文取消,执行优雅停止逻辑 recordingTask.CancelFn() return c.SendString("录制已停止") }
关键注意事项
- 优雅停止:直接调用
Process.Kill()会导致FFmpeg来不及写入视频文件的元数据,生成的MP4可能无法播放。发送q命令是FFmpeg官方推荐的停止方式。 - 并发安全:使用
sync.Map而非普通map,避免多goroutine操作时出现竞态问题。 - 资源清理:停止录制后从
recordingTasks中删除任务,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Tammam
相关产品推荐
相关产品推荐

