package main import ( "errors" "flag" "fmt" "log" "strings" "github.com/asticode/go-astiav" ) var ( input = flag.String("i", "", "the input path") ) type stream struct { decCodec *astiav.Codec decCodecContext *astiav.CodecContext inputStream *astiav.Stream } func main() { // Handle ffmpeg logs astiav.SetLogLevel(astiav.LogLevelDebug) astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) { var cs string if c != nil { if cl := c.Class(); cl != nil { cs = " - class: " + cl.String() } } log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l) }) // Parse flags flag.Parse() // Usage if *input == "" { log.Println("Usage: -i ") return } // Allocate packet pkt := astiav.AllocPacket() defer pkt.Free() // Allocate frame f := astiav.AllocFrame() defer f.Free() // Allocate input format context inputFormatContext := astiav.AllocFormatContext() if inputFormatContext == nil { log.Fatal(errors.New("main: input format context is nil")) } defer inputFormatContext.Free() // Open input if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil { log.Fatal(fmt.Errorf("main: opening input failed: %w", err)) } defer inputFormatContext.CloseInput() // Find stream info if err := inputFormatContext.FindStreamInfo(nil); err != nil { log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err)) } // Loop through streams streams := make(map[int]*stream) // Indexed by input stream index for _, is := range inputFormatContext.Streams() { // Only process audio or video if is.CodecParameters().MediaType() != astiav.MediaTypeAudio && is.CodecParameters().MediaType() != astiav.MediaTypeVideo { continue } // Create stream s := &stream{inputStream: is} // Find decoder if s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()); s.decCodec == nil { log.Fatal(errors.New("main: codec is nil")) } // Allocate codec context if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil { log.Fatal(errors.New("main: codec context is nil")) } defer s.decCodecContext.Free() // Update codec context if err := is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil { log.Fatal(fmt.Errorf("main: updating codec context failed: %w", err)) } // Open codec context if err := s.decCodecContext.Open(s.decCodec, nil); err != nil { log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err)) } // Add stream streams[is.Index()] = s } // Loop for { // We use a closure to ease unreferencing the packet if stop := func() bool { // Read frame if err := inputFormatContext.ReadFrame(pkt); err != nil { if errors.Is(err, astiav.ErrEof) { return true } log.Fatal(fmt.Errorf("main: reading frame failed: %w", err)) } // Make sure to unreference the packet defer pkt.Unref() // Get stream s, ok := streams[pkt.StreamIndex()] if !ok { return false } // Send packet if err := s.decCodecContext.SendPacket(pkt); err != nil { log.Fatal(fmt.Errorf("main: sending packet failed: %w", err)) } // Loop for { // We use a closure to ease unreferencing the frame if stop := func() bool { // Receive frame if err := s.decCodecContext.ReceiveFrame(f); err != nil { if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) { return true } log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err)) } // Make sure to unreference the frame defer f.Unref() // Log log.Printf("new %s frame: stream %d - pts: %d", s.inputStream.CodecParameters().MediaType(), pkt.StreamIndex(), f.Pts()) return false }(); stop { break } } return false }(); stop { break } } // Success log.Println("success") }