package media import ( "context" "os" "os/exec" "path/filepath" "testing" "time" ) // The real thing, when ffmpeg is here: a generated clip goes through as H.264 and comes out // as a playlist of segments, the status follows it to the end, and closing removes it all. func TestStreamWithFFmpeg(t *testing.T) { if _, err := exec.LookPath("ffmpeg"); err != nil { t.Skip("no ffmpeg") } c := Probe(context.Background()) if c.Error != "" { t.Skip(c.Error) } if EngineFor(c, "h264") == "" { t.Skip("nothing here encodes h264") } dir := t.TempDir() clip := filepath.Join(dir, "clip.mp4") out, err := exec.Command("ffmpeg", "-nostdin", "-v", "error", "-f", "lavfi", "-i", "testsrc=size=640x360:rate=25:duration=9", "-f", "lavfi", "-i", "sine=frequency=440:duration=9", "-c:v", "mpeg4", "-q:v", "5", "-c:a", "aac", "-shortest", clip).CombinedOutput() if err != nil { t.Skipf("could not make a clip: %s", out) } src, err := Inspect(context.Background(), c, clip, false) if err != nil || src.Video == nil || src.Audio == nil || src.Video.Codec != "mpeg4" || src.Duration < 8 { t.Fatalf("inspect: %+v %v", src, err) } m := NewStreamer(dir, "hamlab", 2) s, err := m.Open(context.Background(), c, false, clip, "h264", "480p", src) if err != nil { t.Fatal(err) } if s.Width != 640 || s.Height != 360 || s.Engine == "" { t.Fatalf("stream: %+v", s) } if p, ok := s.File("index.m3u8"); !ok { t.Fatal("no playlist") } else if !hasSegments(p) { t.Fatal("Open returned before the first segment") } for _, bad := range []string{"../x", "progress", "seg1.m4s", "index.m3u8/", "init.mp4.tmp"} { if _, ok := s.File(bad); ok { t.Fatalf("%q must not be served", bad) } } // the same request is the same stream if again, err := m.Open(context.Background(), c, false, clip, "h264", "480p", src); err != nil || again.ID != s.ID { t.Fatal("not reused") } var st StreamStatus for i := 0; i < 120; i++ { st = s.Status() if st.Done || st.Error != "" { break } time.Sleep(250 * time.Millisecond) } if !st.Done || st.Error != "" || st.Segments < 2 || st.Progress < 8 { t.Fatalf("status at the end: %+v", st) } if init, _ := s.File("init.mp4"); !exists(init) { t.Fatal("no init segment") } m.Close(s.ID) if _, ok := m.Find(s.ID); ok || exists(s.dir) { t.Fatal("close must forget and remove") } // an unplayable source is a DecodeError, and leaves nothing behind bad := filepath.Join(dir, "bad.mkv") _ = os.WriteFile(bad, []byte("not a video"), 0o644) if _, err := Inspect(context.Background(), c, bad, false); err == nil { t.Fatal("inspect of garbage") } if left, _ := os.ReadDir(m.dir); len(left) != 0 { t.Fatalf("left behind: %v", left) } }