implement libav in some methods
This commit is contained in:
parent
9489689dbf
commit
d92f02d38e
7 changed files with 259 additions and 56 deletions
126
util/av/remux.go
126
util/av/remux.go
|
@ -2,31 +2,117 @@ package av
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||
"github.com/asticode/go-astiav"
|
||||
)
|
||||
|
||||
func RemuxFile(
|
||||
inputFile string,
|
||||
) error {
|
||||
tempFileName := inputFile + ".temp"
|
||||
outputFile := inputFile
|
||||
err := os.Rename(inputFile, tempFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to rename file: %w", err)
|
||||
func RemuxFile(inputFile string) error {
|
||||
ext := strings.ToLower(filepath.Ext(inputFile))
|
||||
var muxerName string
|
||||
switch ext {
|
||||
case ".mp4":
|
||||
muxerName = "mp4"
|
||||
case ".mkv":
|
||||
muxerName = "matroska"
|
||||
case ".mov":
|
||||
muxerName = "mov"
|
||||
case ".avi":
|
||||
muxerName = "avi"
|
||||
default:
|
||||
return fmt.Errorf("unsupported output container for extension: %s", ext)
|
||||
}
|
||||
defer os.Remove(tempFileName)
|
||||
err = ffmpeg.
|
||||
Input(tempFileName).
|
||||
Output(outputFile, ffmpeg.KwArgs{
|
||||
"c": "copy",
|
||||
}).
|
||||
Silent(true).
|
||||
OverWriteOutput().
|
||||
Run()
|
||||
outputFile := strings.TrimSuffix(inputFile, ext) + ".remuxed" + ext
|
||||
|
||||
inputCtx := astiav.AllocFormatContext()
|
||||
if inputCtx == nil {
|
||||
return fmt.Errorf("failed to alloc input format context")
|
||||
}
|
||||
defer inputCtx.Free()
|
||||
|
||||
if err := inputCtx.OpenInput(inputFile, nil, nil); err != nil {
|
||||
return fmt.Errorf("failed to open input: %w", err)
|
||||
}
|
||||
defer inputCtx.CloseInput()
|
||||
|
||||
if err := inputCtx.FindStreamInfo(nil); err != nil {
|
||||
return fmt.Errorf("failed to find stream info: %w", err)
|
||||
}
|
||||
|
||||
outCtx, err := astiav.AllocOutputFormatContext(nil, muxerName, outputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remux file: %w", err)
|
||||
return fmt.Errorf("failed to alloc output format context: %w", err)
|
||||
}
|
||||
defer outCtx.Free()
|
||||
|
||||
inToOutIdx := make(map[int]int)
|
||||
for inIdx, inStream := range inputCtx.Streams() {
|
||||
inCP := inStream.CodecParameters()
|
||||
if inCP.CodecID() == astiav.CodecIDNone {
|
||||
continue
|
||||
}
|
||||
if mt := inCP.MediaType(); mt != astiav.MediaTypeVideo && mt != astiav.MediaTypeAudio && mt != astiav.MediaTypeSubtitle {
|
||||
continue
|
||||
}
|
||||
outStream := outCtx.NewStream(nil)
|
||||
if outStream == nil {
|
||||
return fmt.Errorf("failed to create new stream in output context")
|
||||
}
|
||||
if err := inCP.Copy(outStream.CodecParameters()); err != nil {
|
||||
return fmt.Errorf("failed to copy codec parameters: %w", err)
|
||||
}
|
||||
outStream.CodecParameters().SetCodecTag(0)
|
||||
outStream.SetTimeBase(inStream.TimeBase())
|
||||
inToOutIdx[inIdx] = len(inToOutIdx)
|
||||
}
|
||||
if len(inToOutIdx) == 0 {
|
||||
return fmt.Errorf("no supported streams to remux")
|
||||
}
|
||||
|
||||
if !outCtx.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) {
|
||||
ioCtx, err := astiav.OpenIOContext(outputFile, astiav.NewIOContextFlags(astiav.IOContextFlagWrite), nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open output IO context: %w", err)
|
||||
}
|
||||
defer ioCtx.Close()
|
||||
outCtx.SetPb(ioCtx)
|
||||
}
|
||||
|
||||
if err := outCtx.WriteHeader(nil); err != nil {
|
||||
return fmt.Errorf("failed to write output header: %w", err)
|
||||
}
|
||||
|
||||
packet := astiav.AllocPacket()
|
||||
defer packet.Free()
|
||||
for {
|
||||
if err := inputCtx.ReadFrame(packet); err != nil {
|
||||
if err == astiav.ErrEof {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("error reading frame: %w", err)
|
||||
}
|
||||
outIdx, ok := inToOutIdx[packet.StreamIndex()]
|
||||
if !ok {
|
||||
packet.Unref()
|
||||
continue
|
||||
}
|
||||
inStream := inputCtx.Streams()[packet.StreamIndex()]
|
||||
outStream := outCtx.Streams()[outIdx]
|
||||
packet.SetPts(astiav.RescaleQRnd(packet.Pts(), inStream.TimeBase(), outStream.TimeBase(), astiav.RoundingNearInf))
|
||||
packet.SetDts(astiav.RescaleQRnd(packet.Dts(), inStream.TimeBase(), outStream.TimeBase(), astiav.RoundingNearInf))
|
||||
packet.SetDuration(astiav.RescaleQ(packet.Duration(), inStream.TimeBase(), outStream.TimeBase()))
|
||||
packet.SetStreamIndex(outIdx)
|
||||
packet.SetPos(-1)
|
||||
if err := outCtx.WriteInterleavedFrame(packet); err != nil {
|
||||
packet.Unref()
|
||||
return fmt.Errorf("error writing frame: %w", err)
|
||||
}
|
||||
packet.Unref()
|
||||
}
|
||||
|
||||
if err := outCtx.WriteTrailer(); err != nil {
|
||||
return fmt.Errorf("failed to write trailer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue