// TODO Fix https://github.com/asticode/go-astiav/actions/runs/5853322732/job/15867145888 //go:build !windows package astiav import ( "fmt" "strconv" "testing" "github.com/stretchr/testify/require" ) func TestFilterGraph(t *testing.T) { fg1 := AllocFilterGraph() require.NotNil(t, fg1) defer fg1.Free() cl := fg1.Class() require.NotNil(t, cl) require.Equal(t, "AVFilterGraph", cl.Name()) fg1.SetThreadCount(2) require.Equal(t, 2, fg1.ThreadCount()) fg1.SetThreadType(ThreadTypeSlice) require.Equal(t, ThreadTypeSlice, fg1.ThreadType()) type command struct { args string cmd string flags FilterCommandFlags resp string target string withError bool } type link struct { channelLayout ChannelLayout colorRange ColorRange colorSpace ColorSpace frameRate Rational height int mediaType MediaType pixelFormat PixelFormat sampleAspectRatio Rational sampleFormat SampleFormat sampleRate int timeBase Rational width int } type graph struct { buffersinkExpectedInput link buffersinkName string buffersrcName string commands []command content string s string sources []FilterArgs } for _, v := range []graph{ { buffersinkExpectedInput: link{ colorRange: ColorRangeUnspecified, colorSpace: ColorSpaceUnspecified, frameRate: NewRational(4, 1), height: 8, mediaType: MediaTypeVideo, pixelFormat: PixelFormatYuv420P, sampleAspectRatio: NewRational(2, 1), timeBase: NewRational(1, 4), width: 4, }, buffersinkName: "buffersink", buffersrcName: "buffer", commands: []command{ { args: "a", cmd: "invalid", flags: NewFilterCommandFlags(), target: "scale", withError: true, }, { args: "4", cmd: "width", flags: NewFilterCommandFlags().Add(FilterCommandFlagOne), target: "scale", }, }, content: "[input_1]scale=4x8,settb=1/4,fps=fps=4/1,format=pix_fmts=yuv420p,setsar=2/1", s: " +--------------+\nParsed_setsar_4:default--[4x8 2:1 yuv420p]--default| filter_out |\n | (buffersink) |\n +--------------+\n\n+-------------+\n| filter_in_1 |default--[2x4 1:2 rgba]--Parsed_scale_0:default\n| (buffer) |\n+-------------+\n\n +----------------+\nfilter_in_1:default--[2x4 1:2 rgba]--default| Parsed_scale_0 |default--[4x8 1:2 yuv420p]--Parsed_settb_1:default\n | (scale) |\n +----------------+\n\n +----------------+\nParsed_scale_0:default--[4x8 1:2 yuv420p]--default| Parsed_settb_1 |default--[4x8 1:2 yuv420p]--Parsed_fps_2:default\n | (settb) |\n +----------------+\n\n +--------------+\nParsed_settb_1:default--[4x8 1:2 yuv420p]--default| Parsed_fps_2 |default--[4x8 1:2 yuv420p]--Parsed_format_3:default\n | (fps) |\n +--------------+\n\n +-----------------+\nParsed_fps_2:default--[4x8 1:2 yuv420p]--default| Parsed_format_3 |default--[4x8 1:2 yuv420p]--Parsed_setsar_4:default\n | (format) |\n +-----------------+\n\n +-----------------+\nParsed_format_3:default--[4x8 1:2 yuv420p]--default| Parsed_setsar_4 |default--[4x8 2:1 yuv420p]--filter_out:default\n | (setsar) |\n +-----------------+\n\n", sources: []FilterArgs{ { "height": "4", "pix_fmt": strconv.Itoa(int(PixelFormatRgba)), "sar": "1/2", "time_base": "1/2", "width": "2", }, }, }, { buffersinkExpectedInput: link{ channelLayout: ChannelLayoutStereo, mediaType: MediaTypeAudio, sampleFormat: SampleFormatS16, sampleRate: 3, timeBase: NewRational(1, 4), }, buffersinkName: "abuffersink", buffersrcName: "abuffer", content: "[input_1]aformat=sample_fmts=s16:channel_layouts=stereo:sample_rates=3,asettb=1/4", s: " +---------------+\nParsed_asettb_1:default--[3Hz s16:stereo]--default| filter_out |\n | (abuffersink) |\n +---------------+\n\n+-------------+\n| filter_in_1 |default--[2Hz fltp:mono]--auto_aresample_0:default\n| (abuffer) |\n+-------------+\n\n +------------------+\nauto_aresample_0:default--[3Hz s16:stereo]--default| Parsed_aformat_0 |default--[3Hz s16:stereo]--Parsed_asettb_1:default\n | (aformat) |\n +------------------+\n\n +-----------------+\nParsed_aformat_0:default--[3Hz s16:stereo]--default| Parsed_asettb_1 |default--[3Hz s16:stereo]--filter_out:default\n | (asettb) |\n +-----------------+\n\n +------------------+\nfilter_in_1:default--[2Hz fltp:mono]--default| auto_aresample_0 |default--[3Hz s16:stereo]--Parsed_aformat_0:default\n | (aresample) |\n +------------------+\n\n", sources: []FilterArgs{ { "channel_layout": ChannelLayoutMono.String(), "sample_fmt": strconv.Itoa(int(SampleFormatFltp)), "sample_rate": "2", "time_base": "1/2", }, }, }, } { fg := AllocFilterGraph() require.NotNil(t, fg) defer fg.Free() buffersrc := FindFilterByName(v.buffersrcName) require.NotNil(t, buffersrc) buffersink := FindFilterByName(v.buffersinkName) require.NotNil(t, buffersink) buffersinkContext, err := fg.NewFilterContext(buffersink, "filter_out", nil) require.NoError(t, err) cl = buffersinkContext.Class() require.NotNil(t, cl) require.Equal(t, "AVFilter", cl.Name()) inputs := AllocFilterInOut() defer inputs.Free() inputs.SetName("out") inputs.SetFilterContext(buffersinkContext) inputs.SetPadIdx(0) inputs.SetNext(nil) var outputs *FilterInOut defer func() { if outputs != nil { outputs.Free() } }() var buffersrcContexts []*FilterContext for idx, src := range v.sources { buffersrcContext, err := fg.NewFilterContext(buffersrc, fmt.Sprintf("filter_in_%d", idx+1), src) require.NoError(t, err) buffersrcContexts = append(buffersrcContexts, buffersrcContext) o := AllocFilterInOut() o.SetName(fmt.Sprintf("input_%d", idx+1)) o.SetFilterContext(buffersrcContext) o.SetPadIdx(0) o.SetNext(outputs) outputs = o } require.NoError(t, fg.Parse(v.content, inputs, outputs)) require.NoError(t, fg.Configure()) require.Equal(t, 1, buffersinkContext.NbInputs()) links := buffersinkContext.Inputs() require.Equal(t, 1, len(links)) e, g := v.buffersinkExpectedInput, links[0] require.Equal(t, e.frameRate, g.FrameRate()) require.Equal(t, e.mediaType, g.MediaType()) require.Equal(t, e.timeBase, g.TimeBase()) switch e.mediaType { case MediaTypeAudio: require.True(t, e.channelLayout.Equal(g.ChannelLayout())) require.Equal(t, e.sampleFormat, g.SampleFormat()) require.Equal(t, e.sampleRate, g.SampleRate()) default: require.Equal(t, e.colorRange, g.ColorRange()) require.Equal(t, e.colorSpace, g.ColorSpace()) require.Equal(t, e.height, g.Height()) require.Equal(t, e.pixelFormat, g.PixelFormat()) require.Equal(t, e.sampleAspectRatio, g.SampleAspectRatio()) require.Equal(t, e.width, g.Width()) } for _, buffersrcContext := range buffersrcContexts { require.Equal(t, 0, buffersrcContext.NbInputs()) require.Equal(t, 1, buffersrcContext.NbOutputs()) links := buffersrcContext.Outputs() require.Equal(t, 1, len(links)) require.Equal(t, v.buffersinkExpectedInput.mediaType, links[0].MediaType()) } require.Equal(t, v.s, fg.String()) for _, command := range v.commands { resp, err := fg.SendCommand(command.target, command.cmd, command.args, command.flags) if command.withError { require.Error(t, err) } else { require.NoError(t, err) } require.Equal(t, command.resp, resp) } } fg2 := AllocFilterGraph() require.NotNil(t, fg2) defer fg2.Free() fgs, err := fg2.ParseSegment("anullsrc") require.NoError(t, err) defer fgs.Free() require.Equal(t, 1, fgs.NbChains()) cs := fgs.Chains() require.Equal(t, 1, len(cs)) require.Equal(t, 1, cs[0].NbFilters()) fs := cs[0].Filters() require.Equal(t, 1, len(fs)) f := FindFilterByName(fs[0].FilterName()) require.NotNil(t, f) require.Equal(t, 0, f.NbInputs()) require.Equal(t, 1, f.NbOutputs()) os := f.Outputs() require.Equal(t, 1, len(os)) require.Equal(t, MediaTypeAudio, os[0].MediaType()) // TODO Test BuffersrcAddFrame // TODO Test BuffersinkGetFrame }