mirror of
https://github.com/3d0c/gmf
synced 2025-12-24 10:40:59 +08:00
AVFilter, examples
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
*.mpg
|
||||
*.exe
|
||||
!examples/tests-sample.mp4
|
||||
!examples/bbb.mp4
|
||||
remuxing
|
||||
transcode
|
||||
*.ts
|
||||
|
||||
23
codecCtx.go
23
codecCtx.go
@@ -590,3 +590,26 @@ func (cc *CodecCtx) Encode(frames []*Frame, drain int) ([]*Packet, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Decode2(pkt *Packet) (*Frame, int) {
|
||||
var (
|
||||
ret int
|
||||
)
|
||||
|
||||
if pkt == nil {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, nil))
|
||||
} else {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, &pkt.avPacket))
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
frame := NewFrame()
|
||||
|
||||
if ret = int(C.avcodec_receive_frame(cc.avCodecCtx, frame.avFrame)); ret < 0 {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
return frame, 0
|
||||
}
|
||||
|
||||
BIN
examples/bbb.mp4
Normal file
BIN
examples/bbb.mp4
Normal file
Binary file not shown.
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/3d0c/gmf"
|
||||
)
|
||||
@@ -22,8 +22,9 @@ func (i *arrayFlags) Set(value string) error {
|
||||
}
|
||||
|
||||
type Input struct {
|
||||
ctx *gmf.FmtCtx
|
||||
finished bool
|
||||
ctx *gmf.FmtCtx
|
||||
lastFrame *gmf.Frame
|
||||
finished bool
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -88,9 +89,9 @@ func addStream(codecName string, oc *gmf.FmtCtx, ist *gmf.Stream) (int, int) {
|
||||
cc.SetTimeBase(gmf.AVR{1, 25})
|
||||
ost.SetTimeBase(gmf.AVR{1, 25})
|
||||
cc.SetProfile(gmf.FF_PROFILE_MPEG4_SIMPLE)
|
||||
fmt.Printf("setup dims: %d, %d\n", ist.CodecCtx().Width(), ist.CodecCtx().Height())
|
||||
cc.SetDimension(ist.CodecCtx().Width(), ist.CodecCtx().Height())
|
||||
cc.SetPixFmt(ist.CodecCtx().PixFmt())
|
||||
// cc.SetOptions([]gmf.Option{gmf.Option{Key: "b", Val: 100000}})
|
||||
}
|
||||
|
||||
if err := cc.Open(nil); err != nil {
|
||||
@@ -116,7 +117,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if len(src) == 0 || dst == "" {
|
||||
log.Fatal("at least one source and destination required")
|
||||
log.Fatal("at least one source and destination required, e.g.\n./watermark -src=bbb.mp4 -src=test.png -dst=overlay.mp4")
|
||||
}
|
||||
|
||||
octx, err := gmf.NewOutputCtx(dst)
|
||||
@@ -135,7 +136,7 @@ func main() {
|
||||
inputs[i] = &Input{}
|
||||
inputs[i].ctx = ictx
|
||||
|
||||
log.Printf("src[%d]=%s\n", i, name)
|
||||
log.Printf("Source #%d - %s\n", i, name)
|
||||
}
|
||||
|
||||
srcVideoStream, err := inputs[0].ctx.GetBestStream(gmf.AVMEDIA_TYPE_VIDEO)
|
||||
@@ -172,24 +173,23 @@ func main() {
|
||||
*/
|
||||
|
||||
var (
|
||||
i int = 0
|
||||
pkt *gmf.Packet
|
||||
ist *gmf.Stream
|
||||
ost *gmf.Stream
|
||||
i, ret int = 0, 0
|
||||
pkt *gmf.Packet
|
||||
ist *gmf.Stream
|
||||
ost *gmf.Stream
|
||||
)
|
||||
|
||||
for i, stream := range srcStreams {
|
||||
fmt.Printf("srcStreams[%d] - %s, %d\n", i, stream.CodecCtx().Codec().LongName(), stream.CodecCtx().Width())
|
||||
log.Printf("stream #%d - %s, %s\n", i, stream.CodecCtx().Codec().LongName(), stream.CodecCtx().GetVideoSize())
|
||||
}
|
||||
|
||||
filtered := make([]*gmf.Frame, 0)
|
||||
|
||||
ost, err = octx.GetStream(0)
|
||||
if err != nil {
|
||||
log.Fatalf("can't get stream - %s\n", err)
|
||||
}
|
||||
|
||||
filter, err := gmf.NewFilter("overlay=10:main_h-overlay_h-10", srcStreams, ost, options)
|
||||
defer filter.Release()
|
||||
if err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
@@ -198,21 +198,16 @@ func main() {
|
||||
log.Fatalf("error writing header - %s\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("ost TimeBase: %v, %s\n", ost.TimeBase(), ost.CodecCtx().Codec().LongName())
|
||||
|
||||
total := 0
|
||||
init := false
|
||||
|
||||
for {
|
||||
total++
|
||||
fmt.Printf("Total: %d\n", total)
|
||||
if total == 30 {
|
||||
break
|
||||
}
|
||||
fmt.Printf("i=%d\n", i)
|
||||
var (
|
||||
frame *gmf.Frame
|
||||
ff []*gmf.Frame
|
||||
)
|
||||
|
||||
for {
|
||||
if finishedNb() == len(inputs) {
|
||||
log.Printf("finished all\n")
|
||||
log.Printf("Finished all\n")
|
||||
break
|
||||
}
|
||||
|
||||
@@ -221,7 +216,6 @@ func main() {
|
||||
}
|
||||
|
||||
if inputs[i].finished {
|
||||
fmt.Printf("inputs[%d] - finished\n", i)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
@@ -232,8 +226,12 @@ func main() {
|
||||
if err != nil && err.Error() != "End of file" {
|
||||
log.Fatalf("error getting next packet - %s", err)
|
||||
} else if err != nil && pkt == nil {
|
||||
fmt.Printf("continue getting next packets - %s\n", err)
|
||||
inputs[i].finished = true
|
||||
if !inputs[i].finished {
|
||||
log.Printf("EOF input #%d, closing\n", i)
|
||||
filter.RequestOldest()
|
||||
filter.Close(i)
|
||||
inputs[i].finished = true
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
@@ -247,17 +245,17 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
frames, err := ist.CodecCtx().Decode(pkt)
|
||||
if err != nil && err.Error() != "End of file" {
|
||||
log.Fatalf("error decoding - %s\n", err)
|
||||
} else if err != nil {
|
||||
log.Printf("error decoding pkt - %s\n", err)
|
||||
frame, ret = ist.CodecCtx().Decode2(pkt)
|
||||
if ret < 0 && gmf.AvErrno(ret) == syscall.EAGAIN {
|
||||
continue
|
||||
} else if ret == gmf.AVERROR_EOF {
|
||||
log.Fatalf("EOF in Decode2, handle it\n")
|
||||
} else if ret < 0 {
|
||||
log.Fatalf("Unexpected error - %s\n", gmf.AvError(ret))
|
||||
}
|
||||
|
||||
fmt.Printf("len(frames) = %d\n", len(frames))
|
||||
|
||||
if len(frames) > 0 && !init {
|
||||
if err := filter.AddFrame(frames[0], i); err != nil {
|
||||
if frame != nil && !init {
|
||||
if err := filter.AddFrame(frame, i, 0); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
i++
|
||||
@@ -265,38 +263,22 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, frame := range frames {
|
||||
log.Printf("frame: %dX%d", frame.Width(), frame.Height())
|
||||
|
||||
if err := filter.AddFrame(frame, i); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
ff, err := filter.GetFrame()
|
||||
if err != nil {
|
||||
log.Printf("err != nil - %s\n", err)
|
||||
continue
|
||||
}
|
||||
if len(ff) == 0 {
|
||||
log.Printf("len(ff) = 0\n")
|
||||
filtered = nil
|
||||
break
|
||||
}
|
||||
|
||||
for idx, f := range ff {
|
||||
log.Printf("ff[%d]: %dX%d\n", idx, f.Width(), f.Height())
|
||||
}
|
||||
|
||||
filtered = append(filtered, ff...)
|
||||
if err := filter.AddFrame(frame, i, 4); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
if len(filtered) == 0 {
|
||||
log.Printf("len(filtered) == 0\n")
|
||||
if ff, err = filter.GetFrame(); err != nil && len(ff) == 0 {
|
||||
log.Printf("GetFrame() returned '%s', continue\n", err)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
packets, err := ost.CodecCtx().Encode(filtered, -1)
|
||||
if len(ff) == 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
packets, err := ost.CodecCtx().Encode(ff, -1)
|
||||
if err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
@@ -313,7 +295,7 @@ func main() {
|
||||
}
|
||||
|
||||
i++
|
||||
fmt.Printf("------------------------\n")
|
||||
}
|
||||
|
||||
octx.WriteTrailer()
|
||||
}
|
||||
BIN
examples/wm2
BIN
examples/wm2
Binary file not shown.
273
examples/wm2.go
273
examples/wm2.go
@@ -1,273 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/3d0c/gmf"
|
||||
)
|
||||
|
||||
type arrayFlags []string
|
||||
|
||||
func (i *arrayFlags) String() string {
|
||||
return strings.Join(*i, " ")
|
||||
}
|
||||
|
||||
func (i *arrayFlags) Set(value string) error {
|
||||
*i = append(*i, strings.TrimSpace(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
func assert(i interface{}, err error) interface{} {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func addStream(codecName string, oc *gmf.FmtCtx, ist *gmf.Stream) (int, int) {
|
||||
var cc *gmf.CodecCtx
|
||||
var ost *gmf.Stream
|
||||
|
||||
codec := assert(gmf.FindEncoder(codecName)).(*gmf.Codec)
|
||||
|
||||
// Create Video stream in output context
|
||||
if ost = oc.NewStream(codec); ost == nil {
|
||||
log.Fatal(errors.New("unable to create stream in output context"))
|
||||
}
|
||||
defer gmf.Release(ost)
|
||||
|
||||
if cc = gmf.NewCodecCtx(codec); cc == nil {
|
||||
log.Fatal(errors.New("unable to create codec context"))
|
||||
}
|
||||
defer gmf.Release(cc)
|
||||
|
||||
if oc.IsGlobalHeader() {
|
||||
cc.SetFlag(gmf.CODEC_FLAG_GLOBAL_HEADER)
|
||||
}
|
||||
|
||||
if codec.IsExperimental() {
|
||||
cc.SetStrictCompliance(gmf.FF_COMPLIANCE_EXPERIMENTAL)
|
||||
}
|
||||
|
||||
if cc.Type() == gmf.AVMEDIA_TYPE_AUDIO {
|
||||
cc.SetSampleFmt(ist.CodecCtx().SampleFmt())
|
||||
cc.SetSampleRate(ist.CodecCtx().SampleRate())
|
||||
cc.SetChannels(ist.CodecCtx().Channels())
|
||||
cc.SelectChannelLayout()
|
||||
cc.SelectSampleRate()
|
||||
|
||||
}
|
||||
|
||||
if cc.Type() == gmf.AVMEDIA_TYPE_VIDEO {
|
||||
cc.SetTimeBase(gmf.AVR{1, 25})
|
||||
ost.SetTimeBase(gmf.AVR{1, 25})
|
||||
cc.SetProfile(gmf.FF_PROFILE_MPEG4_SIMPLE)
|
||||
fmt.Printf("setup dims: %d, %d\n", ist.CodecCtx().Width(), ist.CodecCtx().Height())
|
||||
cc.SetDimension(ist.CodecCtx().Width(), ist.CodecCtx().Height())
|
||||
cc.SetPixFmt(ist.CodecCtx().PixFmt())
|
||||
}
|
||||
|
||||
if err := cc.Open(nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ost.SetCodecCtx(cc)
|
||||
|
||||
return ist.Index(), ost.Index()
|
||||
}
|
||||
|
||||
func multiplex(inputs []*gmf.FmtCtx, filter *gmf.Filter) <-chan *gmf.Frame {
|
||||
var (
|
||||
out chan *gmf.Frame = make(chan *gmf.Frame)
|
||||
lf []*gmf.Frame = make([]*gmf.Frame, len(inputs), len(inputs))
|
||||
ff []*gmf.Frame = make([]*gmf.Frame, 0)
|
||||
finished map[int]bool = map[int]bool{}
|
||||
pkt *gmf.Packet
|
||||
ist *gmf.Stream
|
||||
err error
|
||||
ret int
|
||||
)
|
||||
|
||||
go func(out chan *gmf.Frame) {
|
||||
loop:
|
||||
for {
|
||||
for y := 0; y < len(inputs); y++ {
|
||||
ictx := inputs[y]
|
||||
|
||||
pkt, err = ictx.GetNextPacket()
|
||||
if err != nil && err.Error() != "End of file" {
|
||||
log.Fatalf("error getting next packet - %s", err)
|
||||
break loop
|
||||
} else if err != nil && pkt == nil {
|
||||
if lf == nil {
|
||||
log.Fatalf("last frame is not initialized\n")
|
||||
}
|
||||
|
||||
finished[y] = true
|
||||
|
||||
tmp := lf[y].CloneNewFrame()
|
||||
if err := filter.AddFrame(tmp, y); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
ff, ret = filter.GetFrame2()
|
||||
} else {
|
||||
ist, err = ictx.GetStream(pkt.StreamIndex())
|
||||
if err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
if ist.IsAudio() {
|
||||
continue
|
||||
}
|
||||
|
||||
frames, err := ist.CodecCtx().Decode(pkt)
|
||||
if err != nil && err.Error() != "End of file" {
|
||||
log.Fatalf("error decoding - %s\n", err)
|
||||
} else if err != nil {
|
||||
log.Printf("error decoding pkt - %s\n", err)
|
||||
}
|
||||
|
||||
for i, _ := range frames {
|
||||
lf[y] = frames[i].CloneNewFrame()
|
||||
|
||||
if err := filter.AddFrame(frames[i], y); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
ff, ret = filter.GetFrame2()
|
||||
}
|
||||
|
||||
log.Printf("ret=%d\n", ret)
|
||||
|
||||
if len(finished) == len(inputs) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
// for i, _ := range ff {
|
||||
// out <- ff[i]
|
||||
// }
|
||||
}
|
||||
close(out)
|
||||
}(out)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
src arrayFlags
|
||||
dst string
|
||||
streamMap map[int]int = make(map[int]int)
|
||||
)
|
||||
|
||||
log.SetFlags(log.Lshortfile)
|
||||
|
||||
flag.Var(&src, "src", "source files, e.g.: -src=1.mp4 -src=image.png")
|
||||
flag.StringVar(&dst, "dst", "", "destination file, e.g. -dst=result.mp4")
|
||||
flag.Parse()
|
||||
|
||||
if len(src) == 0 || dst == "" {
|
||||
log.Fatal("at least one source and destination required")
|
||||
}
|
||||
|
||||
octx, err := gmf.NewOutputCtx(dst)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
inputs := make([]*gmf.FmtCtx, 0)
|
||||
|
||||
for i, name := range src {
|
||||
ictx, err := gmf.NewInputCtx(name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
inputs = append(inputs, ictx)
|
||||
log.Printf("src[%d]=%s\n", i, name)
|
||||
}
|
||||
|
||||
srcVideoStream, err := inputs[0].GetBestStream(gmf.AVMEDIA_TYPE_VIDEO)
|
||||
if err != nil {
|
||||
log.Fatalf("No video stream found\n")
|
||||
} else {
|
||||
i, o := addStream("libx264", octx, srcVideoStream)
|
||||
streamMap[i] = o
|
||||
}
|
||||
|
||||
srcStreams := []*gmf.Stream{}
|
||||
|
||||
for i, _ := range inputs {
|
||||
for idx := 0; idx < inputs[i].StreamsCnt(); idx++ {
|
||||
stream, err := inputs[i].GetStream(idx)
|
||||
if err != nil {
|
||||
log.Fatalf("error getting stream - %s\n", err)
|
||||
}
|
||||
|
||||
if !stream.IsVideo() {
|
||||
continue
|
||||
}
|
||||
|
||||
srcStreams = append(srcStreams, stream)
|
||||
}
|
||||
}
|
||||
|
||||
options := []*gmf.Option{
|
||||
/*
|
||||
{
|
||||
Key: "pix_fmts", Val: []int32{gmf.AV_PIX_FMT_YUV420P},
|
||||
},
|
||||
*/
|
||||
}
|
||||
|
||||
var (
|
||||
ist *gmf.Stream
|
||||
ost *gmf.Stream
|
||||
)
|
||||
|
||||
for i, stream := range srcStreams {
|
||||
fmt.Printf("srcStreams[%d] - %s, %d\n", i, stream.CodecCtx().Codec().LongName(), stream.CodecCtx().Width())
|
||||
}
|
||||
|
||||
ost, err = octx.GetStream(0)
|
||||
if err != nil {
|
||||
log.Fatalf("can't get stream - %s\n", err)
|
||||
}
|
||||
|
||||
filter, err := gmf.NewFilter("overlay=10:main_h-overlay_h-10", srcStreams, ost, options)
|
||||
if err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
if err := octx.WriteHeader(); err != nil {
|
||||
log.Fatalf("error writing header - %s\n", err)
|
||||
}
|
||||
|
||||
for frame := range multiplex(inputs, filter) {
|
||||
packets, err := ost.CodecCtx().Encode([]*gmf.Frame{frame}, -1)
|
||||
if err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
|
||||
for _, op := range packets {
|
||||
gmf.RescaleTs(op, ist.TimeBase(), ost.TimeBase())
|
||||
op.SetStreamIndex(ost.Index())
|
||||
op.Dump()
|
||||
if err = octx.WritePacket(op); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
op.Free()
|
||||
}
|
||||
}
|
||||
|
||||
octx.WriteTrailer()
|
||||
}
|
||||
179
filter.go
179
filter.go
@@ -17,6 +17,11 @@ int gmf_av_buffersrc_add_frame_flags(AVFilterContext **filt_ctx, AVFrame *frame,
|
||||
return av_buffersrc_add_frame_flags(filt_ctx[i], frame, flags);
|
||||
}
|
||||
|
||||
// XXX PTS! Pass actual pts instead of 0.
|
||||
int gmf_av_buffersrc_close(AVFilterContext **filt_ctx, int i) {
|
||||
return av_buffersrc_close(filt_ctx[i], 0, AV_BUFFERSRC_FLAG_PUSH);
|
||||
}
|
||||
|
||||
AVFilterContext *gmf_get_current(AVFilterContext **filt_ctx, int i) {
|
||||
return filt_ctx[i];
|
||||
}
|
||||
@@ -31,19 +36,22 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AV_BUFFERSINK_FLAG_PEEK = 1
|
||||
AV_BUFFERSINK_FLAG_NO_REQUEST = 2
|
||||
AV_BUFFERSRC_FLAG_PUSH = 4
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
bufferCtx **_Ctype_AVFilterContext
|
||||
bufferCtx []*_Ctype_AVFilterContext
|
||||
sinkCtx *_Ctype_AVFilterContext
|
||||
filterGraph *_Ctype_AVFilterGraph
|
||||
bufferCtxNb int
|
||||
}
|
||||
|
||||
func NewFilter(desc string, srcStreams []*Stream, ost *Stream, options []*Option) (*Filter, error) {
|
||||
f := &Filter{}
|
||||
f := &Filter{
|
||||
filterGraph: C.avfilter_graph_alloc(),
|
||||
bufferCtx: make([]*_Ctype_AVFilterContext, 0),
|
||||
}
|
||||
|
||||
var (
|
||||
ret, i int
|
||||
@@ -52,19 +60,8 @@ func NewFilter(desc string, srcStreams []*Stream, ost *Stream, options []*Option
|
||||
outputs *_Ctype_AVFilterInOut
|
||||
curr *_Ctype_AVFilterInOut
|
||||
last *_Ctype_AVFilterContext
|
||||
format *_Ctype_AVFilterContext
|
||||
)
|
||||
|
||||
cnameOut := C.CString("out")
|
||||
defer C.free(unsafe.Pointer(cnameOut))
|
||||
|
||||
f.filterGraph = C.avfilter_graph_alloc()
|
||||
|
||||
f.bufferCtxNb = len(srcStreams)
|
||||
csz := C.ulong(f.bufferCtxNb)
|
||||
|
||||
f.bufferCtx = (**_Ctype_struct_AVFilterContext)(C.av_calloc(csz, C.sizeof_AVFilterContext))
|
||||
|
||||
cdesc := C.CString(desc)
|
||||
defer C.free(unsafe.Pointer(cdesc))
|
||||
|
||||
@@ -74,97 +71,93 @@ func NewFilter(desc string, srcStreams []*Stream, ost *Stream, options []*Option
|
||||
&inputs,
|
||||
&outputs,
|
||||
)); ret < 0 {
|
||||
return nil, fmt.Errorf("error parsing filter graph - %s", AvError(ret))
|
||||
return f, fmt.Errorf("error parsing filter graph - %s", AvError(ret))
|
||||
}
|
||||
defer C.avfilter_inout_free(&inputs)
|
||||
defer C.avfilter_inout_free(&outputs)
|
||||
|
||||
for curr = inputs; curr != nil; curr = curr.next {
|
||||
if len(srcStreams) < i {
|
||||
return nil, fmt.Errorf("not enough of source streams")
|
||||
}
|
||||
srcStream := srcStreams[i]
|
||||
|
||||
args = fmt.Sprintf("video_size=%s:pix_fmt=%d:time_base=%s:pixel_aspect=%s:sws_param=flags=%d", srcStream.CodecCtx().GetVideoSize(), srcStream.CodecCtx().PixFmt(), srcStream.TimeBase().AVR(), srcStream.CodecCtx().GetAspectRation().AVR(), SWS_BILINEAR)
|
||||
src := srcStreams[i]
|
||||
|
||||
cargs := C.CString(args)
|
||||
args = fmt.Sprintf("video_size=%s:pix_fmt=%d:time_base=%s:pixel_aspect=%s:sws_param=flags=%d:frame_rate=%s", src.CodecCtx().GetVideoSize(), src.CodecCtx().PixFmt(), src.TimeBase().AVR(), src.CodecCtx().GetAspectRation().AVR(), SWS_BILINEAR, src.GetRFrameRate().AVR().String())
|
||||
|
||||
ci := C.int(i)
|
||||
|
||||
name := fmt.Sprintf("in_%d", i)
|
||||
cname := C.CString(name)
|
||||
|
||||
fmt.Printf("args: %s\n", args)
|
||||
|
||||
if ret = int(C.gmf_create_filter(
|
||||
f.bufferCtx,
|
||||
C.avfilter_get_by_name(C.CString("buffer")),
|
||||
cname,
|
||||
cargs,
|
||||
nil,
|
||||
f.filterGraph,
|
||||
ci)); ret < 0 {
|
||||
return nil, fmt.Errorf("error creating filter 'buffer' - %s", AvError(ret))
|
||||
if last, ret = f.create("buffer", fmt.Sprintf("in_%d", i), args); ret < 0 {
|
||||
return f, fmt.Errorf("error creating input buffer - %s", AvError(ret))
|
||||
}
|
||||
|
||||
last = C.gmf_get_current(f.bufferCtx, ci)
|
||||
f.bufferCtx = append(f.bufferCtx, last)
|
||||
|
||||
if ret = int(C.avfilter_link(last, 0, curr.filter_ctx, C.uint(i))); ret < 0 {
|
||||
return nil, fmt.Errorf("error linking filters - %s", AvError(ret))
|
||||
return f, fmt.Errorf("error linking filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_graph_create_filter(
|
||||
&f.sinkCtx,
|
||||
C.avfilter_get_by_name(C.CString("buffersink")),
|
||||
cnameOut,
|
||||
nil,
|
||||
nil,
|
||||
f.filterGraph)); ret < 0 {
|
||||
return nil, fmt.Errorf("error creating filter 'buffersink' - %s", AvError(ret))
|
||||
if f.sinkCtx, ret = f.create("buffersink", "out", ""); ret < 0 {
|
||||
return f, fmt.Errorf("error creating filter 'buffersink' - %s", AvError(ret))
|
||||
}
|
||||
|
||||
// XXX PIXFMT!
|
||||
if ret = int(C.avfilter_graph_create_filter(
|
||||
&format,
|
||||
C.avfilter_get_by_name(C.CString("format")),
|
||||
C.CString("format"),
|
||||
C.CString("yuv420p"),
|
||||
nil,
|
||||
f.filterGraph)); ret < 0 {
|
||||
return nil, fmt.Errorf("error creating filter 'buffer' - %s", AvError(ret))
|
||||
// XXX hardcoded PIXFMT!
|
||||
if last, ret = f.create("format", "format", "yuv420p"); ret < 0 {
|
||||
return f, fmt.Errorf("error creating format filter - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_link(outputs.filter_ctx, 0, format, 0)); ret < 0 {
|
||||
return nil, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
if ret = int(C.avfilter_link(outputs.filter_ctx, 0, last, 0)); ret < 0 {
|
||||
return f, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_link(format, 0, f.sinkCtx, 0)); ret < 0 {
|
||||
return nil, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
if ret = int(C.avfilter_link(last, 0, f.sinkCtx, 0)); ret < 0 {
|
||||
return f, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_graph_config(f.filterGraph, nil)); ret < 0 {
|
||||
return nil, fmt.Errorf("graph config error - %s", AvError(ret))
|
||||
return f, fmt.Errorf("graph config error - %s", AvError(ret))
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", C.GoString(C.avfilter_graph_dump(f.filterGraph, nil)))
|
||||
|
||||
C.avfilter_inout_free(&inputs)
|
||||
C.avfilter_inout_free(&outputs)
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Filter) AddFrame(frame *Frame, istIdx int) error {
|
||||
func (f *Filter) create(filter, name, args string) (*_Ctype_AVFilterContext, int) {
|
||||
var (
|
||||
ctx *_Ctype_AVFilterContext
|
||||
ret int
|
||||
)
|
||||
|
||||
cfilter := C.CString(filter)
|
||||
cname := C.CString(name)
|
||||
cargs := C.CString(args)
|
||||
|
||||
ret = int(C.avfilter_graph_create_filter(
|
||||
&ctx,
|
||||
C.avfilter_get_by_name(cfilter),
|
||||
cname,
|
||||
cargs,
|
||||
nil,
|
||||
f.filterGraph))
|
||||
|
||||
C.free(unsafe.Pointer(cfilter))
|
||||
C.free(unsafe.Pointer(cname))
|
||||
C.free(unsafe.Pointer(cargs))
|
||||
|
||||
return ctx, ret
|
||||
}
|
||||
|
||||
func (f *Filter) AddFrame(frame *Frame, istIdx int, flag int) error {
|
||||
var ret int
|
||||
|
||||
fmt.Printf("AddFrame: i=%d, width=%d, height=%d\n", istIdx, frame.Width(), frame.Height())
|
||||
if istIdx >= len(f.bufferCtx) {
|
||||
return fmt.Errorf("unexpected stream index #%d", istIdx)
|
||||
}
|
||||
|
||||
if ret = int(C.gmf_av_buffersrc_add_frame_flags(
|
||||
f.bufferCtx,
|
||||
if ret = int(C.av_buffersrc_add_frame_flags(
|
||||
f.bufferCtx[istIdx],
|
||||
frame.avFrame,
|
||||
AV_BUFFERSRC_FLAG_PUSH,
|
||||
C.int(istIdx)),
|
||||
C.int(flag)),
|
||||
); ret < 0 {
|
||||
return AvError(ret)
|
||||
}
|
||||
@@ -172,6 +165,16 @@ func (f *Filter) AddFrame(frame *Frame, istIdx int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) Close(istIdx int) error {
|
||||
var ret int
|
||||
|
||||
if ret = int(C.av_buffersrc_close(f.bufferCtx[istIdx], 0, AV_BUFFERSRC_FLAG_PUSH)); ret < 0 {
|
||||
return AvError(ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) GetFrame() ([]*Frame, error) {
|
||||
var (
|
||||
ret int
|
||||
@@ -182,7 +185,6 @@ func (f *Filter) GetFrame() ([]*Frame, error) {
|
||||
frame := NewFrame()
|
||||
|
||||
ret = int(C.av_buffersink_get_frame_flags(f.sinkCtx, frame.avFrame, AV_BUFFERSINK_FLAG_NO_REQUEST))
|
||||
fmt.Printf("ret=%d\n", ret)
|
||||
if AvErrno(ret) == syscall.EAGAIN || ret == AVERROR_EOF {
|
||||
frame.Free()
|
||||
break
|
||||
@@ -194,26 +196,9 @@ func (f *Filter) GetFrame() ([]*Frame, error) {
|
||||
result = append(result, frame)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
f.RequestOldest()
|
||||
|
||||
func (f *Filter) GetFrame2() ([]*Frame, int) {
|
||||
var (
|
||||
ret int
|
||||
result []*Frame = make([]*Frame, 0)
|
||||
)
|
||||
|
||||
for {
|
||||
frame := NewFrame()
|
||||
|
||||
if ret = int(C.av_buffersink_get_frame_flags(f.sinkCtx, frame.avFrame, AV_BUFFERSINK_FLAG_NO_REQUEST)); ret < 0 {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
result = append(result, frame)
|
||||
}
|
||||
|
||||
return result, ret
|
||||
return result, AvError(ret)
|
||||
}
|
||||
|
||||
func (f *Filter) RequestOldest() error {
|
||||
@@ -225,3 +210,19 @@ func (f *Filter) RequestOldest() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) Dump() {
|
||||
fmt.Println(C.GoString(C.avfilter_graph_dump(f.filterGraph, nil)))
|
||||
}
|
||||
|
||||
func (f *Filter) Release() {
|
||||
if f.sinkCtx != nil {
|
||||
C.avfilter_free(f.sinkCtx)
|
||||
}
|
||||
|
||||
for i, _ := range f.bufferCtx {
|
||||
C.avfilter_free(f.bufferCtx[i])
|
||||
}
|
||||
|
||||
C.avfilter_graph_free(&f.filterGraph)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user