goffmpeg/examples/simple-transcode/main.go

175 lines
3.8 KiB
Go

package main
import (
"fmt"
"log"
"os"
"git.kingecg.top/kingecg/goffmpeg/pkg/ffmpeg"
)
// Simple remuxing example using goffmpeg library
func main() {
if len(os.Args) < 3 {
fmt.Println("Usage: simple-transcode <input> <output>")
fmt.Println("Example: simple-transcode input.mp4 output.flv")
os.Exit(1)
}
inputURL := os.Args[1]
outputURL := os.Args[2]
// Open input file
ic := ffmpeg.AllocFormatContext()
defer ic.Free()
if err := ic.OpenInput(inputURL); err != nil {
log.Fatalf("Failed to open input %s: %v", inputURL, err)
}
// Find stream info
if err := ic.FindStreamInfo(); err != nil {
log.Fatalf("Failed to find stream info: %v", err)
}
fmt.Printf("Input: %s\n", inputURL)
ic.DumpFormat(0, inputURL, false)
// Guess output format
of := ffmpeg.GuessFormat("", outputURL)
if of == nil {
log.Fatalf("Failed to guess output format")
}
// Create output context
ofc, err := ffmpeg.AllocOutputContext(outputURL, of)
if err != nil {
log.Fatalf("Failed to allocate output context: %v", err)
}
defer ofc.Free()
// Get input streams
inputStreams := ic.Streams()
// Process streams - select first video and audio only
var videoIdx, audioIdx int = -1, -1
streamCount := 0
for i, is := range inputStreams {
cp := is.CodecParameters()
if cp == nil {
continue
}
streamType := cp.CodecType()
// Skip non-video/audio
if streamType != ffmpeg.CodecTypeVideo && streamType != ffmpeg.CodecTypeAudio {
continue
}
// Skip duplicate audio
if streamType == ffmpeg.CodecTypeAudio && audioIdx >= 0 {
fmt.Printf("\nStream %d: audio (skipped - duplicate)\n", i)
continue
}
// Add stream with same codec (stream copy mode)
os, err := ofc.AddStream(nil)
if err != nil {
fmt.Printf("\nStream %d: failed to add stream: %v\n", i, err)
continue
}
// Copy codec parameters from input to output
if err := os.SetCodecParameters(cp); err != nil {
fmt.Printf("\nStream %d: failed to set codec parameters: %v\n", i, err)
continue
}
// Copy time base
tb := is.TimeBase()
os.SetTimeBase(tb)
if streamType == ffmpeg.CodecTypeVideo {
videoIdx = streamCount
fmt.Printf("\nStream %d: video (stream copy)\n", i)
} else {
audioIdx = streamCount
fmt.Printf("\nStream %d: audio (stream copy)\n", i)
}
streamCount++
}
if videoIdx < 0 && audioIdx < 0 {
log.Fatal("No supported streams found")
}
fmt.Printf("\nOutput: %s\n", outputURL)
fmt.Printf("Output has %d streams\n", streamCount)
ofc.DumpFormat(0, outputURL, true)
// Open output file
if err := ofc.OpenOutput(outputURL); err != nil {
log.Fatalf("Failed to open output: %v", err)
}
// Write header
if err := ofc.WriteHeader(); err != nil {
log.Fatalf("Failed to write header: %v", err)
}
fmt.Println("\nRemuxing...")
pkt := ffmpeg.AllocPacket()
defer pkt.Free()
packetCount := 0
for {
err := ic.ReadPacket(pkt)
if err != nil {
break
}
pktStreamIdx := pkt.StreamIndex()
// Map input stream index to output stream index
var outStreamIdx int
if videoIdx >= 0 && audioIdx >= 0 {
// Both video and audio present
if pktStreamIdx == 0 {
outStreamIdx = videoIdx
} else if pktStreamIdx == 1 {
outStreamIdx = audioIdx
} else {
pkt.Unref()
continue
}
} else if videoIdx >= 0 {
outStreamIdx = videoIdx
} else {
outStreamIdx = audioIdx
}
pkt.SetStreamIndex(outStreamIdx)
if err := ofc.WritePacket(pkt); err != nil {
log.Printf("Warning: failed to write packet: %v", err)
}
pkt.Unref()
packetCount++
if packetCount%100 == 0 {
fmt.Printf("Processed %d packets...\n", packetCount)
}
}
// Write trailer
if err := ofc.WriteTrailer(); err != nil {
log.Fatalf("Failed to write trailer: %v", err)
}
fmt.Printf("\nRemuxing complete! Processed %d packets.\n", packetCount)
fmt.Printf("Output saved to: %s\n", outputURL)
}