segmento/pkg/generator/ffmpeg.go

77 lines
1.6 KiB
Go

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")
}