Added Classer + now logs returns a Classer + tests are now done in astiav package

This commit is contained in:
Quentin Renard
2024-02-28 18:05:36 +01:00
parent 7fe358fbfa
commit b5db8fb22f
51 changed files with 1177 additions and 808 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
.DS_STORE
cover*
coverage.out
tmp

View File

@@ -14,3 +14,7 @@ install-ffmpeg:
cd $(srcPath) && ./configure --prefix=.. $(configure)
cd $(srcPath) && make
cd $(srcPath) && make install
coverage:
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

View File

@@ -1,4 +1,4 @@
package astiav_test
package astiav
import (
"errors"
@@ -7,7 +7,6 @@ import (
"sync"
"testing"
"github.com/asticode/go-astiav"
"github.com/asticode/go-astikit"
)
@@ -46,12 +45,12 @@ func (h *helper) close() {
}
type helperInput struct {
firstPkt *astiav.Packet
formatContext *astiav.FormatContext
lastFrame *astiav.Frame
firstPkt *Packet
formatContext *FormatContext
lastFrame *Frame
}
func (h *helper) inputFormatContext(name string) (fc *astiav.FormatContext, err error) {
func (h *helper) inputFormatContext(name string) (fc *FormatContext, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.formatContext != nil {
@@ -60,7 +59,7 @@ func (h *helper) inputFormatContext(name string) (fc *astiav.FormatContext, err
}
h.m.Unlock()
if fc = astiav.AllocFormatContext(); fc == nil {
if fc = AllocFormatContext(); fc == nil {
err = errors.New("astiav_test: allocated format context is nil")
return
}
@@ -86,7 +85,7 @@ func (h *helper) inputFormatContext(name string) (fc *astiav.FormatContext, err
return
}
func (h *helper) inputFirstPacket(name string) (pkt *astiav.Packet, err error) {
func (h *helper) inputFirstPacket(name string) (pkt *Packet, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.firstPkt != nil {
@@ -95,13 +94,13 @@ func (h *helper) inputFirstPacket(name string) (pkt *astiav.Packet, err error) {
}
h.m.Unlock()
var fc *astiav.FormatContext
var fc *FormatContext
if fc, err = h.inputFormatContext(name); err != nil {
err = fmt.Errorf("astiav_test: getting input format context failed")
return
}
pkt = astiav.AllocPacket()
pkt = AllocPacket()
if pkt == nil {
err = errors.New("astiav_test: pkt is nil")
return
@@ -119,7 +118,7 @@ func (h *helper) inputFirstPacket(name string) (pkt *astiav.Packet, err error) {
return
}
func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *astiav.Frame, err error) {
func (h *helper) inputLastFrame(name string, mediaType MediaType) (f *Frame, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.lastFrame != nil {
@@ -128,14 +127,14 @@ func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *ast
}
h.m.Unlock()
var fc *astiav.FormatContext
var fc *FormatContext
if fc, err = h.inputFormatContext(name); err != nil {
err = fmt.Errorf("astiav_test: getting input format context failed: %w", err)
return
}
var cc *astiav.CodecContext
var cs *astiav.Stream
var cc *CodecContext
var cs *Stream
for _, s := range fc.Streams() {
if s.CodecParameters().MediaType() != mediaType {
continue
@@ -143,13 +142,13 @@ func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *ast
cs = s
c := astiav.FindDecoder(s.CodecParameters().CodecID())
c := FindDecoder(s.CodecParameters().CodecID())
if c == nil {
err = errors.New("astiav_test: no codec")
return
}
cc = astiav.AllocCodecContext(c)
cc = AllocCodecContext(c)
if cc == nil {
err = errors.New("astiav_test: no codec context")
return
@@ -173,25 +172,25 @@ func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *ast
return
}
var pkt1 *astiav.Packet
var pkt1 *Packet
if pkt1, err = h.inputFirstPacket(name); err != nil {
err = fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
return
}
pkt2 := astiav.AllocPacket()
pkt2 := AllocPacket()
h.closer.Add(pkt2.Free)
f = astiav.AllocFrame()
f = AllocFrame()
h.closer.Add(f.Free)
lastFrame := astiav.AllocFrame()
lastFrame := AllocFrame()
h.closer.Add(lastFrame.Free)
pkts := []*astiav.Packet{pkt1}
pkts := []*Packet{pkt1}
for {
if err = fc.ReadFrame(pkt2); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
if len(pkts) == 0 {
if err = f.Ref(lastFrame); err != nil {
err = fmt.Errorf("astiav_test: last refing frame failed: %w", err)
@@ -220,7 +219,7 @@ func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *ast
for {
if err = cc.ReceiveFrame(f); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
err = nil
break
}
@@ -235,7 +234,7 @@ func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *ast
}
}
pkts = []*astiav.Packet{}
pkts = []*Packet{}
}
h.m.Lock()

View File

@@ -122,8 +122,7 @@ func (l ChannelLayout) copy(dst *C.struct_AVChannelLayout) error {
}
func (l ChannelLayout) clone() (ChannelLayout, error) {
// TODO Should it be freed?
cl := C.struct_AVChannelLayout{}
var cl C.struct_AVChannelLayout
err := l.copy(&cl)
dst := newChannelLayoutFromC(&cl)
return dst, err

View File

@@ -1,17 +1,16 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestChannelLayout(t *testing.T) {
cl := astiav.ChannelLayoutStereo
cl := ChannelLayoutStereo
require.Equal(t, 2, cl.NbChannels())
require.Equal(t, "stereo", cl.String())
require.True(t, cl.Valid())
require.True(t, cl.Equal(astiav.ChannelLayoutStereo))
require.False(t, cl.Equal(astiav.ChannelLayoutMono))
require.True(t, cl.Equal(ChannelLayoutStereo))
require.False(t, cl.Equal(ChannelLayoutMono))
}

134
class.go Normal file
View File

@@ -0,0 +1,134 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/log.h>
//#include <stdint.h>
/*
static inline char* astiavClassItemName(AVClass* c, void* ptr) {
return (char*)c->item_name(ptr);
}
static inline AVClassCategory astiavClassCategory(AVClass* c, void* ptr) {
if (c->get_category) return c->get_category(ptr);
return c->category;
}
static inline AVClass** astiavClassParent(AVClass* c, void* ptr) {
if (c->parent_log_context_offset) {
AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) + c->parent_log_context_offset);
if (parent && *parent) {
return parent;
}
}
return NULL;
}
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/log.h#L66
type Class struct {
c *C.struct_AVClass
ptr unsafe.Pointer
}
func newClassFromC(ptr unsafe.Pointer) *Class {
if ptr == nil {
return nil
}
c := (**C.struct_AVClass)(ptr)
if c == nil {
return nil
}
return &Class{
c: *c,
ptr: ptr,
}
}
func (c *Class) Category() ClassCategory {
return ClassCategory(C.astiavClassCategory(c.c, c.ptr))
}
func (c *Class) ItemName() string {
return C.GoString(C.astiavClassItemName(c.c, c.ptr))
}
func (c *Class) Name() string {
return C.GoString(c.c.class_name)
}
func (c *Class) Parent() *Class {
return newClassFromC(unsafe.Pointer(C.astiavClassParent(c.c, c.ptr)))
}
func (c *Class) String() string {
return fmt.Sprintf("%s [%s] @ %p", c.ItemName(), c.Name(), c.ptr)
}
type Classer interface {
Class() *Class
}
type UnknownClasser struct {
c *Class
}
func newUnknownClasser(ptr unsafe.Pointer) *UnknownClasser {
return &UnknownClasser{c: newClassFromC(ptr)}
}
func (c *UnknownClasser) Class() *Class {
return c.c
}
var classers = newClasserPool()
type classerPool struct {
m sync.Mutex
p map[unsafe.Pointer]Classer
}
func newClasserPool() *classerPool {
return &classerPool{p: make(map[unsafe.Pointer]Classer)}
}
func (p *classerPool) unsafePointer(c Classer) unsafe.Pointer {
if c == nil {
return nil
}
cl := c.Class()
if cl == nil {
return nil
}
return cl.ptr
}
func (p *classerPool) set(c Classer) {
p.m.Lock()
defer p.m.Unlock()
if ptr := p.unsafePointer(c); ptr != nil {
p.p[ptr] = c
}
}
func (p *classerPool) del(c Classer) {
p.m.Lock()
defer p.m.Unlock()
if ptr := p.unsafePointer(c); ptr != nil {
delete(p.p, ptr)
}
}
func (p *classerPool) get(ptr unsafe.Pointer) (Classer, bool) {
p.m.Lock()
defer p.m.Unlock()
c, ok := p.p[ptr]
return c, ok
}

30
class_category.go Normal file
View File

@@ -0,0 +1,30 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/log.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/log.h#L28
// TODO Find a way to use C.enum_AVClassCategory instead of uint
type ClassCategory uint
const (
ClassCategoryBitstreamFilter = ClassCategory(C.AV_CLASS_CATEGORY_BITSTREAM_FILTER)
ClassCategoryDecoder = ClassCategory(C.AV_CLASS_CATEGORY_DECODER)
ClassCategoryDemuxer = ClassCategory(C.AV_CLASS_CATEGORY_DEMUXER)
ClassCategoryDeviceAudioInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT)
ClassCategoryDeviceAudioOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT)
ClassCategoryDeviceInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_INPUT)
ClassCategoryDeviceOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_OUTPUT)
ClassCategoryDeviceVideoInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT)
ClassCategoryDeviceVideoOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT)
ClassCategoryEncoder = ClassCategory(C.AV_CLASS_CATEGORY_ENCODER)
ClassCategoryFilter = ClassCategory(C.AV_CLASS_CATEGORY_FILTER)
ClassCategoryInput = ClassCategory(C.AV_CLASS_CATEGORY_INPUT)
ClassCategoryMuxer = ClassCategory(C.AV_CLASS_CATEGORY_MUXER)
ClassCategoryNa = ClassCategory(C.AV_CLASS_CATEGORY_NA)
ClassCategoryNb = ClassCategory(C.AV_CLASS_CATEGORY_NB)
ClassCategoryOutput = ClassCategory(C.AV_CLASS_CATEGORY_OUTPUT)
ClassCategorySwresampler = ClassCategory(C.AV_CLASS_CATEGORY_SWRESAMPLER)
ClassCategorySwscaler = ClassCategory(C.AV_CLASS_CATEGORY_SWSCALER)
)

63
class_test.go Normal file
View File

@@ -0,0 +1,63 @@
package astiav
import (
"fmt"
"os"
"path/filepath"
"testing"
"unsafe"
"github.com/stretchr/testify/require"
)
func TestClass(t *testing.T) {
c := FindDecoder(CodecIDMjpeg)
require.NotNil(t, c)
cc := AllocCodecContext(c)
require.NotNil(t, cc)
defer cc.Free()
cl := cc.Class()
require.NotNil(t, cl)
require.Equal(t, ClassCategoryDecoder, cl.Category())
require.Equal(t, "mjpeg", cl.ItemName())
require.Equal(t, "AVCodecContext", cl.Name())
require.Equal(t, fmt.Sprintf("mjpeg [AVCodecContext] @ %p", cc.c), cl.String())
// TODO Test parent
}
func TestClassers(t *testing.T) {
cl := len(classers.p)
f := AllocFilterGraph()
c := FindDecoder(CodecIDMjpeg)
require.NotNil(t, c)
cc := AllocCodecContext(c)
require.NotNil(t, cc)
bufferSink := FindFilterByName("buffersink")
require.NotNil(t, bufferSink)
fc, err := f.NewFilterContext(bufferSink, "filter_out", nil)
require.NoError(t, err)
fmc1 := AllocFormatContext()
fmc2 := AllocFormatContext()
require.NoError(t, fmc2.OpenInput("testdata/video.mp4", nil, nil))
path := filepath.Join(t.TempDir(), "iocontext.txt")
ic, err := OpenIOContext(path, NewIOContextFlags(IOContextFlagWrite))
require.NoError(t, err)
defer os.RemoveAll(path)
ssc, err := CreateSoftwareScaleContext(1, 1, PixelFormatRgba, 2, 2, PixelFormatRgba, NewSoftwareScaleContextFlags())
require.NoError(t, err)
require.Equal(t, cl+8, len(classers.p))
v, ok := classers.get(unsafe.Pointer(f.c))
require.True(t, ok)
require.Equal(t, f, v)
cc.Free()
fc.Free()
f.Free()
fmc1.Free()
fmc2.CloseInput()
require.NoError(t, ic.Closep())
ssc.Free()
require.Equal(t, cl, len(classers.p))
}

View File

@@ -39,6 +39,17 @@ type CodecContext struct {
hdc *HardwareDeviceContext
}
func newCodecContextFromC(c *C.struct_AVCodecContext) *CodecContext {
if c == nil {
return nil
}
cc := &CodecContext{c: c}
classers.set(cc)
return cc
}
var _ Classer = (*CodecContext)(nil)
func AllocCodecContext(c *Codec) *CodecContext {
var cc *C.struct_AVCodec
if c != nil {
@@ -47,18 +58,12 @@ func AllocCodecContext(c *Codec) *CodecContext {
return newCodecContextFromC(C.avcodec_alloc_context3(cc))
}
func newCodecContextFromC(c *C.struct_AVCodecContext) *CodecContext {
if c == nil {
return nil
}
return &CodecContext{c: c}
}
func (cc *CodecContext) Free() {
if cc.hdc != nil {
C.av_buffer_unref(&cc.hdc.c)
cc.hdc = nil
}
classers.del(cc)
C.avcodec_free_context(&cc.c)
}
@@ -99,6 +104,10 @@ func (cc *CodecContext) ChromaLocation() ChromaLocation {
return ChromaLocation(cc.c.chroma_sample_location)
}
func (cc *CodecContext) Class() *Class {
return newClassFromC(unsafe.Pointer(cc.c))
}
func (cc *CodecContext) CodecID() CodecID {
return CodecID(cc.c.codec_id)
}

View File

@@ -1,9 +1,8 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -15,36 +14,39 @@ func TestCodecContext(t *testing.T) {
s1 := ss[0]
s2 := ss[1]
c1 := astiav.FindDecoder(s1.CodecParameters().CodecID())
c1 := FindDecoder(s1.CodecParameters().CodecID())
require.NotNil(t, c1)
cc1 := astiav.AllocCodecContext(c1)
cc1 := AllocCodecContext(c1)
require.NotNil(t, cc1)
defer cc1.Free()
err = s1.CodecParameters().ToCodecContext(cc1)
require.NoError(t, err)
require.Equal(t, "Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 320x180 [SAR 1:1 DAR 16:9], 441 kb/s", cc1.String())
require.Equal(t, int64(441324), cc1.BitRate())
require.Equal(t, astiav.ChromaLocationLeft, cc1.ChromaLocation())
require.Equal(t, astiav.CodecIDH264, cc1.CodecID())
require.Equal(t, astiav.ColorPrimariesUnspecified, cc1.ColorPrimaries())
require.Equal(t, astiav.ColorRangeUnspecified, cc1.ColorRange())
require.Equal(t, astiav.ColorSpaceUnspecified, cc1.ColorSpace())
require.Equal(t, astiav.ColorTransferCharacteristicUnspecified, cc1.ColorTransferCharacteristic())
require.Equal(t, ChromaLocationLeft, cc1.ChromaLocation())
require.Equal(t, CodecIDH264, cc1.CodecID())
require.Equal(t, ColorPrimariesUnspecified, cc1.ColorPrimaries())
require.Equal(t, ColorRangeUnspecified, cc1.ColorRange())
require.Equal(t, ColorSpaceUnspecified, cc1.ColorSpace())
require.Equal(t, ColorTransferCharacteristicUnspecified, cc1.ColorTransferCharacteristic())
require.Equal(t, 12, cc1.GopSize())
require.Equal(t, 180, cc1.Height())
require.Equal(t, astiav.Level(13), cc1.Level())
require.Equal(t, astiav.MediaTypeVideo, cc1.MediaType())
require.Equal(t, astiav.PixelFormatYuv420P, cc1.PixelFormat())
require.Equal(t, astiav.ProfileH264ConstrainedBaseline, cc1.Profile())
require.Equal(t, astiav.NewRational(1, 1), cc1.SampleAspectRatio())
require.Equal(t, astiav.StrictStdComplianceNormal, cc1.StrictStdCompliance())
require.Equal(t, Level(13), cc1.Level())
require.Equal(t, MediaTypeVideo, cc1.MediaType())
require.Equal(t, PixelFormatYuv420P, cc1.PixelFormat())
require.Equal(t, ProfileH264ConstrainedBaseline, cc1.Profile())
require.Equal(t, NewRational(1, 1), cc1.SampleAspectRatio())
require.Equal(t, StrictStdComplianceNormal, cc1.StrictStdCompliance())
require.Equal(t, 1, cc1.ThreadCount())
require.Equal(t, astiav.ThreadType(3), cc1.ThreadType())
require.Equal(t, ThreadType(3), cc1.ThreadType())
require.Equal(t, 320, cc1.Width())
cl := cc1.Class()
require.NotNil(t, cl)
require.Equal(t, "AVCodecContext", cl.Name())
c2 := astiav.FindDecoder(s2.CodecParameters().CodecID())
c2 := FindDecoder(s2.CodecParameters().CodecID())
require.NotNil(t, c2)
cc2 := astiav.AllocCodecContext(c2)
cc2 := AllocCodecContext(c2)
require.NotNil(t, cc2)
defer cc2.Free()
err = s2.CodecParameters().ToCodecContext(cc2)
@@ -52,66 +54,66 @@ func TestCodecContext(t *testing.T) {
require.Equal(t, "Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 161 kb/s", cc2.String())
require.Equal(t, int64(161052), cc2.BitRate())
require.Equal(t, 2, cc2.Channels())
require.True(t, cc2.ChannelLayout().Equal(astiav.ChannelLayoutStereo))
require.Equal(t, astiav.CodecIDAac, cc2.CodecID())
require.True(t, cc2.ChannelLayout().Equal(ChannelLayoutStereo))
require.Equal(t, CodecIDAac, cc2.CodecID())
require.Equal(t, 1024, cc2.FrameSize())
require.Equal(t, astiav.MediaTypeAudio, cc2.MediaType())
require.Equal(t, astiav.SampleFormatFltp, cc2.SampleFormat())
require.Equal(t, MediaTypeAudio, cc2.MediaType())
require.Equal(t, SampleFormatFltp, cc2.SampleFormat())
require.Equal(t, 48000, cc2.SampleRate())
require.Equal(t, astiav.StrictStdComplianceNormal, cc2.StrictStdCompliance())
require.Equal(t, StrictStdComplianceNormal, cc2.StrictStdCompliance())
require.Equal(t, 1, cc2.ThreadCount())
require.Equal(t, astiav.ThreadType(3), cc2.ThreadType())
require.Equal(t, ThreadType(3), cc2.ThreadType())
c3 := astiav.FindEncoder(astiav.CodecIDMjpeg)
c3 := FindEncoder(CodecIDMjpeg)
require.NotNil(t, c3)
cc3 := astiav.AllocCodecContext(c3)
cc3 := AllocCodecContext(c3)
require.NotNil(t, cc3)
defer cc3.Free()
cc3.SetHeight(2)
cc3.SetPixelFormat(astiav.PixelFormatYuvj420P)
cc3.SetTimeBase(astiav.NewRational(1, 1))
cc3.SetPixelFormat(PixelFormatYuvj420P)
cc3.SetTimeBase(NewRational(1, 1))
cc3.SetWidth(3)
err = cc3.Open(c3, nil)
require.NoError(t, err)
cc4 := astiav.AllocCodecContext(nil)
cc4 := AllocCodecContext(nil)
require.NotNil(t, cc4)
defer cc4.Free()
cc4.SetBitRate(1)
cc4.SetChannelLayout(astiav.ChannelLayout21)
cc4.SetChannelLayout(ChannelLayout21)
cc4.SetChannels(3)
cc4.SetFlags(astiav.NewCodecContextFlags(4))
cc4.SetFlags2(astiav.NewCodecContextFlags2(5))
cc4.SetFramerate(astiav.NewRational(6, 1))
cc4.SetFlags(NewCodecContextFlags(4))
cc4.SetFlags2(NewCodecContextFlags2(5))
cc4.SetFramerate(NewRational(6, 1))
cc4.SetGopSize(7)
cc4.SetHeight(8)
cc4.SetPixelFormat(astiav.PixelFormat0Bgr)
cc4.SetPixelFormat(PixelFormat0Bgr)
cc4.SetQmin(5)
cc4.SetSampleAspectRatio(astiav.NewRational(10, 1))
cc4.SetSampleFormat(astiav.SampleFormatDbl)
cc4.SetSampleAspectRatio(NewRational(10, 1))
cc4.SetSampleFormat(SampleFormatDbl)
cc4.SetSampleRate(12)
cc4.SetStrictStdCompliance(astiav.StrictStdComplianceExperimental)
cc4.SetStrictStdCompliance(StrictStdComplianceExperimental)
cc4.SetThreadCount(13)
cc4.SetThreadType(astiav.ThreadTypeSlice)
cc4.SetTimeBase(astiav.NewRational(15, 1))
cc4.SetThreadType(ThreadTypeSlice)
cc4.SetTimeBase(NewRational(15, 1))
cc4.SetWidth(16)
require.Equal(t, int64(1), cc4.BitRate())
require.True(t, cc4.ChannelLayout().Equal(astiav.ChannelLayout21))
require.True(t, cc4.ChannelLayout().Equal(ChannelLayout21))
require.Equal(t, 3, cc4.Channels())
require.Equal(t, astiav.NewCodecContextFlags(4), cc4.Flags())
require.Equal(t, astiav.NewCodecContextFlags2(5), cc4.Flags2())
require.Equal(t, astiav.NewRational(6, 1), cc4.Framerate())
require.Equal(t, NewCodecContextFlags(4), cc4.Flags())
require.Equal(t, NewCodecContextFlags2(5), cc4.Flags2())
require.Equal(t, NewRational(6, 1), cc4.Framerate())
require.Equal(t, 7, cc4.GopSize())
require.Equal(t, 8, cc4.Height())
require.Equal(t, astiav.PixelFormat0Bgr, cc4.PixelFormat())
require.Equal(t, PixelFormat0Bgr, cc4.PixelFormat())
require.Equal(t, 5, cc4.Qmin())
require.Equal(t, astiav.NewRational(10, 1), cc4.SampleAspectRatio())
require.Equal(t, astiav.SampleFormatDbl, cc4.SampleFormat())
require.Equal(t, NewRational(10, 1), cc4.SampleAspectRatio())
require.Equal(t, SampleFormatDbl, cc4.SampleFormat())
require.Equal(t, 12, cc4.SampleRate())
require.Equal(t, astiav.StrictStdComplianceExperimental, cc4.StrictStdCompliance())
require.Equal(t, StrictStdComplianceExperimental, cc4.StrictStdCompliance())
require.Equal(t, 13, cc4.ThreadCount())
require.Equal(t, astiav.ThreadTypeSlice, cc4.ThreadType())
require.Equal(t, astiav.NewRational(15, 1), cc4.TimeBase())
require.Equal(t, ThreadTypeSlice, cc4.ThreadType())
require.Equal(t, NewRational(15, 1), cc4.TimeBase())
require.Equal(t, 16, cc4.Width())
// TODO Test ReceivePacket

View File

@@ -1,14 +1,13 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestCodecID(t *testing.T) {
require.Equal(t, astiav.MediaTypeVideo, astiav.CodecIDH264.MediaType())
require.Equal(t, "h264", astiav.CodecIDH264.Name())
require.Equal(t, "h264", astiav.CodecIDH264.String())
require.Equal(t, MediaTypeVideo, CodecIDH264.MediaType())
require.Equal(t, "h264", CodecIDH264.Name())
require.Equal(t, "h264", CodecIDH264.String())
}

View File

@@ -1,9 +1,8 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -17,75 +16,75 @@ func TestCodecParameters(t *testing.T) {
cp1 := s1.CodecParameters()
require.Equal(t, int64(441324), cp1.BitRate())
require.Equal(t, astiav.ChromaLocationLeft, cp1.ChromaLocation())
require.Equal(t, astiav.CodecIDH264, cp1.CodecID())
require.Equal(t, astiav.CodecTag(0x31637661), cp1.CodecTag())
require.Equal(t, astiav.ColorPrimariesUnspecified, cp1.ColorPrimaries())
require.Equal(t, astiav.ColorRangeUnspecified, cp1.ColorRange())
require.Equal(t, astiav.ColorSpaceUnspecified, cp1.ColorSpace())
require.Equal(t, astiav.ColorTransferCharacteristicUnspecified, cp1.ColorTransferCharacteristic())
require.Equal(t, ChromaLocationLeft, cp1.ChromaLocation())
require.Equal(t, CodecIDH264, cp1.CodecID())
require.Equal(t, CodecTag(0x31637661), cp1.CodecTag())
require.Equal(t, ColorPrimariesUnspecified, cp1.ColorPrimaries())
require.Equal(t, ColorRangeUnspecified, cp1.ColorRange())
require.Equal(t, ColorSpaceUnspecified, cp1.ColorSpace())
require.Equal(t, ColorTransferCharacteristicUnspecified, cp1.ColorTransferCharacteristic())
require.Equal(t, 180, cp1.Height())
require.Equal(t, astiav.Level(13), cp1.Level())
require.Equal(t, astiav.MediaTypeVideo, cp1.MediaType())
require.Equal(t, astiav.PixelFormatYuv420P, cp1.PixelFormat())
require.Equal(t, astiav.ProfileH264ConstrainedBaseline, cp1.Profile())
require.Equal(t, astiav.NewRational(1, 1), cp1.SampleAspectRatio())
require.Equal(t, Level(13), cp1.Level())
require.Equal(t, MediaTypeVideo, cp1.MediaType())
require.Equal(t, PixelFormatYuv420P, cp1.PixelFormat())
require.Equal(t, ProfileH264ConstrainedBaseline, cp1.Profile())
require.Equal(t, NewRational(1, 1), cp1.SampleAspectRatio())
require.Equal(t, 320, cp1.Width())
cp2 := s2.CodecParameters()
require.Equal(t, int64(161052), cp2.BitRate())
require.Equal(t, 2, cp2.Channels())
require.True(t, cp2.ChannelLayout().Equal(astiav.ChannelLayoutStereo))
require.Equal(t, astiav.CodecIDAac, cp2.CodecID())
require.Equal(t, astiav.CodecTag(0x6134706d), cp2.CodecTag())
require.True(t, cp2.ChannelLayout().Equal(ChannelLayoutStereo))
require.Equal(t, CodecIDAac, cp2.CodecID())
require.Equal(t, CodecTag(0x6134706d), cp2.CodecTag())
require.Equal(t, 1024, cp2.FrameSize())
require.Equal(t, astiav.MediaTypeAudio, cp2.MediaType())
require.Equal(t, astiav.SampleFormatFltp, cp2.SampleFormat())
require.Equal(t, MediaTypeAudio, cp2.MediaType())
require.Equal(t, SampleFormatFltp, cp2.SampleFormat())
require.Equal(t, 48000, cp2.SampleRate())
cp3 := astiav.AllocCodecParameters()
cp3 := AllocCodecParameters()
require.NotNil(t, cp3)
defer cp3.Free()
err = cp2.Copy(cp3)
require.NoError(t, err)
require.Equal(t, 2, cp3.Channels())
cc4 := astiav.AllocCodecContext(nil)
cc4 := AllocCodecContext(nil)
require.NotNil(t, cc4)
defer cc4.Free()
err = cp2.ToCodecContext(cc4)
require.NoError(t, err)
require.Equal(t, 2, cc4.Channels())
cp5 := astiav.AllocCodecParameters()
cp5 := AllocCodecParameters()
require.NotNil(t, cp5)
defer cp5.Free()
err = cp5.FromCodecContext(cc4)
require.NoError(t, err)
require.Equal(t, 2, cp5.Channels())
cp6 := astiav.AllocCodecParameters()
cp6 := AllocCodecParameters()
require.NotNil(t, cp6)
defer cp6.Free()
cp6.SetChannelLayout(astiav.ChannelLayout21)
require.True(t, cp6.ChannelLayout().Equal(astiav.ChannelLayout21))
cp6.SetChannelLayout(ChannelLayout21)
require.True(t, cp6.ChannelLayout().Equal(ChannelLayout21))
defer cp6.Free()
cp6.SetChannels(3)
require.Equal(t, 3, cp6.Channels())
cp6.SetCodecID(astiav.CodecIDRawvideo)
require.Equal(t, astiav.CodecIDRawvideo, cp6.CodecID())
cp6.SetCodecTag(astiav.CodecTag(2))
require.Equal(t, astiav.CodecTag(2), cp6.CodecTag())
cp6.SetCodecType(astiav.MediaTypeAudio)
require.Equal(t, astiav.MediaTypeAudio, cp6.CodecType())
cp6.SetCodecID(CodecIDRawvideo)
require.Equal(t, CodecIDRawvideo, cp6.CodecID())
cp6.SetCodecTag(CodecTag(2))
require.Equal(t, CodecTag(2), cp6.CodecTag())
cp6.SetCodecType(MediaTypeAudio)
require.Equal(t, MediaTypeAudio, cp6.CodecType())
cp6.SetHeight(1)
require.Equal(t, 1, cp6.Height())
cp6.SetPixelFormat(astiav.PixelFormat0Bgr)
require.Equal(t, astiav.PixelFormat0Bgr, cp6.PixelFormat())
cp6.SetSampleAspectRatio(astiav.NewRational(1, 2))
require.Equal(t, astiav.NewRational(1, 2), cp6.SampleAspectRatio())
cp6.SetSampleFormat(astiav.SampleFormatDbl)
require.Equal(t, astiav.SampleFormatDbl, cp6.SampleFormat())
cp6.SetPixelFormat(PixelFormat0Bgr)
require.Equal(t, PixelFormat0Bgr, cp6.PixelFormat())
cp6.SetSampleAspectRatio(NewRational(1, 2))
require.Equal(t, NewRational(1, 2), cp6.SampleAspectRatio())
cp6.SetSampleFormat(SampleFormatDbl)
require.Equal(t, SampleFormatDbl, cp6.SampleFormat())
cp6.SetSampleRate(4)
require.Equal(t, 4, cp6.SampleRate())
cp6.SetWidth(2)

View File

@@ -1,34 +1,33 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestCodec(t *testing.T) {
c := astiav.FindDecoder(astiav.CodecIDMp3)
c := FindDecoder(CodecIDMp3)
require.NotNil(t, c)
require.Equal(t, c.ID(), astiav.CodecIDMp3)
require.Equal(t, c.ID(), CodecIDMp3)
require.Nil(t, c.ChannelLayouts())
require.True(t, c.IsDecoder())
require.False(t, c.IsEncoder())
require.Nil(t, c.PixelFormats())
require.Equal(t, []astiav.SampleFormat{astiav.SampleFormatFltp, astiav.SampleFormatFlt}, c.SampleFormats())
require.Equal(t, []SampleFormat{SampleFormatFltp, SampleFormatFlt}, c.SampleFormats())
require.Equal(t, "mp3float", c.Name())
require.Equal(t, "mp3float", c.String())
c = astiav.FindDecoderByName("aac")
c = FindDecoderByName("aac")
require.NotNil(t, c)
els := []astiav.ChannelLayout{
astiav.ChannelLayoutMono,
astiav.ChannelLayoutStereo,
astiav.ChannelLayoutSurround,
astiav.ChannelLayout4Point0,
astiav.ChannelLayout5Point0Back,
astiav.ChannelLayout5Point1Back,
astiav.ChannelLayout7Point1WideBack,
els := []ChannelLayout{
ChannelLayoutMono,
ChannelLayoutStereo,
ChannelLayoutSurround,
ChannelLayout4Point0,
ChannelLayout5Point0Back,
ChannelLayout5Point1Back,
ChannelLayout7Point1WideBack,
}
gls := c.ChannelLayouts()
require.Len(t, gls, len(els))
@@ -37,40 +36,40 @@ func TestCodec(t *testing.T) {
}
require.True(t, c.IsDecoder())
require.False(t, c.IsEncoder())
require.Equal(t, []astiav.SampleFormat{astiav.SampleFormatFltp}, c.SampleFormats())
require.Equal(t, []SampleFormat{SampleFormatFltp}, c.SampleFormats())
require.Equal(t, "aac", c.Name())
require.Equal(t, "aac", c.String())
c = astiav.FindEncoder(astiav.CodecIDMjpeg)
c = FindEncoder(CodecIDMjpeg)
require.NotNil(t, c)
require.False(t, c.IsDecoder())
require.True(t, c.IsEncoder())
require.Contains(t, c.PixelFormats(), astiav.PixelFormatYuvj420P)
require.Contains(t, c.PixelFormats(), PixelFormatYuvj420P)
require.Nil(t, c.SampleFormats())
require.Contains(t, c.Name(), "mjpeg")
require.Contains(t, c.String(), "mjpeg")
c = astiav.FindEncoderByName("mjpeg")
c = FindEncoderByName("mjpeg")
require.NotNil(t, c)
require.False(t, c.IsDecoder())
require.True(t, c.IsEncoder())
require.Equal(t, []astiav.PixelFormat{
astiav.PixelFormatYuvj420P,
astiav.PixelFormatYuvj422P,
astiav.PixelFormatYuvj444P,
astiav.PixelFormatYuv420P,
astiav.PixelFormatYuv422P,
astiav.PixelFormatYuv444P,
require.Equal(t, []PixelFormat{
PixelFormatYuvj420P,
PixelFormatYuvj422P,
PixelFormatYuvj444P,
PixelFormatYuv420P,
PixelFormatYuv422P,
PixelFormatYuv444P,
}, c.PixelFormats())
require.Equal(t, "mjpeg", c.Name())
require.Equal(t, "mjpeg", c.String())
c = astiav.FindDecoderByName("invalid")
c = FindDecoderByName("invalid")
require.Nil(t, c)
var found bool
for _, c := range astiav.Codecs() {
if c.ID() == astiav.CodecIDMjpeg {
for _, c := range Codecs() {
if c.ID() == CodecIDMjpeg {
found = true
}
}

View File

@@ -1,11 +1,9 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
)
func TestDevice(t *testing.T) {
astiav.RegisterAllDevices()
RegisterAllDevices()
}

View File

@@ -1,11 +1,10 @@
package astiav_test
package astiav
import (
"errors"
"fmt"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -14,10 +13,10 @@ type testError struct{}
func (err testError) Error() string { return "" }
func TestError(t *testing.T) {
require.Equal(t, "Decoder not found", astiav.ErrDecoderNotFound.Error())
err1 := fmt.Errorf("test 1: %w", astiav.ErrDecoderNotFound)
require.True(t, errors.Is(err1, astiav.ErrDecoderNotFound))
require.Equal(t, "Decoder not found", ErrDecoderNotFound.Error())
err1 := fmt.Errorf("test 1: %w", ErrDecoderNotFound)
require.True(t, errors.Is(err1, ErrDecoderNotFound))
require.False(t, errors.Is(err1, testError{}))
err2 := fmt.Errorf("test 2: %w", astiav.ErrDemuxerNotFound)
require.False(t, errors.Is(err2, astiav.ErrDecoderNotFound))
err2 := fmt.Errorf("test 2: %w", ErrDemuxerNotFound)
require.False(t, errors.Is(err2, ErrDecoderNotFound))
}

View File

@@ -23,8 +23,14 @@ type stream struct {
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags

View File

@@ -37,8 +37,14 @@ type stream struct {
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags

View File

@@ -28,8 +28,14 @@ type stream struct {
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags

View File

@@ -18,8 +18,14 @@ var (
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
@@ -96,11 +102,9 @@ func main() {
// If this is a file, we need to use an io context
if !outputFormatContext.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) {
// Create io context
ioContext := astiav.NewIOContext()
// Open io context
if err = ioContext.Open(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
ioContext, err := astiav.OpenIOContext(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite))
if err != nil {
log.Fatal(fmt.Errorf("main: opening io context failed: %w", err))
}
defer ioContext.Closep() //nolint:errcheck

View File

@@ -20,8 +20,14 @@ var (
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags

View File

@@ -42,8 +42,14 @@ type stream struct {
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
@@ -306,11 +312,9 @@ func openOutputFile() (err error) {
// If this is a file, we need to use an io context
if !outputFormatContext.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) {
// Create io context
ioContext := astiav.NewIOContext()
// Open io context
if err = ioContext.Open(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
var ioContext *astiav.IOContext
if ioContext, err = astiav.OpenIOContext(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
err = fmt.Errorf("main: opening io context failed: %w", err)
return
}

View File

@@ -6,18 +6,28 @@ package astiav
//#include <libavfilter/buffersrc.h>
//#include <libavutil/frame.h>
import "C"
import "unsafe"
import (
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L67
type FilterContext struct {
c *C.struct_AVFilterContext
}
func newFilterContext() *FilterContext {
return &FilterContext{}
func newFilterContext(c *C.struct_AVFilterContext) *FilterContext {
if c == nil {
return nil
}
fc := &FilterContext{c: c}
classers.set(fc)
return fc
}
var _ Classer = (*FilterContext)(nil)
func (fc *FilterContext) Free() {
classers.del(fc)
C.avfilter_free(fc.c)
}
@@ -37,6 +47,10 @@ func (fc *FilterContext) BuffersinkGetFrame(f *Frame, fs BuffersinkFlags) error
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)
}

View File

@@ -17,21 +17,32 @@ func newFilterGraphFromC(c *C.struct_AVFilterGraph) *FilterGraph {
if c == nil {
return nil
}
return &FilterGraph{c: c}
g := &FilterGraph{c: c}
classers.set(g)
return g
}
var _ Classer = (*FilterGraph)(nil)
func AllocFilterGraph() *FilterGraph {
return newFilterGraphFromC(C.avfilter_graph_alloc())
}
func (g *FilterGraph) Free() {
C.avfilter_graph_free(&g.c)
classers.del(g)
if g.c != nil {
C.avfilter_graph_free(&g.c)
}
}
func (g *FilterGraph) String() string {
return C.GoString(C.avfilter_graph_dump(g.c, nil))
}
func (g *FilterGraph) Class() *Class {
return newClassFromC(unsafe.Pointer(g.c))
}
type FilterArgs map[string]string
func (args FilterArgs) String() string {
@@ -50,9 +61,11 @@ func (g *FilterGraph) NewFilterContext(f *Filter, name string, args FilterArgs)
}
cn := C.CString(name)
defer C.free(unsafe.Pointer(cn))
fc := newFilterContext()
err := newError(C.avfilter_graph_create_filter(&fc.c, f.c, cn, ca, nil, g.c))
return fc, err
var c *C.struct_AVFilterContext
if err := newError(C.avfilter_graph_create_filter(&c, f.c, cn, ca, nil, g.c)); err != nil {
return nil, err
}
return newFilterContext(c), nil
}
func (g *FilterGraph) Parse(content string, inputs, outputs *FilterInOut) error {

View File

@@ -1,48 +1,53 @@
// TODO Fix https://github.com/asticode/go-astiav/actions/runs/5853322732/job/15867145888
//go:build !windows
package astiav_test
package astiav
import (
"fmt"
"strconv"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestFilterGraph(t *testing.T) {
fg := astiav.AllocFilterGraph()
fg := AllocFilterGraph()
defer fg.Free()
cl := fg.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFilterGraph", cl.Name())
bufferSink := astiav.FindFilterByName("buffersink")
bufferSink := FindFilterByName("buffersink")
require.NotNil(t, bufferSink)
fcOut, err := fg.NewFilterContext(bufferSink, "filter_out", nil)
require.NoError(t, err)
defer fcOut.Free()
cl = fcOut.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFilter", cl.Name())
inputs := astiav.AllocFilterInOut()
inputs := AllocFilterInOut()
defer inputs.Free()
inputs.SetName("out")
inputs.SetFilterContext(fcOut)
inputs.SetPadIdx(0)
inputs.SetNext(nil)
var outputs *astiav.FilterInOut
var outputs *FilterInOut
defer func() {
if outputs != nil {
outputs.Free()
}
}()
var fcIns []*astiav.FilterContext
var fcIns []*FilterContext
for i := 0; i < 2; i++ {
bufferSrc := astiav.FindFilterByName("buffer")
bufferSrc := FindFilterByName("buffer")
require.NotNil(t, bufferSrc)
fcIn, err := fg.NewFilterContext(bufferSrc, fmt.Sprintf("filter_in_%d", i+1), astiav.FilterArgs{
"pix_fmt": strconv.Itoa(int(astiav.PixelFormatYuv420P)),
fcIn, err := fg.NewFilterContext(bufferSrc, fmt.Sprintf("filter_in_%d", i+1), FilterArgs{
"pix_fmt": strconv.Itoa(int(PixelFormatYuv420P)),
"pixel_aspect": "1/1",
"time_base": "1/1000",
"video_size": "1x1",
@@ -51,7 +56,7 @@ func TestFilterGraph(t *testing.T) {
fcIns = append(fcIns, fcIn)
defer fcIn.Free()
o := astiav.AllocFilterInOut()
o := AllocFilterInOut()
o.SetName(fmt.Sprintf("input_%d", i+1))
o.SetFilterContext(fcIn)
o.SetPadIdx(0)
@@ -68,19 +73,19 @@ func TestFilterGraph(t *testing.T) {
require.Equal(t, 1, fcOut.NbInputs())
require.Equal(t, 1, len(fcOut.Inputs()))
require.Equal(t, astiav.NewRational(1, 1000), fcOut.Inputs()[0].TimeBase())
require.Equal(t, NewRational(1, 1000), fcOut.Inputs()[0].TimeBase())
require.Equal(t, 0, fcOut.NbOutputs())
for _, fc := range fcIns {
require.Equal(t, 0, fc.NbInputs())
require.Equal(t, 1, fc.NbOutputs())
require.Equal(t, 1, len(fc.Outputs()))
require.Equal(t, astiav.NewRational(1, 1000), fc.Outputs()[0].TimeBase())
require.Equal(t, NewRational(1, 1000), fc.Outputs()[0].TimeBase())
}
resp, err := fg.SendCommand("scale", "invalid", "a", astiav.NewFilterCommandFlags())
resp, err := fg.SendCommand("scale", "invalid", "a", NewFilterCommandFlags())
require.Error(t, err)
require.Empty(t, resp)
resp, err = fg.SendCommand("scale", "width", "4", astiav.NewFilterCommandFlags().Add(astiav.FilterCommandFlagOne))
resp, err = fg.SendCommand("scale", "width", "4", NewFilterCommandFlags().Add(FilterCommandFlagOne))
require.NoError(t, err)
require.Empty(t, resp)

View File

@@ -1,14 +1,13 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestFilter(t *testing.T) {
f := astiav.FindFilterByName("format")
f := FindFilterByName("format")
require.NotNil(t, f)
require.Equal(t, "format", f.Name())
require.Equal(t, "format", f.String())

View File

@@ -1,152 +1,151 @@
// Code generated by astiav. DO NOT EDIT.
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestBuffersinkFlags(t *testing.T) {
fs := astiav.NewBuffersinkFlags(astiav.BuffersinkFlag(1))
require.True(t, fs.Has(astiav.BuffersinkFlag(1)))
fs = fs.Add(astiav.BuffersinkFlag(2))
require.True(t, fs.Has(astiav.BuffersinkFlag(2)))
fs = fs.Del(astiav.BuffersinkFlag(2))
require.False(t, fs.Has(astiav.BuffersinkFlag(2)))
fs := NewBuffersinkFlags(BuffersinkFlag(1))
require.True(t, fs.Has(BuffersinkFlag(1)))
fs = fs.Add(BuffersinkFlag(2))
require.True(t, fs.Has(BuffersinkFlag(2)))
fs = fs.Del(BuffersinkFlag(2))
require.False(t, fs.Has(BuffersinkFlag(2)))
}
func TestBuffersrcFlags(t *testing.T) {
fs := astiav.NewBuffersrcFlags(astiav.BuffersrcFlag(1))
require.True(t, fs.Has(astiav.BuffersrcFlag(1)))
fs = fs.Add(astiav.BuffersrcFlag(2))
require.True(t, fs.Has(astiav.BuffersrcFlag(2)))
fs = fs.Del(astiav.BuffersrcFlag(2))
require.False(t, fs.Has(astiav.BuffersrcFlag(2)))
fs := NewBuffersrcFlags(BuffersrcFlag(1))
require.True(t, fs.Has(BuffersrcFlag(1)))
fs = fs.Add(BuffersrcFlag(2))
require.True(t, fs.Has(BuffersrcFlag(2)))
fs = fs.Del(BuffersrcFlag(2))
require.False(t, fs.Has(BuffersrcFlag(2)))
}
func TestCodecContextFlags(t *testing.T) {
fs := astiav.NewCodecContextFlags(astiav.CodecContextFlag(1))
require.True(t, fs.Has(astiav.CodecContextFlag(1)))
fs = fs.Add(astiav.CodecContextFlag(2))
require.True(t, fs.Has(astiav.CodecContextFlag(2)))
fs = fs.Del(astiav.CodecContextFlag(2))
require.False(t, fs.Has(astiav.CodecContextFlag(2)))
fs := NewCodecContextFlags(CodecContextFlag(1))
require.True(t, fs.Has(CodecContextFlag(1)))
fs = fs.Add(CodecContextFlag(2))
require.True(t, fs.Has(CodecContextFlag(2)))
fs = fs.Del(CodecContextFlag(2))
require.False(t, fs.Has(CodecContextFlag(2)))
}
func TestCodecContextFlags2(t *testing.T) {
fs := astiav.NewCodecContextFlags2(astiav.CodecContextFlag2(1))
require.True(t, fs.Has(astiav.CodecContextFlag2(1)))
fs = fs.Add(astiav.CodecContextFlag2(2))
require.True(t, fs.Has(astiav.CodecContextFlag2(2)))
fs = fs.Del(astiav.CodecContextFlag2(2))
require.False(t, fs.Has(astiav.CodecContextFlag2(2)))
fs := NewCodecContextFlags2(CodecContextFlag2(1))
require.True(t, fs.Has(CodecContextFlag2(1)))
fs = fs.Add(CodecContextFlag2(2))
require.True(t, fs.Has(CodecContextFlag2(2)))
fs = fs.Del(CodecContextFlag2(2))
require.False(t, fs.Has(CodecContextFlag2(2)))
}
func TestCodecHardwareConfigMethodFlags(t *testing.T) {
fs := astiav.NewCodecHardwareConfigMethodFlags(astiav.CodecHardwareConfigMethodFlag(1))
require.True(t, fs.Has(astiav.CodecHardwareConfigMethodFlag(1)))
fs = fs.Add(astiav.CodecHardwareConfigMethodFlag(2))
require.True(t, fs.Has(astiav.CodecHardwareConfigMethodFlag(2)))
fs = fs.Del(astiav.CodecHardwareConfigMethodFlag(2))
require.False(t, fs.Has(astiav.CodecHardwareConfigMethodFlag(2)))
fs := NewCodecHardwareConfigMethodFlags(CodecHardwareConfigMethodFlag(1))
require.True(t, fs.Has(CodecHardwareConfigMethodFlag(1)))
fs = fs.Add(CodecHardwareConfigMethodFlag(2))
require.True(t, fs.Has(CodecHardwareConfigMethodFlag(2)))
fs = fs.Del(CodecHardwareConfigMethodFlag(2))
require.False(t, fs.Has(CodecHardwareConfigMethodFlag(2)))
}
func TestDictionaryFlags(t *testing.T) {
fs := astiav.NewDictionaryFlags(astiav.DictionaryFlag(1))
require.True(t, fs.Has(astiav.DictionaryFlag(1)))
fs = fs.Add(astiav.DictionaryFlag(2))
require.True(t, fs.Has(astiav.DictionaryFlag(2)))
fs = fs.Del(astiav.DictionaryFlag(2))
require.False(t, fs.Has(astiav.DictionaryFlag(2)))
fs := NewDictionaryFlags(DictionaryFlag(1))
require.True(t, fs.Has(DictionaryFlag(1)))
fs = fs.Add(DictionaryFlag(2))
require.True(t, fs.Has(DictionaryFlag(2)))
fs = fs.Del(DictionaryFlag(2))
require.False(t, fs.Has(DictionaryFlag(2)))
}
func TestFilterCommandFlags(t *testing.T) {
fs := astiav.NewFilterCommandFlags(astiav.FilterCommandFlag(1))
require.True(t, fs.Has(astiav.FilterCommandFlag(1)))
fs = fs.Add(astiav.FilterCommandFlag(2))
require.True(t, fs.Has(astiav.FilterCommandFlag(2)))
fs = fs.Del(astiav.FilterCommandFlag(2))
require.False(t, fs.Has(astiav.FilterCommandFlag(2)))
fs := NewFilterCommandFlags(FilterCommandFlag(1))
require.True(t, fs.Has(FilterCommandFlag(1)))
fs = fs.Add(FilterCommandFlag(2))
require.True(t, fs.Has(FilterCommandFlag(2)))
fs = fs.Del(FilterCommandFlag(2))
require.False(t, fs.Has(FilterCommandFlag(2)))
}
func TestFormatContextFlags(t *testing.T) {
fs := astiav.NewFormatContextFlags(astiav.FormatContextFlag(1))
require.True(t, fs.Has(astiav.FormatContextFlag(1)))
fs = fs.Add(astiav.FormatContextFlag(2))
require.True(t, fs.Has(astiav.FormatContextFlag(2)))
fs = fs.Del(astiav.FormatContextFlag(2))
require.False(t, fs.Has(astiav.FormatContextFlag(2)))
fs := NewFormatContextFlags(FormatContextFlag(1))
require.True(t, fs.Has(FormatContextFlag(1)))
fs = fs.Add(FormatContextFlag(2))
require.True(t, fs.Has(FormatContextFlag(2)))
fs = fs.Del(FormatContextFlag(2))
require.False(t, fs.Has(FormatContextFlag(2)))
}
func TestFormatContextCtxFlags(t *testing.T) {
fs := astiav.NewFormatContextCtxFlags(astiav.FormatContextCtxFlag(1))
require.True(t, fs.Has(astiav.FormatContextCtxFlag(1)))
fs = fs.Add(astiav.FormatContextCtxFlag(2))
require.True(t, fs.Has(astiav.FormatContextCtxFlag(2)))
fs = fs.Del(astiav.FormatContextCtxFlag(2))
require.False(t, fs.Has(astiav.FormatContextCtxFlag(2)))
fs := NewFormatContextCtxFlags(FormatContextCtxFlag(1))
require.True(t, fs.Has(FormatContextCtxFlag(1)))
fs = fs.Add(FormatContextCtxFlag(2))
require.True(t, fs.Has(FormatContextCtxFlag(2)))
fs = fs.Del(FormatContextCtxFlag(2))
require.False(t, fs.Has(FormatContextCtxFlag(2)))
}
func TestFormatEventFlags(t *testing.T) {
fs := astiav.NewFormatEventFlags(astiav.FormatEventFlag(1))
require.True(t, fs.Has(astiav.FormatEventFlag(1)))
fs = fs.Add(astiav.FormatEventFlag(2))
require.True(t, fs.Has(astiav.FormatEventFlag(2)))
fs = fs.Del(astiav.FormatEventFlag(2))
require.False(t, fs.Has(astiav.FormatEventFlag(2)))
fs := NewFormatEventFlags(FormatEventFlag(1))
require.True(t, fs.Has(FormatEventFlag(1)))
fs = fs.Add(FormatEventFlag(2))
require.True(t, fs.Has(FormatEventFlag(2)))
fs = fs.Del(FormatEventFlag(2))
require.False(t, fs.Has(FormatEventFlag(2)))
}
func TestIOContextFlags(t *testing.T) {
fs := astiav.NewIOContextFlags(astiav.IOContextFlag(1))
require.True(t, fs.Has(astiav.IOContextFlag(1)))
fs = fs.Add(astiav.IOContextFlag(2))
require.True(t, fs.Has(astiav.IOContextFlag(2)))
fs = fs.Del(astiav.IOContextFlag(2))
require.False(t, fs.Has(astiav.IOContextFlag(2)))
fs := NewIOContextFlags(IOContextFlag(1))
require.True(t, fs.Has(IOContextFlag(1)))
fs = fs.Add(IOContextFlag(2))
require.True(t, fs.Has(IOContextFlag(2)))
fs = fs.Del(IOContextFlag(2))
require.False(t, fs.Has(IOContextFlag(2)))
}
func TestIOFormatFlags(t *testing.T) {
fs := astiav.NewIOFormatFlags(astiav.IOFormatFlag(1))
require.True(t, fs.Has(astiav.IOFormatFlag(1)))
fs = fs.Add(astiav.IOFormatFlag(2))
require.True(t, fs.Has(astiav.IOFormatFlag(2)))
fs = fs.Del(astiav.IOFormatFlag(2))
require.False(t, fs.Has(astiav.IOFormatFlag(2)))
fs := NewIOFormatFlags(IOFormatFlag(1))
require.True(t, fs.Has(IOFormatFlag(1)))
fs = fs.Add(IOFormatFlag(2))
require.True(t, fs.Has(IOFormatFlag(2)))
fs = fs.Del(IOFormatFlag(2))
require.False(t, fs.Has(IOFormatFlag(2)))
}
func TestPacketFlags(t *testing.T) {
fs := astiav.NewPacketFlags(astiav.PacketFlag(1))
require.True(t, fs.Has(astiav.PacketFlag(1)))
fs = fs.Add(astiav.PacketFlag(2))
require.True(t, fs.Has(astiav.PacketFlag(2)))
fs = fs.Del(astiav.PacketFlag(2))
require.False(t, fs.Has(astiav.PacketFlag(2)))
fs := NewPacketFlags(PacketFlag(1))
require.True(t, fs.Has(PacketFlag(1)))
fs = fs.Add(PacketFlag(2))
require.True(t, fs.Has(PacketFlag(2)))
fs = fs.Del(PacketFlag(2))
require.False(t, fs.Has(PacketFlag(2)))
}
func TestSeekFlags(t *testing.T) {
fs := astiav.NewSeekFlags(astiav.SeekFlag(1))
require.True(t, fs.Has(astiav.SeekFlag(1)))
fs = fs.Add(astiav.SeekFlag(2))
require.True(t, fs.Has(astiav.SeekFlag(2)))
fs = fs.Del(astiav.SeekFlag(2))
require.False(t, fs.Has(astiav.SeekFlag(2)))
fs := NewSeekFlags(SeekFlag(1))
require.True(t, fs.Has(SeekFlag(1)))
fs = fs.Add(SeekFlag(2))
require.True(t, fs.Has(SeekFlag(2)))
fs = fs.Del(SeekFlag(2))
require.False(t, fs.Has(SeekFlag(2)))
}
func TestSoftwareScaleContextFlags(t *testing.T) {
fs := astiav.NewSoftwareScaleContextFlags(astiav.SoftwareScaleContextFlag(1))
require.True(t, fs.Has(astiav.SoftwareScaleContextFlag(1)))
fs = fs.Add(astiav.SoftwareScaleContextFlag(2))
require.True(t, fs.Has(astiav.SoftwareScaleContextFlag(2)))
fs = fs.Del(astiav.SoftwareScaleContextFlag(2))
require.False(t, fs.Has(astiav.SoftwareScaleContextFlag(2)))
fs := NewSoftwareScaleContextFlags(SoftwareScaleContextFlag(1))
require.True(t, fs.Has(SoftwareScaleContextFlag(1)))
fs = fs.Add(SoftwareScaleContextFlag(2))
require.True(t, fs.Has(SoftwareScaleContextFlag(2)))
fs = fs.Del(SoftwareScaleContextFlag(2))
require.False(t, fs.Has(SoftwareScaleContextFlag(2)))
}
func TestStreamEventFlags(t *testing.T) {
fs := astiav.NewStreamEventFlags(astiav.StreamEventFlag(1))
require.True(t, fs.Has(astiav.StreamEventFlag(1)))
fs = fs.Add(astiav.StreamEventFlag(2))
require.True(t, fs.Has(astiav.StreamEventFlag(2)))
fs = fs.Del(astiav.StreamEventFlag(2))
require.False(t, fs.Has(astiav.StreamEventFlag(2)))
fs := NewStreamEventFlags(StreamEventFlag(1))
require.True(t, fs.Has(StreamEventFlag(1)))
fs = fs.Add(StreamEventFlag(2))
require.True(t, fs.Has(StreamEventFlag(2)))
fs = fs.Del(StreamEventFlag(2))
require.False(t, fs.Has(StreamEventFlag(2)))
}

View File

@@ -29,22 +29,22 @@ type FormatContext struct {
c *C.struct_AVFormatContext
}
func newFormatContext() *FormatContext {
return &FormatContext{}
}
func newFormatContextFromC(c *C.struct_AVFormatContext) *FormatContext {
if c == nil {
return nil
}
return &FormatContext{c: c}
fc := &FormatContext{c: c}
classers.set(fc)
return fc
}
var _ Classer = (*FormatContext)(nil)
func AllocFormatContext() *FormatContext {
return newFormatContextFromC(C.avformat_alloc_context())
}
func AllocOutputFormatContext(o *OutputFormat, formatName, filename string) (*FormatContext, error) {
func AllocOutputFormatContext(of *OutputFormat, formatName, filename string) (*FormatContext, error) {
fonc := (*C.char)(nil)
if len(formatName) > 0 {
fonc = C.CString(formatName)
@@ -55,19 +55,30 @@ func AllocOutputFormatContext(o *OutputFormat, formatName, filename string) (*Fo
finc = C.CString(filename)
defer C.free(unsafe.Pointer(finc))
}
fc := newFormatContext()
var oc *C.struct_AVOutputFormat
if o != nil {
oc = o.c
var ofc *C.struct_AVOutputFormat
if of != nil {
ofc = of.c
}
err := newError(C.avformat_alloc_output_context2(&fc.c, oc, fonc, finc))
return fc, err
var fcc *C.struct_AVFormatContext
if err := newError(C.avformat_alloc_output_context2(&fcc, ofc, fonc, finc)); err != nil {
return nil, err
}
return newFormatContextFromC(fcc), nil
}
func (fc *FormatContext) Free() {
classers.del(fc)
C.avformat_free_context(fc.c)
}
func (fc *FormatContext) BitRate() int64 {
return int64(fc.c.bit_rate)
}
func (fc *FormatContext) Class() *Class {
return newClassFromC(unsafe.Pointer(fc.c))
}
func (fc *FormatContext) CtxFlags() FormatContextCtxFlags {
return FormatContextCtxFlags(fc.c.ctx_flags)
}
@@ -115,6 +126,13 @@ func (fc *FormatContext) OutputFormat() *OutputFormat {
}
func (fc *FormatContext) Pb() *IOContext {
// If the io context has been created using the format context's OpenInput() method, we need to
// make sure to return the same go struct as the one stored in classers
if c, ok := classers.get(unsafe.Pointer(fc.c.pb)); ok {
if v, ok := c.(*IOContext); ok {
return v
}
}
return newIOContextFromC(fc.c.pb)
}
@@ -153,15 +171,23 @@ func (fc *FormatContext) OpenInput(url string, fmt *InputFormat, d *Dictionary)
if fmt != nil {
fmtc = fmt.c
}
return newError(C.avformat_open_input(&fc.c, urlc, fmtc, dc))
if err := newError(C.avformat_open_input(&fc.c, urlc, fmtc, dc)); err != nil {
return err
}
if pb := fc.Pb(); pb != nil {
classers.set(pb)
}
return nil
}
func (fc *FormatContext) CloseInput() {
C.avformat_close_input(&fc.c)
}
func (fc *FormatContext) Free() {
C.avformat_free_context(fc.c)
if pb := fc.Pb(); pb != nil {
classers.del(pb)
}
classers.del(fc)
if fc.c != nil {
C.avformat_close_input(&fc.c)
}
}
func (fc *FormatContext) NewStream(c *Codec) *Stream {

View File

@@ -1,9 +1,8 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -15,40 +14,42 @@ func TestFormatContext(t *testing.T) {
s1 := ss[0]
require.Equal(t, int64(607583), fc1.BitRate())
require.Equal(t, astiav.NewFormatContextCtxFlags(0), fc1.CtxFlags())
require.Equal(t, NewFormatContextCtxFlags(0), fc1.CtxFlags())
require.Equal(t, int64(5014000), fc1.Duration())
require.True(t, fc1.EventFlags().Has(astiav.FormatEventFlagMetadataUpdated))
require.True(t, fc1.Flags().Has(astiav.FormatContextFlagAutoBsf))
require.Equal(t, astiav.NewRational(24, 1), fc1.GuessFrameRate(s1, nil))
require.Equal(t, astiav.NewRational(1, 1), fc1.GuessSampleAspectRatio(s1, nil))
require.True(t, fc1.InputFormat().Flags().Has(astiav.IOFormatFlagNoByteSeek))
require.Equal(t, astiav.IOContextFlags(0), fc1.IOFlags())
require.True(t, fc1.EventFlags().Has(FormatEventFlagMetadataUpdated))
require.True(t, fc1.Flags().Has(FormatContextFlagAutoBsf))
require.Equal(t, NewRational(24, 1), fc1.GuessFrameRate(s1, nil))
require.Equal(t, NewRational(1, 1), fc1.GuessSampleAspectRatio(s1, nil))
require.True(t, fc1.InputFormat().Flags().Has(IOFormatFlagNoByteSeek))
require.Equal(t, IOContextFlags(0), fc1.IOFlags())
require.Equal(t, int64(0), fc1.MaxAnalyzeDuration())
require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, astiav.NewDictionaryFlags()).Value())
require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, NewDictionaryFlags()).Value())
require.Equal(t, int64(0), fc1.StartTime())
require.Equal(t, 2, fc1.NbStreams())
require.Len(t, fc1.Streams(), 2)
cl := fc1.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFormatContext", cl.Name())
sdp, err := fc1.SDPCreate()
require.NoError(t, err)
require.Equal(t, "v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\ns=Big Buck Bunny\r\nt=0 0\r\na=tool:libavformat LIBAVFORMAT_VERSION\r\nm=video 0 RTP/AVP 96\r\nb=AS:441\r\na=rtpmap:96 H264/90000\r\na=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LADasgKDPz4CIAAAMAAgAAAwBhHihUkA==,aM48gA==; profile-level-id=42C00D\r\na=control:streamid=0\r\nm=audio 0 RTP/AVP 97\r\nb=AS:161\r\na=rtpmap:97 MPEG4-GENERIC/48000/2\r\na=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190\r\na=control:streamid=1\r\n", sdp)
fc2, err := astiav.AllocOutputFormatContext(nil, "", "/tmp/test.mp4")
fc2, err := AllocOutputFormatContext(nil, "", "/tmp/test.mp4")
require.NoError(t, err)
defer fc2.Free()
require.True(t, fc2.OutputFormat().Flags().Has(astiav.IOFormatFlagGlobalheader))
require.True(t, fc2.OutputFormat().Flags().Has(IOFormatFlagGlobalheader))
fc3 := astiav.AllocFormatContext()
fc3 := AllocFormatContext()
require.NotNil(t, fc3)
defer fc3.Free()
c := astiav.NewIOContext()
err = c.Open("testdata/video.mp4", astiav.NewIOContextFlags(astiav.IOContextFlagRead))
c, err := OpenIOContext("testdata/video.mp4", NewIOContextFlags(IOContextFlagRead))
require.NoError(t, err)
defer c.Closep() //nolint:errcheck
fc3.SetPb(c)
fc3.SetStrictStdCompliance(astiav.StrictStdComplianceExperimental)
fc3.SetStrictStdCompliance(StrictStdComplianceExperimental)
require.NotNil(t, fc3.Pb())
require.Equal(t, astiav.StrictStdComplianceExperimental, fc3.StrictStdCompliance())
require.Equal(t, StrictStdComplianceExperimental, fc3.StrictStdCompliance())
s2 := fc3.NewStream(nil)
require.NotNil(t, s2)
s3 := fc3.NewStream(nil)

View File

@@ -214,6 +214,6 @@ func (f *Frame) MoveRef(src *Frame) {
C.av_frame_move_ref(f.c, src.c)
}
func (f *Frame) UnsafePointer() *C.struct_AVFrame {
func (f *Frame) UnsafeTypedPointer() *C.struct_AVFrame {
return f.c
}

View File

@@ -1,285 +0,0 @@
package astiav
import (
"image"
"testing"
"github.com/stretchr/testify/require"
)
type mockedFrameDataFrame struct {
h int
imageBytes []byte
linesizes []int
pf PixelFormat
planesBytes [][]byte
w int
}
var _ frameDataFramer = (*mockedFrameDataFrame)(nil)
func (f *mockedFrameDataFrame) height() int {
return f.h
}
func (f *mockedFrameDataFrame) imageBufferSize(align int) (int, error) {
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) imageCopyToBuffer(b []byte, align int) (int, error) {
copy(b, f.imageBytes)
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) linesize(i int) int {
return f.linesizes[i]
}
func (f *mockedFrameDataFrame) pixelFormat() PixelFormat {
return f.pf
}
func (f *mockedFrameDataFrame) planeBytes(i int) []byte {
return f.planesBytes[i]
}
func (f *mockedFrameDataFrame) width() int {
return f.w
}
func TestFrameDataInternal(t *testing.T) {
fdf := &mockedFrameDataFrame{}
fd := newFrameData(fdf)
for _, v := range []struct {
err bool
i image.Image
pfs []PixelFormat
}{
{
i: &image.Gray{},
pfs: []PixelFormat{PixelFormatGray8},
},
{
i: &image.Gray16{},
pfs: []PixelFormat{PixelFormatGray16Be},
},
{
i: &image.RGBA{},
pfs: []PixelFormat{
PixelFormatRgb0,
PixelFormat0Rgb,
PixelFormatRgb4,
PixelFormatRgb8,
},
},
{
i: &image.NRGBA{},
pfs: []PixelFormat{PixelFormatRgba},
},
{
i: &image.NRGBA64{},
pfs: []PixelFormat{PixelFormatRgba64Be},
},
{
i: &image.NYCbCrA{},
pfs: []PixelFormat{
PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P,
},
},
{
i: &image.YCbCr{},
pfs: []PixelFormat{
PixelFormatYuv410P,
PixelFormatYuv411P,
PixelFormatYuvj411P,
PixelFormatYuv420P,
PixelFormatYuvj420P,
PixelFormatYuv422P,
PixelFormatYuvj422P,
PixelFormatYuv440P,
PixelFormatYuvj440P,
PixelFormatYuv444P,
PixelFormatYuvj444P,
},
},
{
err: true,
pfs: []PixelFormat{PixelFormatAbgr},
},
} {
for _, pf := range v.pfs {
fdf.pf = pf
i, err := fd.GuessImageFormat()
if v.err {
require.Error(t, err)
} else {
require.IsType(t, v.i, i)
}
}
}
fdf.imageBytes = []byte{0, 1, 2, 3}
_, err := fd.Bytes(0)
require.Error(t, err)
fdf.h = 1
fdf.w = 2
b, err := fd.Bytes(0)
require.NoError(t, err)
require.Equal(t, fdf.imageBytes, b)
for _, v := range []struct {
e image.Image
err bool
i image.Image
linesizes []int
pixelFormat PixelFormat
planesBytes [][]byte
}{
{
e: &image.Alpha{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Alpha16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.CMYK{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.CMYK{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NYCbCrA{
A: []byte{6, 7},
AStride: 4,
YCbCr: image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, 2, 1),
},
},
i: &image.NYCbCrA{},
linesizes: []int{1, 2, 3, 4},
pixelFormat: PixelFormatYuv444P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}, {6, 7}},
},
{
e: &image.RGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.RGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.YCbCr{},
linesizes: []int{1, 2, 3},
pixelFormat: PixelFormatYuv420P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}},
},
} {
fdf.linesizes = v.linesizes
fdf.pf = v.pixelFormat
fdf.planesBytes = v.planesBytes
err = fd.ToImage(v.i)
if v.err {
require.Error(t, err)
} else {
require.Equal(t, v.e, v.i)
}
}
}

View File

@@ -1,20 +1,297 @@
package astiav_test
package astiav
import (
"image"
"image/png"
"os"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
type mockedFrameDataFrame struct {
h int
imageBytes []byte
linesizes []int
pf PixelFormat
planesBytes [][]byte
w int
}
var _ frameDataFramer = (*mockedFrameDataFrame)(nil)
func (f *mockedFrameDataFrame) height() int {
return f.h
}
func (f *mockedFrameDataFrame) imageBufferSize(align int) (int, error) {
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) imageCopyToBuffer(b []byte, align int) (int, error) {
copy(b, f.imageBytes)
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) linesize(i int) int {
return f.linesizes[i]
}
func (f *mockedFrameDataFrame) pixelFormat() PixelFormat {
return f.pf
}
func (f *mockedFrameDataFrame) planeBytes(i int) []byte {
return f.planesBytes[i]
}
func (f *mockedFrameDataFrame) width() int {
return f.w
}
func TestFrameDataInternal(t *testing.T) {
fdf := &mockedFrameDataFrame{}
fd := newFrameData(fdf)
for _, v := range []struct {
err bool
i image.Image
pfs []PixelFormat
}{
{
i: &image.Gray{},
pfs: []PixelFormat{PixelFormatGray8},
},
{
i: &image.Gray16{},
pfs: []PixelFormat{PixelFormatGray16Be},
},
{
i: &image.RGBA{},
pfs: []PixelFormat{
PixelFormatRgb0,
PixelFormat0Rgb,
PixelFormatRgb4,
PixelFormatRgb8,
},
},
{
i: &image.NRGBA{},
pfs: []PixelFormat{PixelFormatRgba},
},
{
i: &image.NRGBA64{},
pfs: []PixelFormat{PixelFormatRgba64Be},
},
{
i: &image.NYCbCrA{},
pfs: []PixelFormat{
PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P,
},
},
{
i: &image.YCbCr{},
pfs: []PixelFormat{
PixelFormatYuv410P,
PixelFormatYuv411P,
PixelFormatYuvj411P,
PixelFormatYuv420P,
PixelFormatYuvj420P,
PixelFormatYuv422P,
PixelFormatYuvj422P,
PixelFormatYuv440P,
PixelFormatYuvj440P,
PixelFormatYuv444P,
PixelFormatYuvj444P,
},
},
{
err: true,
pfs: []PixelFormat{PixelFormatAbgr},
},
} {
for _, pf := range v.pfs {
fdf.pf = pf
i, err := fd.GuessImageFormat()
if v.err {
require.Error(t, err)
} else {
require.IsType(t, v.i, i)
}
}
}
fdf.imageBytes = []byte{0, 1, 2, 3}
_, err := fd.Bytes(0)
require.Error(t, err)
fdf.h = 1
fdf.w = 2
b, err := fd.Bytes(0)
require.NoError(t, err)
require.Equal(t, fdf.imageBytes, b)
for _, v := range []struct {
e image.Image
err bool
i image.Image
linesizes []int
pixelFormat PixelFormat
planesBytes [][]byte
}{
{
e: &image.Alpha{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Alpha16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.CMYK{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.CMYK{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NYCbCrA{
A: []byte{6, 7},
AStride: 4,
YCbCr: image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, 2, 1),
},
},
i: &image.NYCbCrA{},
linesizes: []int{1, 2, 3, 4},
pixelFormat: PixelFormatYuv444P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}, {6, 7}},
},
{
e: &image.RGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.RGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.YCbCr{},
linesizes: []int{1, 2, 3},
pixelFormat: PixelFormatYuv420P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}},
},
} {
fdf.linesizes = v.linesizes
fdf.pf = v.pixelFormat
fdf.planesBytes = v.planesBytes
err = fd.ToImage(v.i)
if v.err {
require.Error(t, err)
} else {
require.Equal(t, v.e, v.i)
}
}
}
func TestFrameData(t *testing.T) {
const (
name = "image-rgba"
ext = "png"
)
f, err := globalHelper.inputLastFrame(name+"."+ext, astiav.MediaTypeVideo)
f, err := globalHelper.inputLastFrame(name+"."+ext, MediaTypeVideo)
require.NoError(t, err)
fd := f.Data()

View File

@@ -1,45 +1,44 @@
package astiav_test
package astiav
import (
"bytes"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestFrame(t *testing.T) {
f1, err := globalHelper.inputLastFrame("video.mp4", astiav.MediaTypeVideo)
f1, err := globalHelper.inputLastFrame("video.mp4", MediaTypeVideo)
require.NoError(t, err)
require.Equal(t, [8]int{384, 192, 192, 0, 0, 0, 0, 0}, f1.Linesize())
require.Equal(t, int64(60928), f1.PktDts())
require.NotNil(t, f1.UnsafePointer())
require.Equal(t, f1.c, f1.UnsafeTypedPointer())
f2 := astiav.AllocFrame()
f2 := AllocFrame()
require.NotNil(t, f2)
defer f2.Free()
f2.SetChannelLayout(astiav.ChannelLayout21)
f2.SetColorRange(astiav.ColorRangeJpeg)
f2.SetChannelLayout(ChannelLayout21)
f2.SetColorRange(ColorRangeJpeg)
f2.SetHeight(2)
f2.SetKeyFrame(true)
f2.SetNbSamples(4)
f2.SetPictureType(astiav.PictureTypeB)
f2.SetPixelFormat(astiav.PixelFormat0Bgr)
require.Equal(t, astiav.PixelFormat0Bgr, f2.PixelFormat()) // Need to test it right away as sample format actually updates the same field
f2.SetPictureType(PictureTypeB)
f2.SetPixelFormat(PixelFormat0Bgr)
require.Equal(t, PixelFormat0Bgr, f2.PixelFormat()) // Need to test it right away as sample format actually updates the same field
f2.SetPts(7)
f2.SetSampleAspectRatio(astiav.NewRational(10, 2))
f2.SetSampleFormat(astiav.SampleFormatDbl)
require.Equal(t, astiav.SampleFormatDbl, f2.SampleFormat())
f2.SetSampleAspectRatio(NewRational(10, 2))
f2.SetSampleFormat(SampleFormatDbl)
require.Equal(t, SampleFormatDbl, f2.SampleFormat())
f2.SetSampleRate(9)
f2.SetWidth(10)
require.True(t, f2.ChannelLayout().Equal(astiav.ChannelLayout21))
require.Equal(t, astiav.ColorRangeJpeg, f2.ColorRange())
require.True(t, f2.ChannelLayout().Equal(ChannelLayout21))
require.Equal(t, ColorRangeJpeg, f2.ColorRange())
require.Equal(t, 2, f2.Height())
require.True(t, f2.KeyFrame())
require.Equal(t, 4, f2.NbSamples())
require.Equal(t, astiav.PictureTypeB, f2.PictureType())
require.Equal(t, PictureTypeB, f2.PictureType())
require.Equal(t, int64(7), f2.Pts())
require.Equal(t, astiav.NewRational(10, 2), f2.SampleAspectRatio())
require.Equal(t, NewRational(10, 2), f2.SampleAspectRatio())
require.Equal(t, 9, f2.SampleRate())
require.Equal(t, 10, f2.Width())
@@ -61,40 +60,40 @@ func TestFrame(t *testing.T) {
f3.Unref()
require.Equal(t, 0, f3.Height())
f4 := astiav.AllocFrame()
f4 := AllocFrame()
require.NotNil(t, f4)
defer f4.Free()
f4.SetNbSamples(960)
f4.SetChannelLayout(astiav.ChannelLayoutStereo)
f4.SetSampleFormat(astiav.SampleFormatS16)
f4.SetChannelLayout(ChannelLayoutStereo)
f4.SetSampleFormat(SampleFormatS16)
f4.SetSampleRate(48000)
err = f4.AllocBuffer(0)
require.NoError(t, err)
err = f4.AllocSamples(0)
require.NoError(t, err)
f5 := astiav.AllocFrame()
f5 := AllocFrame()
require.NotNil(t, f5)
defer f5.Free()
sd := f5.NewSideData(astiav.FrameSideDataTypeAudioServiceType, 4)
sd := f5.NewSideData(FrameSideDataTypeAudioServiceType, 4)
require.NotNil(t, sd)
sd.SetData([]byte{1, 2, 3})
sd = f5.SideData(astiav.FrameSideDataTypeAudioServiceType)
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, astiav.FrameSideDataTypeAudioServiceType, sd.Type())
require.Equal(t, FrameSideDataTypeAudioServiceType, sd.Type())
require.True(t, bytes.HasPrefix(sd.Data(), []byte{1, 2, 3}))
require.Len(t, sd.Data(), 4)
sd.SetData([]byte{1, 2, 3, 4, 5})
sd = f5.SideData(astiav.FrameSideDataTypeAudioServiceType)
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, []byte{1, 2, 3, 4}, sd.Data())
f6 := astiav.AllocFrame()
f6 := AllocFrame()
require.NotNil(t, f6)
defer f6.Free()
f6.SetColorRange(astiav.ColorRangeUnspecified)
f6.SetColorRange(ColorRangeUnspecified)
f6.SetHeight(2)
f6.SetPixelFormat(astiav.PixelFormatYuv420P)
f6.SetPixelFormat(PixelFormatYuv420P)
f6.SetWidth(4)
const align = 1
require.NoError(t, f6.AllocBuffer(align))

View File

@@ -1,13 +1,12 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestHardwareDeviceType(t *testing.T) {
require.Equal(t, "cuda", astiav.HardwareDeviceTypeCUDA.String())
require.Equal(t, astiav.FindHardwareDeviceTypeByName("cuda"), astiav.HardwareDeviceTypeCUDA)
require.Equal(t, "cuda", HardwareDeviceTypeCUDA.String())
require.Equal(t, FindHardwareDeviceTypeByName("cuda"), HardwareDeviceTypeCUDA)
}

View File

@@ -1,15 +1,14 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestInputFormat(t *testing.T) {
formatName := "rawvideo"
inputFormat := astiav.FindInputFormat(formatName)
inputFormat := FindInputFormat(formatName)
require.NotNil(t, inputFormat)
require.Equal(t, formatName, inputFormat.Name())
require.Equal(t, formatName, inputFormat.String())

View File

@@ -1,16 +1,15 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestIntReadWrite(t *testing.T) {
is := []uint8{1, 2, 3, 4, 5, 6, 7, 8}
require.Equal(t, uint32(0), astiav.RL32([]byte{}))
require.Equal(t, uint32(0x4030201), astiav.RL32(is))
require.Equal(t, uint32(0), astiav.RL32WithOffset([]byte{}, 4))
require.Equal(t, uint32(0x8070605), astiav.RL32WithOffset(is, 4))
require.Equal(t, uint32(0), RL32([]byte{}))
require.Equal(t, uint32(0x4030201), RL32(is))
require.Equal(t, uint32(0), RL32WithOffset([]byte{}, 4))
require.Equal(t, uint32(0x8070605), RL32WithOffset(is, 4))
}

View File

@@ -61,21 +61,20 @@ func (fs {{ $val.Name }}Flags{{ $val.Suffix }}) Has(f {{ $val.Name }}Flag{{ $val
{{ end }}`
var tmplTest = `// Code generated by astiav. DO NOT EDIT.
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
{{ range $val := . }}
func Test{{ $val.Name }}Flags{{ $val.Suffix }}(t *testing.T) {
fs := astiav.New{{ $val.Name }}Flags{{ $val.Suffix }}(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(1))
require.True(t, fs.Has(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(1)))
fs = fs.Add(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.True(t, fs.Has(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2)))
fs = fs.Del(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.False(t, fs.Has(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2)))
fs := New{{ $val.Name }}Flags{{ $val.Suffix }}({{ $val.Name }}Flag{{ $val.Suffix }}(1))
require.True(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(1)))
fs = fs.Add({{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.True(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(2)))
fs = fs.Del({{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.False(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(2)))
}
{{ end }}`

View File

@@ -10,25 +10,37 @@ type IOContext struct {
c *C.struct_AVIOContext
}
func NewIOContext() *IOContext {
return &IOContext{}
}
func newIOContextFromC(c *C.struct_AVIOContext) *IOContext {
if c == nil {
return nil
}
return &IOContext{c: c}
ic := &IOContext{c: c}
classers.set(ic)
return ic
}
var _ Classer = (*IOContext)(nil)
func OpenIOContext(filename string, flags IOContextFlags) (*IOContext, error) {
cfi := C.CString(filename)
defer C.free(unsafe.Pointer(cfi))
var c *C.struct_AVIOContext
if err := newError(C.avio_open(&c, cfi, C.int(flags))); err != nil {
return nil, err
}
return newIOContextFromC(c), nil
}
func (ic *IOContext) Class() *Class {
return newClassFromC(unsafe.Pointer(ic.c))
}
func (ic *IOContext) Closep() error {
return newError(C.avio_closep(&ic.c))
}
func (ic *IOContext) Open(filename string, flags IOContextFlags) error {
cfi := C.CString(filename)
defer C.free(unsafe.Pointer(cfi))
return newError(C.avio_open(&ic.c, cfi, C.int(flags)))
classers.del(ic)
if ic.c != nil {
return newError(C.avio_closep(&ic.c))
}
return nil
}
func (ic *IOContext) Write(b []byte) {

View File

@@ -1,19 +1,20 @@
package astiav_test
package astiav
import (
"os"
"path/filepath"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestIOContext(t *testing.T) {
c := astiav.NewIOContext()
path := filepath.Join(t.TempDir(), "iocontext.txt")
err := c.Open(path, astiav.NewIOContextFlags(astiav.IOContextFlagWrite))
c, err := OpenIOContext(path, NewIOContextFlags(IOContextFlagWrite))
require.NoError(t, err)
cl := c.Class()
require.NotNil(t, cl)
require.Equal(t, "AVIOContext", cl.Name())
c.Write(nil)
c.Write([]byte("test"))
err = c.Closep()

60
log.go
View File

@@ -5,19 +5,14 @@ package astiav
//#include <stdio.h>
//#include <stdlib.h>
/*
extern void goAstiavLogCallback(int level, char* fmt, char* msg, char* parent);
extern void goAstiavLogCallback(void* ptr, int level, char* fmt, char* msg);
static inline void astiavLogCallback(void *avcl, int level, const char *fmt, va_list vl)
static inline void astiavLogCallback(void *ptr, int level, const char *fmt, va_list vl)
{
if (level > av_log_get_level()) return;
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
char parent[1024];
if (avc) {
sprintf(parent, "%p", avcl);
}
char msg[1024];
vsprintf(msg, fmt, vl);
goAstiavLogCallback(level, (char*)(fmt), msg, parent);
goAstiavLogCallback(ptr, level, (char*)(fmt), msg);
}
static inline void astiavSetLogCallback()
{
@@ -27,13 +22,15 @@ static inline void astiavResetLogCallback()
{
av_log_set_callback(av_log_default_callback);
}
static inline void astiavLog(int level, const char *fmt)
static inline void astiavLog(void* ptr, int level, const char *fmt, char* arg)
{
av_log(NULL, level, fmt, NULL);
av_log(ptr, level, fmt, arg);
}
*/
import "C"
import "unsafe"
import (
"unsafe"
)
type LogLevel int
@@ -53,7 +50,11 @@ func SetLogLevel(l LogLevel) {
C.av_log_set_level(C.int(l))
}
type LogCallback func(l LogLevel, fmt, msg, parent string)
func GetLogLevel() LogLevel {
return LogLevel(C.av_log_get_level())
}
type LogCallback func(c Classer, l LogLevel, fmt, msg string)
var logCallback LogCallback
@@ -63,19 +64,42 @@ func SetLogCallback(c LogCallback) {
}
//export goAstiavLogCallback
func goAstiavLogCallback(level C.int, fmt, msg, parent *C.char) {
func goAstiavLogCallback(ptr unsafe.Pointer, level C.int, fmt, msg *C.char) {
// No callback
if logCallback == nil {
return
}
logCallback(LogLevel(level), C.GoString(fmt), C.GoString(msg), C.GoString(parent))
// Get classer
var c Classer
if ptr != nil {
var ok bool
if c, ok = classers.get(ptr); !ok {
c = newUnknownClasser(ptr)
}
}
// Callback
logCallback(c, LogLevel(level), C.GoString(fmt), C.GoString(msg))
}
func ResetLogCallback() {
C.astiavResetLogCallback()
}
func Log(l LogLevel, msg string) {
msgc := C.CString(msg)
defer C.free(unsafe.Pointer(msgc))
C.astiavLog(C.int(l), msgc)
func Log(c Classer, l LogLevel, fmt string, args ...string) {
fmtc := C.CString(fmt)
defer C.free(unsafe.Pointer(fmtc))
argc := (*C.char)(nil)
if len(args) > 0 {
argc = C.CString(args[0])
defer C.free(unsafe.Pointer(argc))
}
var ptr unsafe.Pointer
if c != nil {
if cl := c.Class(); cl != nil {
ptr = cl.ptr
}
}
C.astiavLog(ptr, C.int(l), fmtc, argc)
}

View File

@@ -1,51 +1,73 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
type logItem struct {
c Classer
fmt string
l astiav.LogLevel
l LogLevel
msg string
}
func TestLog(t *testing.T) {
var lis []logItem
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
SetLogLevel(LogLevelWarning)
require.Equal(t, LogLevelWarning, GetLogLevel())
SetLogCallback(func(c Classer, l LogLevel, fmt, msg string) {
lis = append(lis, logItem{
c: c,
fmt: fmt,
l: l,
msg: msg,
})
})
astiav.SetLogLevel(astiav.LogLevelWarning)
astiav.Log(astiav.LogLevelInfo, "info")
astiav.Log(astiav.LogLevelWarning, "warning")
astiav.Log(astiav.LogLevelError, "error")
astiav.Log(astiav.LogLevelFatal, "fatal")
f := AllocFilterGraph()
defer f.Free()
Log(f, LogLevelInfo, "info")
Log(f, LogLevelWarning, "warning %s", "arg")
Log(f, LogLevelError, "error")
Log(f, LogLevelFatal, "fatal")
require.Equal(t, []logItem{
{
fmt: "warning",
l: astiav.LogLevelWarning,
msg: "warning",
c: f,
fmt: "warning %s",
l: LogLevelWarning,
msg: "warning arg",
},
{
c: f,
fmt: "error",
l: astiav.LogLevelError,
l: LogLevelError,
msg: "error",
},
{
c: f,
fmt: "fatal",
l: astiav.LogLevelFatal,
l: LogLevelFatal,
msg: "fatal",
},
}, lis)
astiav.ResetLogCallback()
ResetLogCallback()
lis = []logItem{}
astiav.Log(astiav.LogLevelError, "test error log\n")
Log(nil, LogLevelError, "test error log\n")
require.Equal(t, []logItem{}, lis)
lcs := []Classer{}
SetLogCallback(func(c Classer, l LogLevel, fmt, msg string) {
if c != nil {
lcs = append(lcs, c)
}
})
classers.del(f)
lcs = []Classer{}
Log(f, LogLevelWarning, "")
require.Len(t, lcs, 1)
require.IsType(t, &UnknownClasser{}, lcs[0])
}

View File

@@ -1,14 +1,13 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestMathematics(t *testing.T) {
require.Equal(t, int64(1000), astiav.RescaleQ(100, astiav.NewRational(1, 100), astiav.NewRational(1, 1000)))
require.Equal(t, int64(0), astiav.RescaleQRnd(1, astiav.NewRational(1, 100), astiav.NewRational(1, 10), astiav.RoundingDown))
require.Equal(t, int64(1), astiav.RescaleQRnd(1, astiav.NewRational(1, 100), astiav.NewRational(1, 10), astiav.RoundingUp))
require.Equal(t, int64(1000), RescaleQ(100, NewRational(1, 100), NewRational(1, 1000)))
require.Equal(t, int64(0), RescaleQRnd(1, NewRational(1, 100), NewRational(1, 10), RoundingDown))
require.Equal(t, int64(1), RescaleQRnd(1, NewRational(1, 100), NewRational(1, 10), RoundingUp))
}

View File

@@ -1,12 +1,11 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestMediaType(t *testing.T) {
require.Equal(t, "video", astiav.MediaTypeVideo.String())
require.Equal(t, "video", MediaTypeVideo.String())
}

View File

@@ -1,9 +1,8 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -13,13 +12,13 @@ func TestPacket(t *testing.T) {
require.Equal(t, []byte{0x0, 0x0, 0x0, 0xd1, 0x65, 0x88, 0x82, 0x0, 0x1f, 0x5f, 0xff, 0xf8, 0x22, 0x8a, 0x0, 0x2, 0x2d, 0xbe, 0x38, 0xc7, 0x19, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xb9, 0xb8, 0xe6, 0x39, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xc0}, pkt1.Data())
require.Equal(t, int64(0), pkt1.Dts())
require.Equal(t, int64(512), pkt1.Duration())
require.True(t, pkt1.Flags().Has(astiav.PacketFlagKey))
require.True(t, pkt1.Flags().Has(PacketFlagKey))
require.Equal(t, int64(48), pkt1.Pos())
require.Equal(t, int64(0), pkt1.Pts())
require.Equal(t, 213, pkt1.Size())
require.Equal(t, 0, pkt1.StreamIndex())
pkt2 := astiav.AllocPacket()
pkt2 := AllocPacket()
require.NotNil(t, pkt2)
defer pkt2.Free()
require.Nil(t, pkt2.Data())
@@ -28,14 +27,14 @@ func TestPacket(t *testing.T) {
require.Len(t, pkt2.Data(), 5)
pkt2.SetDts(1)
pkt2.SetDuration(2)
pkt2.SetFlags(astiav.NewPacketFlags(3))
pkt2.SetFlags(NewPacketFlags(3))
pkt2.SetPos(4)
pkt2.SetPts(5)
pkt2.SetSize(6)
pkt2.SetStreamIndex(7)
require.Equal(t, int64(1), pkt2.Dts())
require.Equal(t, int64(2), pkt2.Duration())
require.Equal(t, astiav.NewPacketFlags(3), pkt2.Flags())
require.Equal(t, NewPacketFlags(3), pkt2.Flags())
require.Equal(t, int64(4), pkt2.Pos())
require.Equal(t, int64(5), pkt2.Pts())
require.Equal(t, 6, pkt2.Size())
@@ -60,26 +59,26 @@ func TestPacket(t *testing.T) {
pkt3.SetDts(10)
pkt3.SetDuration(20)
pkt3.SetPts(30)
pkt3.RescaleTs(astiav.NewRational(1, 10), astiav.NewRational(1, 1))
pkt3.RescaleTs(NewRational(1, 10), NewRational(1, 1))
require.Equal(t, int64(1), pkt3.Dts())
require.Equal(t, int64(2), pkt3.Duration())
require.Equal(t, int64(3), pkt3.Pts())
pkt4 := astiav.AllocPacket()
pkt4 := AllocPacket()
require.NotNil(t, pkt4)
defer pkt4.Free()
b := []byte("test")
require.NoError(t, pkt4.FromData(b))
require.Equal(t, b, pkt4.Data())
pkt5 := astiav.AllocPacket()
pkt5 := AllocPacket()
require.NotNil(t, pkt5)
defer pkt5.Free()
b = []byte{1, 2, 3, 4}
require.NoError(t, pkt5.AddSideData(astiav.PacketSideDataTypeAudioServiceType, b))
require.Equal(t, b, pkt5.SideData(astiav.PacketSideDataTypeAudioServiceType))
require.NoError(t, pkt5.AddSideData(PacketSideDataTypeAudioServiceType, b))
require.Equal(t, b, pkt5.SideData(PacketSideDataTypeAudioServiceType))
pkt6 := astiav.AllocPacket()
pkt6 := AllocPacket()
require.NotNil(t, pkt6)
defer pkt6.Free()
b = []byte{}

View File

@@ -1,12 +1,11 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestPictureType(t *testing.T) {
require.Equal(t, "I", astiav.PictureTypeI.String())
require.Equal(t, "I", PictureTypeI.String())
}

View File

@@ -1,14 +1,13 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestPixelFormat(t *testing.T) {
p := astiav.FindPixelFormatByName("yuv420p")
require.Equal(t, astiav.PixelFormatYuv420P, p)
p := FindPixelFormatByName("yuv420p")
require.Equal(t, PixelFormatYuv420P, p)
require.Equal(t, "yuv420p", p.String())
}

View File

@@ -1,14 +1,13 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestRational(t *testing.T) {
r := astiav.NewRational(2, 1)
r := NewRational(2, 1)
require.Equal(t, 2, r.Num())
require.Equal(t, 1, r.Den())
r.SetNum(1)

View File

@@ -1,12 +1,11 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestSampleFormat(t *testing.T) {
require.Equal(t, "s16", astiav.SampleFormatS16.String())
require.Equal(t, "s16", SampleFormatS16.String())
}

View File

@@ -5,6 +5,7 @@ package astiav
import "C"
import (
"errors"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libswscale/swscale_internal.h#L300
@@ -31,7 +32,7 @@ type softwareScaleContextUpdate struct {
}
func CreateSoftwareScaleContext(srcW, srcH int, srcFormat PixelFormat, dstW, dstH int, dstFormat PixelFormat, flags SoftwareScaleContextFlags) (*SoftwareScaleContext, error) {
ssc := SoftwareScaleContext{
ssc := &SoftwareScaleContext{
dstFormat: C.enum_AVPixelFormat(dstFormat),
dstH: C.int(dstH),
dstW: C.int(dstW),
@@ -54,7 +55,20 @@ func CreateSoftwareScaleContext(srcW, srcH int, srcFormat PixelFormat, dstW, dst
if ssc.c == nil {
return nil, errors.New("astiav: empty new context")
}
return &ssc, nil
classers.set(ssc)
return ssc, nil
}
func (ssc *SoftwareScaleContext) Free() {
classers.del(ssc)
C.sws_freeContext(ssc.c)
}
var _ Classer = (*SoftwareScaleContext)(nil)
func (ssc *SoftwareScaleContext) Class() *Class {
return newClassFromC(unsafe.Pointer(ssc.c))
}
func (ssc *SoftwareScaleContext) ScaleFrame(src, dst *Frame) error {
@@ -195,9 +209,3 @@ func (ssc *SoftwareScaleContext) SourceResolution() (int, int) {
func (ssc *SoftwareScaleContext) SetSourceResolution(w int, h int) error {
return ssc.update(softwareScaleContextUpdate{srcW: &w, srcH: &h})
}
func (ssc *SoftwareScaleContext) Free() {
if ssc.c != nil {
C.sws_freeContext(ssc.c)
}
}

View File

@@ -1,40 +1,39 @@
package astiav_test
package astiav
import (
"os"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestSoftwareScaleContext(t *testing.T) {
f1 := astiav.AllocFrame()
f1 := AllocFrame()
require.NotNil(t, f1)
defer f1.Free()
f2 := astiav.AllocFrame()
f2 := AllocFrame()
require.NotNil(t, f2)
defer f2.Free()
f3 := astiav.AllocFrame()
f3 := AllocFrame()
require.NotNil(t, f3)
defer f3.Free()
srcW := 4
srcH := 2
srcPixelFormat := astiav.PixelFormatYuv420P
srcPixelFormat := PixelFormatYuv420P
dstW := 8
dstH := 4
dstPixelFormat := astiav.PixelFormatRgba
swscf1 := astiav.SoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagBilinear)
dstPixelFormat := PixelFormatRgba
swscf1 := SoftwareScaleContextFlags(SoftwareScaleContextFlagBilinear)
f1.SetHeight(srcH)
f1.SetWidth(srcW)
f1.SetPixelFormat(srcPixelFormat)
require.NoError(t, f1.AllocBuffer(1))
swsc1, err := astiav.CreateSoftwareScaleContext(srcW, srcH, srcPixelFormat, dstW, dstH, dstPixelFormat, swscf1)
swsc1, err := CreateSoftwareScaleContext(srcW, srcH, srcPixelFormat, dstW, dstH, dstPixelFormat, swscf1)
require.NoError(t, err)
defer swsc1.Free()
require.Equal(t, dstH, swsc1.DestinationHeight())
@@ -50,6 +49,9 @@ func TestSoftwareScaleContext(t *testing.T) {
require.Equal(t, w, srcW)
require.Equal(t, h, srcH)
require.Equal(t, srcW, swsc1.SourceWidth())
cl := swsc1.Class()
require.NotNil(t, cl)
require.Equal(t, "SWScaler", cl.Name())
require.NoError(t, swsc1.ScaleFrame(f1, f2))
require.Equal(t, dstH, f2.Height())
@@ -58,11 +60,11 @@ func TestSoftwareScaleContext(t *testing.T) {
dstW = 4
dstH = 3
dstPixelFormat = astiav.PixelFormatYuv420P
swscf2 := astiav.SoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagPoint)
dstPixelFormat = PixelFormatYuv420P
swscf2 := SoftwareScaleContextFlags(SoftwareScaleContextFlagPoint)
srcW = 2
srcH = 1
srcPixelFormat = astiav.PixelFormatRgba
srcPixelFormat = PixelFormatRgba
require.NoError(t, swsc1.SetDestinationHeight(dstH))
require.Equal(t, dstH, swsc1.DestinationHeight())
@@ -91,14 +93,14 @@ func TestSoftwareScaleContext(t *testing.T) {
require.Equal(t, w, srcW)
require.Equal(t, h, srcH)
f4, err := globalHelper.inputLastFrame("image-rgba.png", astiav.MediaTypeVideo)
f4, err := globalHelper.inputLastFrame("image-rgba.png", MediaTypeVideo)
require.NoError(t, err)
f5 := astiav.AllocFrame()
f5 := AllocFrame()
require.NotNil(t, f5)
defer f5.Free()
swsc2, err := astiav.CreateSoftwareScaleContext(f4.Width(), f4.Height(), f4.PixelFormat(), 512, 512, f4.PixelFormat(), astiav.NewSoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagBilinear))
swsc2, err := CreateSoftwareScaleContext(f4.Width(), f4.Height(), f4.PixelFormat(), 512, 512, f4.PixelFormat(), NewSoftwareScaleContextFlags(SoftwareScaleContextFlagBilinear))
require.NoError(t, err)
require.NoError(t, swsc2.ScaleFrame(f4, f5))

View File

@@ -1,9 +1,8 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
@@ -16,25 +15,25 @@ func TestStream(t *testing.T) {
s2 := ss[1]
require.Equal(t, 0, s1.Index())
require.Equal(t, astiav.NewRational(24, 1), s1.AvgFrameRate())
require.Equal(t, NewRational(24, 1), s1.AvgFrameRate())
require.Equal(t, int64(61440), s1.Duration())
require.True(t, s1.EventFlags().Has(astiav.StreamEventFlag(2)))
require.True(t, s1.EventFlags().Has(StreamEventFlag(2)))
require.Equal(t, 1, s1.ID())
require.Equal(t, "und", s1.Metadata().Get("language", nil, astiav.NewDictionaryFlags()).Value())
require.Equal(t, "und", s1.Metadata().Get("language", nil, NewDictionaryFlags()).Value())
require.Equal(t, int64(120), s1.NbFrames())
require.Equal(t, astiav.NewRational(24, 1), s1.RFrameRate())
require.Equal(t, astiav.NewRational(1, 1), s1.SampleAspectRatio())
require.Equal(t, []byte{}, s1.SideData(astiav.PacketSideDataTypeNb))
require.Equal(t, NewRational(24, 1), s1.RFrameRate())
require.Equal(t, NewRational(1, 1), s1.SampleAspectRatio())
require.Equal(t, []byte{}, s1.SideData(PacketSideDataTypeNb))
require.Equal(t, int64(0), s1.StartTime())
require.Equal(t, astiav.NewRational(1, 12288), s1.TimeBase())
require.Equal(t, NewRational(1, 12288), s1.TimeBase())
require.Equal(t, 1, s2.Index())
require.Equal(t, int64(240640), s2.Duration())
require.Equal(t, 2, s2.ID())
require.Equal(t, int64(235), s2.NbFrames())
require.Equal(t, int64(0), s2.StartTime())
require.Equal(t, astiav.NewRational(1, 48000), s2.TimeBase())
require.Equal(t, NewRational(1, 48000), s2.TimeBase())
s1.SetTimeBase(astiav.NewRational(1, 1))
require.Equal(t, astiav.NewRational(1, 1), s1.TimeBase())
s1.SetTimeBase(NewRational(1, 1))
require.Equal(t, NewRational(1, 1), s1.TimeBase())
}

View File

@@ -1,12 +1,11 @@
package astiav_test
package astiav
import (
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestTime(t *testing.T) {
require.NotEqual(t, 0, astiav.RelativeTime())
require.NotEqual(t, 0, RelativeTime())
}