diff --git a/format_context.go b/format_context.go index 598da91..007cb43 100644 --- a/format_context.go +++ b/format_context.go @@ -114,6 +114,10 @@ func (fc *FormatContext) SetMetadata(d *Dictionary) { } } +func (fc *FormatContext) NbPrograms() int { + return int(fc.c.nb_programs) +} + func (fc *FormatContext) NbStreams() int { return int(fc.c.nb_streams) } @@ -133,6 +137,14 @@ func (fc *FormatContext) Pb() *IOContext { return newIOContextFromC(fc.c.pb) } +func (fc *FormatContext) Programs() (ps []*Program) { + pcs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.struct_AVProgram)(nil))](*C.struct_AVProgram))(unsafe.Pointer(fc.c.programs)) + for i := 0; i < fc.NbPrograms(); i++ { + ps = append(ps, newProgramFromC(pcs[i], fc)) + } + return +} + func (fc *FormatContext) SetPb(i *IOContext) { fc.c.pb = i.c } @@ -190,6 +202,10 @@ func (fc *FormatContext) CloseInput() { } } +func (fc *FormatContext) NewProgram(id int) *Program { + return newProgramFromC(C.av_new_program(fc.c, C.int(id)), fc) +} + func (fc *FormatContext) NewStream(c *Codec) *Stream { var cc *C.struct_AVCodec if c != nil { diff --git a/format_context_test.go b/format_context_test.go index 79ba427..a1e093e 100644 --- a/format_context_test.go +++ b/format_context_test.go @@ -74,6 +74,15 @@ func TestFormatContext(t *testing.T) { fc4.SetInterruptCallback().Interrupt() require.ErrorIs(t, fc4.OpenInput("testdata/video.mp4", nil, nil), ErrExit) + fc5 := AllocFormatContext() + require.NotNil(t, fc5) + defer fc5.Free() + require.NotNil(t, fc5.NewProgram(1)) + require.Equal(t, 1, fc5.NbPrograms()) + ps := fc5.Programs() + require.Equal(t, 1, len(ps)) + require.Equal(t, 1, ps[0].ID()) + // TODO Test ReadFrame // TODO Test SeekFrame // TODO Test Flush diff --git a/program.go b/program.go new file mode 100644 index 0000000..0e7b2ce --- /dev/null +++ b/program.go @@ -0,0 +1,53 @@ +package astiav + +//#cgo pkg-config: libavformat +//#include +import "C" +import ( + "unsafe" +) + +// https://github.com/FFmpeg/FFmpeg/blob/n7.0/libavformat/avformat.h#L1181 +type Program struct { + c *C.struct_AVProgram + fc *FormatContext +} + +func newProgramFromC(c *C.struct_AVProgram, fc *FormatContext) *Program { + if c == nil { + return nil + } + return &Program{ + c: c, + fc: fc, + } +} + +func (p *Program) AddStream(s *Stream) { + C.av_program_add_stream_index(p.fc.c, p.c.id, C.uint(s.c.index)) +} + +func (p *Program) ID() int { + return int(p.c.id) +} + +func (p *Program) NbStreams() int { + return int(p.c.nb_stream_indexes) +} + +func (p *Program) SetID(i int) { + p.c.id = C.int(i) +} + +func (p *Program) Streams() (ss []*Stream) { + is := make(map[int]bool) + for _, idx := range unsafe.Slice(p.c.stream_index, p.c.nb_stream_indexes) { + is[int(idx)] = true + } + for _, s := range p.fc.Streams() { + if _, ok := is[s.Index()]; ok { + ss = append(ss, s) + } + } + return +} diff --git a/program_test.go b/program_test.go new file mode 100644 index 0000000..6c15724 --- /dev/null +++ b/program_test.go @@ -0,0 +1,25 @@ +package astiav + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProgram(t *testing.T) { + fc := AllocFormatContext() + require.NotNil(t, fc) + defer fc.Free() + p := fc.NewProgram(1) + require.Equal(t, 1, p.ID()) + p.SetID(2) + require.Equal(t, 2, p.ID()) + s := fc.NewStream(nil) + s.SetID(2) + require.Equal(t, 0, p.NbStreams()) + p.AddStream(s) + require.Equal(t, 1, p.NbStreams()) + ss := p.Streams() + require.Equal(t, 1, len(ss)) + require.Equal(t, s.ID(), ss[0].ID()) +}