diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f91e5b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/segmento diff --git a/main.go b/main.go new file mode 100644 index 0000000..94895b3 --- /dev/null +++ b/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + "github.com/tcolgate/mp3" +) + +// TODO what should be the behaviour when adding a Frame to a Segment causes it to over-shoot +// the TargetDuration? +// should the frame be partially added, or not added at all? +// typical frame duration 36ms +type Segment struct { + TargetDuration time.Duration + Duration time.Duration + Data []byte +} + +func newSegment(capacity int) *Segment { + return &Segment{ + Data: make([]byte, 0, capacity), + } +} + +func (s *Segment) Write(p []byte) (n int, err error) { + fmt.Println("write", len(p), "bytes") + return len(p), nil +} + +type MP3HTTPSegmenter struct { + decoder *mp3.Decoder +} + +func (s *MP3HTTPSegmenter) Segment(r io.Reader) (chan Segment, error) { + c := make(chan Segment) + + go func() { + d := mp3.NewDecoder(r) + + var ( + v mp3.Frame + skipped int + ) + + for { + if err := d.Decode(&v, &skipped); err != nil { + log.Fatal(err) + } + + fmt.Println("decoded frame of", v.Size(), "bytes, duration", v.Duration(), ", skipped", skipped, "bytes") + } + }() + + return c, nil +} + +func newMP3HTTPSegmenter() *MP3HTTPSegmenter { + return &MP3HTTPSegmenter{} +} + +// TODO + +// A Segmenter class which allows the passing in of a Reader +// i.e. +// Segment(r io.Reader) (chan *Segment, error) +// This will create an mp3.NewDecoder(r) and store the returned decoder. +// As the Reader is read, the Segmenter will publish a stream of SegmentEvents + +func main() { + // TODO accept some flags with: + // URL - source of stream + // TargetLength - length of segments in seconds + // Output + // -d some_dir/ => output playlist and chunks to this directory, cleaning up old files from time to time. + // -b 0.0.0.0:3000 => serve playlist and chunks from an HTTP server bound to this address + + var url string + flag.StringVar(&url, "url", "", "URL of MP3 stream") + + flag.Parse() + + if url == "" { + log.Println("Invalid arguments") + flag.PrintDefaults() + os.Exit(-1) + } + + log.Println("Open URL:", url) + + client := &http.Client{} + resp, err := client.Get(url) + + if err != nil { + log.Fatal(err) + } + + defer resp.Body.Close() + + segmenter := newMP3HTTPSegmenter() + segments, err := segmenter.Segment(resp.Body) + if err != nil { + log.Fatal(err) + } + + go func() { + for segment := range segments { + log.Println("got segment", segment) + } + }() + + time.Sleep(10 * time.Second) + + log.Println("exiting") +}