Removed FilterLink and added BuffersinkFilterContext and BuffersrcFilterContext

This commit is contained in:
Quentin Renard
2024-10-18 14:57:22 +02:00
parent c8b4cbec66
commit cd2a16de95
8 changed files with 162 additions and 163 deletions

4
BREAKING_CHANGES.md Normal file
View File

@@ -0,0 +1,4 @@
# v0.24.0
- use `FilterGraph`.`NewBuffersinkFilterContext` and `FilterGraph`.`NewBuffersrcFilterContext` instead of `FilterGraph`.`NewFilterContext` when creating `buffersink` and `buffersrc` filter contexts and use `BuffersinkFilterContext`.`GetFrame` and `BuffersrcFilterContext`.`AddFrame` to manipulate them. Use `BuffersinkFilterContext`.`FilterContext` and `BuffersrcFilterContext`.`FilterContext` in `FilterInOut`.`SetFilterContext`.
- `FilterLink` has been removed and methods like `BuffersinkFilterContext`.`ChannelLayout` have been added instead

View File

@@ -58,3 +58,7 @@ export PKG_CONFIG_PATH="{{ path to your working directory }}/tmp/n7.0/lib/pkgcon
# Why astiav?
After maintaining for several years the most starred [fork](https://github.com/asticode/goav) of [goav](https://github.com/giorgisio/goav), I've decided to write from scratch my own C bindings to fix most of the problems I still encountered using `goav`.
# Breaking changes
You can see the list of breaking changes [here](BREAKING_CHANGES.md).

View File

@@ -23,8 +23,8 @@ var (
)
type stream struct {
buffersinkContext *astiav.FilterContext
buffersrcContext *astiav.FilterContext
buffersinkContext *astiav.BuffersinkFilterContext
buffersrcContext *astiav.BuffersrcFilterContext
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
decFrame *astiav.Frame
@@ -232,7 +232,7 @@ func initFilter() (err error) {
}
// Create filter contexts
if s.buffersrcContext, err = s.filterGraph.NewFilterContext(buffersrc, "in", astiav.FilterArgs{
if s.buffersrcContext, err = s.filterGraph.NewBuffersrcFilterContext(buffersrc, "in", astiav.FilterArgs{
"pix_fmt": strconv.Itoa(int(s.decCodecContext.PixelFormat())),
"pixel_aspect": s.decCodecContext.SampleAspectRatio().String(),
"time_base": s.inputStream.TimeBase().String(),
@@ -241,20 +241,20 @@ func initFilter() (err error) {
err = fmt.Errorf("main: creating buffersrc context failed: %w", err)
return
}
if s.buffersinkContext, err = s.filterGraph.NewFilterContext(buffersink, "in", nil); err != nil {
if s.buffersinkContext, err = s.filterGraph.NewBuffersinkFilterContext(buffersink, "in", nil); err != nil {
err = fmt.Errorf("main: creating buffersink context failed: %w", err)
return
}
// Update outputs
outputs.SetName("in")
outputs.SetFilterContext(s.buffersrcContext)
outputs.SetFilterContext(s.buffersrcContext.FilterContext())
outputs.SetPadIdx(0)
outputs.SetNext(nil)
// Update inputs
inputs.SetName("out")
inputs.SetFilterContext(s.buffersinkContext)
inputs.SetFilterContext(s.buffersinkContext.FilterContext())
inputs.SetPadIdx(0)
inputs.SetNext(nil)
@@ -278,7 +278,7 @@ func initFilter() (err error) {
func filterFrame(f *astiav.Frame, s *stream) (err error) {
// Add frame
if err = s.buffersrcContext.BuffersrcAddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
if err = s.buffersrcContext.AddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
err = fmt.Errorf("main: adding frame failed: %w", err)
return
}
@@ -289,7 +289,7 @@ func filterFrame(f *astiav.Frame, s *stream) (err error) {
s.filterFrame.Unref()
// Get frame
if err = s.buffersinkContext.BuffersinkGetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if err = s.buffersinkContext.GetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
err = nil
break

View File

@@ -25,8 +25,8 @@ var (
)
type stream struct {
buffersinkContext *astiav.FilterContext
buffersrcContext *astiav.FilterContext
buffersinkContext *astiav.BuffersinkFilterContext
buffersrcContext *astiav.BuffersrcFilterContext
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
decFrame *astiav.Frame
@@ -395,24 +395,24 @@ func initFilters() (err error) {
}
// Create filter contexts
if s.buffersrcContext, err = s.filterGraph.NewFilterContext(buffersrc, "in", args); err != nil {
if s.buffersrcContext, err = s.filterGraph.NewBuffersrcFilterContext(buffersrc, "in", args); err != nil {
err = fmt.Errorf("main: creating buffersrc context failed: %w", err)
return
}
if s.buffersinkContext, err = s.filterGraph.NewFilterContext(buffersink, "out", nil); err != nil {
if s.buffersinkContext, err = s.filterGraph.NewBuffersinkFilterContext(buffersink, "out", nil); err != nil {
err = fmt.Errorf("main: creating buffersink context failed: %w", err)
return
}
// Update outputs
outputs.SetName("in")
outputs.SetFilterContext(s.buffersrcContext)
outputs.SetFilterContext(s.buffersrcContext.FilterContext())
outputs.SetPadIdx(0)
outputs.SetNext(nil)
// Update inputs
inputs.SetName("out")
inputs.SetFilterContext(s.buffersinkContext)
inputs.SetFilterContext(s.buffersinkContext.FilterContext())
inputs.SetPadIdx(0)
inputs.SetNext(nil)
@@ -441,7 +441,7 @@ func initFilters() (err error) {
func filterEncodeWriteFrame(f *astiav.Frame, s *stream) (err error) {
// Add frame
if err = s.buffersrcContext.BuffersrcAddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
if err = s.buffersrcContext.AddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
err = fmt.Errorf("main: adding frame failed: %w", err)
return
}
@@ -452,7 +452,7 @@ func filterEncodeWriteFrame(f *astiav.Frame, s *stream) (err error) {
s.filterFrame.Unref()
// Get frame
if err = s.buffersinkContext.BuffersinkGetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if err = s.buffersinkContext.GetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
err = nil
break

View File

@@ -6,7 +6,6 @@ package astiav
//#include <libavutil/frame.h>
import "C"
import (
"math"
"unsafe"
)
@@ -38,46 +37,96 @@ func (fc *FilterContext) Free() {
}
}
func (fc *FilterContext) BuffersrcAddFrame(f *Frame, fs BuffersrcFlags) error {
var cf *C.AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersrc_add_frame_flags(fc.c, cf, C.int(fs)))
}
func (fc *FilterContext) BuffersinkGetFrame(f *Frame, fs BuffersinkFlags) error {
var cf *C.AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersink_get_frame_flags(fc.c, cf, C.int(fs)))
}
func (fc *FilterContext) Class() *Class {
return newClassFromC(unsafe.Pointer(fc.c))
}
func (fc *FilterContext) NbInputs() int {
return int(fc.c.nb_inputs)
type BuffersinkFilterContext struct {
fc *FilterContext
}
func (fc *FilterContext) NbOutputs() int {
return int(fc.c.nb_outputs)
func newBuffersinkFilterContext(fc *FilterContext) *BuffersinkFilterContext {
return &BuffersinkFilterContext{fc: fc}
}
func (fc *FilterContext) Inputs() (ls []*FilterLink) {
lcs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.AVFilterLink)(nil))](*C.AVFilterLink))(unsafe.Pointer(fc.c.inputs))
for i := 0; i < fc.NbInputs(); i++ {
ls = append(ls, newFilterLinkFromC(lcs[i]))
}
return
func (bfc *BuffersinkFilterContext) ChannelLayout() ChannelLayout {
var cl C.AVChannelLayout
C.av_buffersink_get_ch_layout(bfc.fc.c, &cl)
return newChannelLayoutFromC(&cl)
}
func (fc *FilterContext) Outputs() (ls []*FilterLink) {
lcs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.AVFilterLink)(nil))](*C.AVFilterLink))(unsafe.Pointer(fc.c.outputs))
for i := 0; i < fc.NbOutputs(); i++ {
ls = append(ls, newFilterLinkFromC(lcs[i]))
func (bfc *BuffersinkFilterContext) ColorRange() ColorRange {
return ColorRange(C.av_buffersink_get_color_range(bfc.fc.c))
}
return
func (bfc *BuffersinkFilterContext) ColorSpace() ColorSpace {
return ColorSpace(C.av_buffersink_get_colorspace(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) FilterContext() *FilterContext {
return bfc.fc
}
func (bfc *BuffersinkFilterContext) FrameRate() Rational {
return newRationalFromC(C.av_buffersink_get_frame_rate(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) GetFrame(f *Frame, fs BuffersinkFlags) error {
var cf *C.AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersink_get_frame_flags(bfc.fc.c, cf, C.int(fs)))
}
func (bfc *BuffersinkFilterContext) Height() int {
return int(C.av_buffersink_get_h(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) MediaType() MediaType {
return MediaType(C.av_buffersink_get_type(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) PixelFormat() PixelFormat {
return PixelFormat(C.av_buffersink_get_format(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) SampleAspectRatio() Rational {
return newRationalFromC(C.av_buffersink_get_sample_aspect_ratio(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) SampleFormat() SampleFormat {
return SampleFormat(C.av_buffersink_get_format(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) SampleRate() int {
return int(C.av_buffersink_get_sample_rate(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) TimeBase() Rational {
return newRationalFromC(C.av_buffersink_get_time_base(bfc.fc.c))
}
func (bfc *BuffersinkFilterContext) Width() int {
return int(C.av_buffersink_get_w(bfc.fc.c))
}
type BuffersrcFilterContext struct {
fc *FilterContext
}
func newBuffersrcFilterContext(fc *FilterContext) *BuffersrcFilterContext {
return &BuffersrcFilterContext{fc: fc}
}
func (bfc *BuffersrcFilterContext) AddFrame(f *Frame, fs BuffersrcFlags) error {
var cf *C.AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersrc_add_frame_flags(bfc.fc.c, cf, C.int(fs)))
}
func (bfc *BuffersrcFilterContext) FilterContext() *FilterContext {
return bfc.fc
}

View File

@@ -103,6 +103,22 @@ func (g *FilterGraph) NewFilterContext(f *Filter, name string, args FilterArgs)
return fc, nil
}
func (g *FilterGraph) NewBuffersinkFilterContext(f *Filter, name string, args FilterArgs) (*BuffersinkFilterContext, error) {
fc, err := g.NewFilterContext(f, name, args)
if err != nil {
return nil, err
}
return newBuffersinkFilterContext(fc), nil
}
func (g *FilterGraph) NewBuffersrcFilterContext(f *Filter, name string, args FilterArgs) (*BuffersrcFilterContext, error) {
fc, err := g.NewFilterContext(f, name, args)
if err != nil {
return nil, err
}
return newBuffersrcFilterContext(fc), nil
}
func (g *FilterGraph) Parse(content string, inputs, outputs *FilterInOut) error {
cc := C.CString(content)
defer C.free(unsafe.Pointer(cc))

View File

@@ -31,13 +31,14 @@ func TestFilterGraph(t *testing.T) {
target string
withError bool
}
type link struct {
type buffersink struct {
channelLayout ChannelLayout
colorRange ColorRange
colorSpace ColorSpace
frameRate Rational
height int
mediaType MediaType
name string
pixelFormat PixelFormat
sampleAspectRatio Rational
sampleFormat SampleFormat
@@ -45,10 +46,12 @@ func TestFilterGraph(t *testing.T) {
timeBase Rational
width int
}
type buffersrc struct {
name string
}
type graph struct {
buffersinkExpectedInput link
buffersinkName string
buffersrcName string
buffersink buffersink
buffersrc buffersrc
commands []command
content string
s string
@@ -56,19 +59,19 @@ func TestFilterGraph(t *testing.T) {
}
for _, v := range []graph{
{
buffersinkExpectedInput: link{
buffersink: buffersink{
colorRange: ColorRangeUnspecified,
colorSpace: ColorSpaceUnspecified,
frameRate: NewRational(4, 1),
height: 8,
mediaType: MediaTypeVideo,
name: "buffersink",
pixelFormat: PixelFormatYuv420P,
sampleAspectRatio: NewRational(2, 1),
timeBase: NewRational(1, 4),
width: 4,
},
buffersinkName: "buffersink",
buffersrcName: "buffer",
buffersrc: buffersrc{name: "buffer"},
commands: []command{
{
args: "a",
@@ -97,15 +100,15 @@ func TestFilterGraph(t *testing.T) {
},
},
{
buffersinkExpectedInput: link{
buffersink: buffersink{
channelLayout: ChannelLayoutStereo,
mediaType: MediaTypeAudio,
name: "abuffersink",
sampleFormat: SampleFormatS16,
sampleRate: 3,
timeBase: NewRational(1, 4),
},
buffersinkName: "abuffersink",
buffersrcName: "abuffer",
buffersrc: buffersrc{name: "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{
@@ -122,21 +125,21 @@ func TestFilterGraph(t *testing.T) {
require.NotNil(t, fg)
defer fg.Free()
buffersrc := FindFilterByName(v.buffersrcName)
buffersrc := FindFilterByName(v.buffersrc.name)
require.NotNil(t, buffersrc)
buffersink := FindFilterByName(v.buffersinkName)
buffersink := FindFilterByName(v.buffersink.name)
require.NotNil(t, buffersink)
buffersinkContext, err := fg.NewFilterContext(buffersink, "filter_out", nil)
buffersinkContext, err := fg.NewBuffersinkFilterContext(buffersink, "filter_out", nil)
require.NoError(t, err)
cl = buffersinkContext.Class()
cl = buffersinkContext.FilterContext().Class()
require.NotNil(t, cl)
require.Equal(t, "AVFilter", cl.Name())
inputs := AllocFilterInOut()
defer inputs.Free()
inputs.SetName("out")
inputs.SetFilterContext(buffersinkContext)
inputs.SetFilterContext(buffersinkContext.FilterContext())
inputs.SetPadIdx(0)
inputs.SetNext(nil)
@@ -147,15 +150,15 @@ func TestFilterGraph(t *testing.T) {
}
}()
var buffersrcContexts []*FilterContext
var buffersrcContexts []*BuffersrcFilterContext
for idx, src := range v.sources {
buffersrcContext, err := fg.NewFilterContext(buffersrc, fmt.Sprintf("filter_in_%d", idx+1), src)
buffersrcContext, err := fg.NewBuffersrcFilterContext(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.SetFilterContext(buffersrcContext.FilterContext())
o.SetPadIdx(0)
o.SetNext(outputs)
@@ -165,33 +168,21 @@ func TestFilterGraph(t *testing.T) {
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 {
require.Equal(t, v.buffersink.frameRate, buffersinkContext.FrameRate())
require.Equal(t, v.buffersink.mediaType, buffersinkContext.MediaType())
require.Equal(t, v.buffersink.timeBase, buffersinkContext.TimeBase())
switch v.buffersink.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())
require.True(t, v.buffersink.channelLayout.Equal(buffersinkContext.ChannelLayout()))
require.Equal(t, v.buffersink.sampleFormat, buffersinkContext.SampleFormat())
require.Equal(t, v.buffersink.sampleRate, buffersinkContext.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.buffersink.colorRange, buffersinkContext.ColorRange())
require.Equal(t, v.buffersink.colorSpace, buffersinkContext.ColorSpace())
require.Equal(t, v.buffersink.height, buffersinkContext.Height())
require.Equal(t, v.buffersink.pixelFormat, buffersinkContext.PixelFormat())
require.Equal(t, v.buffersink.sampleAspectRatio, buffersinkContext.SampleAspectRatio())
require.Equal(t, v.buffersink.width, buffersinkContext.Width())
}
require.Equal(t, v.s, fg.String())

View File

@@ -1,65 +0,0 @@
package astiav
//#include <libavfilter/avfilter.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L471
type FilterLink struct {
c *C.AVFilterLink
}
func newFilterLinkFromC(c *C.AVFilterLink) *FilterLink {
if c == nil {
return nil
}
return &FilterLink{c: c}
}
func (l *FilterLink) ChannelLayout() ChannelLayout {
v, _ := newChannelLayoutFromC(&l.c.ch_layout).clone()
return v
}
func (l *FilterLink) ColorRange() ColorRange {
return ColorRange(l.c.color_range)
}
func (l *FilterLink) ColorSpace() ColorSpace {
return ColorSpace(l.c.colorspace)
}
func (l *FilterLink) FrameRate() Rational {
return newRationalFromC(l.c.frame_rate)
}
func (l *FilterLink) Height() int {
return int(l.c.h)
}
func (l *FilterLink) MediaType() MediaType {
return MediaType(l.c._type)
}
func (l *FilterLink) PixelFormat() PixelFormat {
return PixelFormat(l.c.format)
}
func (l *FilterLink) SampleAspectRatio() Rational {
return newRationalFromC(l.c.sample_aspect_ratio)
}
func (l *FilterLink) SampleFormat() SampleFormat {
return SampleFormat(l.c.format)
}
func (l *FilterLink) SampleRate() int {
return int(l.c.sample_rate)
}
func (l *FilterLink) TimeBase() Rational {
return newRationalFromC(l.c.time_base)
}
func (l *FilterLink) Width() int {
return int(l.c.w)
}