package generator import ( "fmt" "io" "io/ioutil" "log" "os" "os/exec" "path/filepath" "segmenta/pkg/handler" "sync" ) const AppName = "segmenta" const DefaultPlaylistFilename = "playlist.m3u8" // Generator generates assets for a given stream. The default implementation // segments using FFMPEG. type FFMPEGGenerator struct { name string src io.Reader handlers map[handler.Handler]bool } func NewFFMPEGGenerator(name string, src io.Reader) *FFMPEGGenerator { return &FFMPEGGenerator{ name: name, src: src, handlers: make(map[handler.Handler]bool), } } func (g *FFMPEGGenerator) Generate() error { // first, create a temporary directory. prefix := fmt.Sprintf("%s-%s", AppName, g.name) tmpdir, err := ioutil.TempDir("", prefix) if err != nil { return fmt.Errorf("ffmpeg: error initializing TempDir: %v", tmpdir) } defer os.RemoveAll(tmpdir) playlist := filepath.Join(tmpdir, DefaultPlaylistFilename) var wg sync.WaitGroup go g.doFFMPEG(&wg, playlist) wg.Add(1) wg.Wait() return nil } func (g *FFMPEGGenerator) AddHandler(h handler.Handler) { g.handlers[h] = true } func (g *FFMPEGGenerator) RemoveHandler(h handler.Handler) { delete(g.handlers, h) } // TODO should wg be passed in a Context? func (g *FFMPEGGenerator) doFFMPEG(wg *sync.WaitGroup, dest string) { defer wg.Done() cmd := exec.Command("ffmpeg", "-re", "-i", "-", "-f", "hls", "-t", "10", dest) cmd.Stdin = g.src if err := cmd.Run(); err != nil { // TODO: error handling log.Fatalf("ffmpeg: error running cmd: %v", err) } fmt.Println("ffmpeg: exited") }