package playout import ( "bytes" "ffmpeg-playout/status" "fmt" "log" "net/http" "net/url" "os" "os/exec" "path" "strconv" "time" ) type Job struct { ID int Version string StartAt time.Time StopAt time.Time Source string Output string ControlChannel chan string } type Config struct { PlayoutScriptPath string PlayoutScript string ProgressDir string PrometheusPushGateway string } //nolint:funlen func (p *Job) Playout(cfg *Config) { // TODO delete playout Job from store after finishing/aborting playout for { select { case ctrlMsg := <-p.ControlChannel: fmt.Println(ctrlMsg) continue case <-time.After(time.Until(p.StartAt)): log.Printf("Start Playout for %v", p.ID) progressPath := path.Join(cfg.ProgressDir, strconv.Itoa(p.ID)) playoutScript := path.Join(cfg.PlayoutScriptPath, cfg.PlayoutScript) cmd := exec.Command(playoutScript, //nolint:gosec fmt.Sprintf("-i %v", p.Source), fmt.Sprintf("-o %v", p.Output), fmt.Sprintf("-p %v", progressPath), ) cmd.Dir = cfg.PlayoutScriptPath err := cmd.Start() if err != nil { log.Printf("Failed to start Playout %v: %v", p.ID, err) } pid := cmd.Process.Pid log.Printf("PID for %v: %v", p.ID, pid) log.Println(cmd.Args) go func() { stats := make(chan status.Status) c := 0 for { if c > 15 { return } if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) { time.Sleep(1 * time.Second) c++ continue } err := status.FFmpegStatusWatcher(stats, progressPath) if err != nil { log.Println(err) continue } else { break } } u, _ := url.Parse(cfg.PrometheusPushGateway) u.Path = path.Join(u.Path, "metrics", "job", "Decklink_Outputs", p.Output) c = 0 for c < 4 { select { case stat := <-stats: metric := fmt.Sprintf("frame %v\n dup_frames %v\n bitrate %v\n fps %v\n speed %v\n", stat.Frame, stat.Dupframes, stat.Bitrate, stat.FPS, stat.Speed) resp, err := http.Post(u.Path, "text/plain", bytes.NewBuffer([]byte(metric))) if err != nil { log.Println(err) } log.Println(metric) defer resp.Body.Close() case <-time.After(4 * time.Second): c++ continue } } }() ProcessManagement: for { select { case ctrlMsg := <-p.ControlChannel: log.Println(ctrlMsg) continue case <-time.After(time.Until(p.StopAt)): log.Printf("Kill %v", cmd.Process.Pid) err := cmd.Process.Kill() if err != nil { log.Printf("Failed to kill %v: %v", cmd.Process.Pid, err) } break ProcessManagement } } err = cmd.Wait() if err != nil { log.Println(err) } out, _ := cmd.CombinedOutput() log.Println(string(out)) log.Println(cmd.ProcessState.ExitCode()) log.Printf("Finish %v", p.ID) return } } }