Files
gmf/filter_go112.go
2019-03-21 21:54:07 +03:00

214 lines
4.6 KiB
Go

// +build go1.12
package gmf
/*
#cgo pkg-config: libavfilter
#include <stdio.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/pixdesc.h>
*/
import "C"
import (
"fmt"
"syscall"
"unsafe"
)
const (
AV_BUFFERSINK_FLAG_PEEK = 1
AV_BUFFERSINK_FLAG_NO_REQUEST = 2
AV_BUFFERSRC_FLAG_PUSH = 4
)
type Filter struct {
bufferCtx []*C.AVFilterContext
sinkCtx *C.AVFilterContext
filterGraph *C.AVFilterGraph
}
func NewFilter(desc string, srcStreams []*Stream, ost *Stream, options []*Option) (*Filter, error) {
f := &Filter{
filterGraph: C.avfilter_graph_alloc(),
bufferCtx: make([]*C.AVFilterContext, 0),
}
var (
ret, i int
args string
inputs *C.AVFilterInOut
outputs *C.AVFilterInOut
curr *C.AVFilterInOut
last *C.AVFilterContext
)
cdesc := C.CString(desc)
defer C.free(unsafe.Pointer(cdesc))
if ret = int(C.avfilter_graph_parse2(
f.filterGraph,
cdesc,
&inputs,
&outputs,
)); ret < 0 {
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")
}
src := srcStreams[i]
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())
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))
}
f.bufferCtx = append(f.bufferCtx, last)
if ret = int(C.avfilter_link(last, 0, curr.filter_ctx, C.uint(i))); ret < 0 {
return f, fmt.Errorf("error linking filters - %s", AvError(ret))
}
i++
}
if f.sinkCtx, ret = f.create("buffersink", "out", ""); ret < 0 {
return f, fmt.Errorf("error creating filter 'buffersink' - %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, last, 0)); ret < 0 {
return f, 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 f, fmt.Errorf("graph config error - %s", AvError(ret))
}
return f, nil
}
func (f *Filter) create(filter, name, args string) (*C.AVFilterContext, int) {
var (
ctx *C.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
if istIdx >= len(f.bufferCtx) {
return fmt.Errorf("unexpected stream index #%d", istIdx)
}
if ret = int(C.av_buffersrc_add_frame_flags(
f.bufferCtx[istIdx],
frame.avFrame,
C.int(flag)),
); ret < 0 {
return AvError(ret)
}
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
result []*Frame = make([]*Frame, 0)
)
for {
frame := NewFrame()
ret = int(C.av_buffersink_get_frame_flags(f.sinkCtx, frame.avFrame, AV_BUFFERSINK_FLAG_NO_REQUEST))
if AvErrno(ret) == syscall.EAGAIN || ret == AVERROR_EOF {
frame.Free()
break
} else if ret < 0 {
frame.Free()
return nil, AvError(ret)
}
result = append(result, frame)
}
f.RequestOldest()
return result, AvError(ret)
}
func (f *Filter) RequestOldest() error {
var ret int
if ret = int(C.avfilter_graph_request_oldest(f.filterGraph)); ret < 0 {
return AvError(ret)
}
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)
}