hlsproxy.go (2187B)
1 package main 2 3 import ( 4 "fmt" 5 "log" 6 "net/http" 7 "net/url" 8 "os" 9 "path" 10 "time" 11 12 "github.com/untangledco/streaming/m3u8" 13 ) 14 15 const usage string = "usage: hlsproxy url" 16 17 func init() { 18 log.SetFlags(0) 19 log.SetPrefix("hlssrv: ") 20 } 21 22 func servePlaylist(p *m3u8.Playlist) http.HandlerFunc { 23 return func(w http.ResponseWriter, req *http.Request) { 24 if req.Method != http.MethodGet { 25 http.Error(w, "nope", http.StatusMethodNotAllowed) 26 } 27 log.Println(req.Method, req.URL) 28 w.Header().Set("Content-Type", "application/vnd.apple.mpegurl") 29 if err := m3u8.Encode(w, p); err != nil { 30 log.Println("encode playlist:", err) 31 } 32 } 33 } 34 35 func injectBreak(plist *m3u8.Playlist, dur time.Duration, schedule int) error { 36 // ticks := (dur * time.Second) / 90000 37 var money []m3u8.Segment 38 for i, seg := range plist.Segments { 39 if i%schedule != 0 || i == 0 { 40 money = append(money, seg) 41 continue 42 } 43 adbreak := m3u8.Segment{ 44 URI: "https://test-streams.mux.dev/test_001/stream_1000k_48k_640x360_000.ts", 45 Duration: dur, 46 Discontinuity: true, 47 } 48 seg.Discontinuity = true 49 money = append(money, adbreak, seg) 50 } 51 plist.Segments = money 52 return nil 53 } 54 55 func main() { 56 if len(os.Args) != 2 { 57 fmt.Fprintln(os.Stderr, usage) 58 os.Exit(127) 59 } 60 link, err := url.Parse(os.Args[1]) 61 if err != nil { 62 log.Fatal(err) 63 } 64 resp, err := http.Get(link.String()) 65 if err != nil { 66 log.Fatal("get playlist:", err) 67 } 68 defer resp.Body.Close() 69 if resp.StatusCode != http.StatusOK { 70 log.Fatal("get playlist: remote status:", resp.Status) 71 } 72 source, err := m3u8.Decode(resp.Body) 73 if err != nil { 74 log.Fatal("parse playlist:", err) 75 } 76 link.Path = path.Dir(link.Path) + "/" 77 for i := range source.Media { 78 source.Media[i].URI = link.String() + source.Media[i].URI 79 } 80 for i := range source.Variants { 81 source.Variants[i].URI = link.String() + source.Variants[i].URI 82 } 83 for i := range source.Segments { 84 source.Segments[i].URI = link.String() + source.Segments[i].URI 85 } 86 if err := injectBreak(source, 10*time.Second, 8); err != nil { 87 log.Fatalf("inject ad break: %v", err) 88 } 89 http.HandleFunc("/", servePlaylist(source)) 90 log.Fatal(http.ListenAndServe(":8080", nil)) 91 }