Added format/codec context private data + options

This commit is contained in:
Quentin Renard
2024-12-10 16:46:30 +01:00
parent 09052ff745
commit 8bf2b046a2
13 changed files with 173 additions and 0 deletions

View File

@@ -210,6 +210,11 @@ func (cc *CodecContext) SetPixelFormat(pixFmt PixelFormat) {
cc.c.pix_fmt = C.enum_AVPixelFormat(pixFmt)
}
// https://ffmpeg.org/doxygen/7.0/structAVCodecContext.html#af3379123060ad8cc9c321c29af4f8360
func (cc *CodecContext) PrivateData() *PrivateData {
return newPrivateDataFromC(cc.c.priv_data)
}
// https://ffmpeg.org/doxygen/7.0/structAVCodecContext.html#a7abe7095de73df98df4895bf9e25fc6b
func (cc *CodecContext) Profile() Profile {
return Profile(cc.c.profile)

View File

@@ -35,6 +35,7 @@ func TestCodecContext(t *testing.T) {
require.Equal(t, Level(13), cc1.Level())
require.Equal(t, MediaTypeVideo, cc1.MediaType())
require.Equal(t, PixelFormatYuv420P, cc1.PixelFormat())
require.NotNil(t, cc1.PrivateData())
require.Equal(t, ProfileH264ConstrainedBaseline, cc1.Profile())
require.Equal(t, NewRational(1, 1), cc1.SampleAspectRatio())
require.Equal(t, StrictStdComplianceNormal, cc1.StrictStdCompliance())

View File

@@ -245,6 +245,26 @@ func (fs IOFormatFlags) Del(f IOFormatFlag) IOFormatFlags {
func (fs IOFormatFlags) Has(f IOFormatFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type OptionSearchFlags astikit.BitFlags
func NewOptionSearchFlags(fs ...OptionSearchFlag) OptionSearchFlags {
o := OptionSearchFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs OptionSearchFlags) Add(f OptionSearchFlag) OptionSearchFlags {
return OptionSearchFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs OptionSearchFlags) Del(f OptionSearchFlag) OptionSearchFlags {
return OptionSearchFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs OptionSearchFlags) Has(f OptionSearchFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type PacketFlags astikit.BitFlags
func NewPacketFlags(fs ...PacketFlag) PacketFlags {

View File

@@ -114,6 +114,15 @@ func TestIOFormatFlags(t *testing.T) {
require.False(t, fs.Has(IOFormatFlag(2)))
}
func TestOptionSearchFlags(t *testing.T) {
fs := NewOptionSearchFlags(OptionSearchFlag(1))
require.True(t, fs.Has(OptionSearchFlag(1)))
fs = fs.Add(OptionSearchFlag(2))
require.True(t, fs.Has(OptionSearchFlag(2)))
fs = fs.Del(OptionSearchFlag(2))
require.False(t, fs.Has(OptionSearchFlag(2)))
}
func TestPacketFlags(t *testing.T) {
fs := NewPacketFlags(PacketFlag(1))
require.True(t, fs.Has(PacketFlag(1)))

View File

@@ -186,6 +186,11 @@ func (fc *FormatContext) SetPb(i *IOContext) {
fc.c.pb = i.c
}
// https://ffmpeg.org/doxygen/7.0/structAVFormatContext.html#ac4c0777e54085af2f3f1b27130e2b21b
func (fc *FormatContext) PrivateData() *PrivateData {
return newPrivateDataFromC(fc.c.priv_data)
}
// https://ffmpeg.org/doxygen/7.0/structAVFormatContext.html#a2590129e00adfa726ab2033a10e905e9
func (fc *FormatContext) StartTime() int64 {
return int64(fc.c.start_time)

View File

@@ -24,6 +24,7 @@ func TestFormatContext(t *testing.T) {
require.Equal(t, IOContextFlags(0), fc1.IOFlags())
require.Equal(t, int64(0), fc1.MaxAnalyzeDuration())
require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, NewDictionaryFlags()).Value())
require.NotNil(t, fc1.PrivateData())
require.Equal(t, int64(0), fc1.StartTime())
require.Equal(t, 2, fc1.NbStreams())
require.Len(t, fc1.Streams(), 2)

View File

@@ -26,6 +26,7 @@ var list = []listItem{
{Name: "FormatEvent"},
{Name: "IOContext"},
{Name: "IOFormat"},
{Name: "OptionSearch"},
{Name: "Packet"},
{Name: "Seek"},
{Name: "SoftwareScaleContext"},

12
option.c Normal file
View File

@@ -0,0 +1,12 @@
#include <libavutil/opt.h>
int astiavOptionGet(void *obj, const char *name, const char **value, int flags)
{
uint8_t *v = NULL;
int ret = av_opt_get(obj, name, flags, &v);
if (ret < 0) {
return ret;
}
*value = (const char *)v;
return 0;
}

60
option.go Normal file
View File

@@ -0,0 +1,60 @@
package astiav
//#include <libavutil/opt.h>
//#include "option.h"
import "C"
import (
"unsafe"
)
type Options struct {
c unsafe.Pointer
}
func newOptionsFromC(c unsafe.Pointer) *Options {
if c == nil {
return nil
}
return &Options{c: c}
}
type Option struct {
Name string
}
// https://www.ffmpeg.org/doxygen/7.0/group__opt__mng.html#gabc75970cd87d1bf47a4ff449470e9225
func (os *Options) List() (list []Option) {
var prev *C.AVOption
for {
o := C.av_opt_next(os.c, prev)
if o == nil {
return
}
list = append(list, Option{Name: C.GoString(o.name)})
prev = o
}
}
// https://www.ffmpeg.org/doxygen/7.0/group__opt__set__funcs.html#ga5fd4b92bdf4f392a2847f711676a7537
func (os *Options) Set(name, value string, f OptionSearchFlags) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
return newError(C.av_opt_set(os.c, cname, cvalue, C.int(f)))
}
// https://www.ffmpeg.org/doxygen/7.0/group__opt__get__funcs.html#gaf31144e60f9ce89dbe8cbea57a0b232c
func (os *Options) Get(name string, f OptionSearchFlags) (string, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
var cvalue *C.char = nil
if err := newError(C.astiavOptionGet(os.c, cname, &cvalue, C.int(f))); err != nil {
return "", err
}
if cvalue == nil {
return "", nil
}
defer C.av_freep(unsafe.Pointer(&cvalue))
return C.GoString(cvalue), nil
}

1
option.h Normal file
View File

@@ -0,0 +1 @@
int astiavOptionGet(void *obj, const char *name, const char **value, int flags);

12
option_search_flag.go Normal file
View File

@@ -0,0 +1,12 @@
package astiav
//#include <libavutil/opt.h>
import "C"
// https://ffmpeg.org/doxygen/7.0/group__opt__mng.html#ga25801ba4fc9b5313eb33ec84e082dd72
type OptionSearchFlag int64
const (
OptionSearchFlagChildren = CodecContextFlag(C.AV_OPT_SEARCH_CHILDREN)
OptionSearchFlagFakeObject = CodecContextFlag(C.AV_OPT_SEARCH_FAKE_OBJ)
)

28
option_test.go Normal file
View File

@@ -0,0 +1,28 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestOption(t *testing.T) {
fc, err := AllocOutputFormatContext(nil, "mp4", "")
require.NoError(t, err)
pd := fc.PrivateData()
require.NotNil(t, pd)
os := pd.Options()
require.NotNil(t, os)
require.Equal(t, []Option{{Name: "brand"}, {Name: "empty_hdlr_name"}, {Name: "encryption_key"}, {Name: "encryption_kid"}, {Name: "encryption_scheme"}, {Name: "frag_duration"}, {Name: "frag_interleave"}, {Name: "frag_size"}, {Name: "fragment_index"}, {Name: "iods_audio_profile"}, {Name: "iods_video_profile"}, {Name: "ism_lookahead"}, {Name: "movflags"}, {Name: "cmaf"}, {Name: "dash"}, {Name: "default_base_moof"}, {Name: "delay_moov"}, {Name: "disable_chpl"}, {Name: "empty_moov"}, {Name: "faststart"}, {Name: "frag_custom"}, {Name: "frag_discont"}, {Name: "frag_every_frame"}, {Name: "frag_keyframe"}, {Name: "global_sidx"}, {Name: "isml"}, {Name: "moov_size"}, {Name: "negative_cts_offsets"}, {Name: "omit_tfhd_offset"}, {Name: "prefer_icc"}, {Name: "rtphint"}, {Name: "separate_moof"}, {Name: "skip_sidx"}, {Name: "skip_trailer"}, {Name: "use_metadata_tags"}, {Name: "write_colr"}, {Name: "write_gama"}, {Name: "min_frag_duration"}, {Name: "mov_gamma"}, {Name: "movie_timescale"}, {Name: "rtpflags"}, {Name: "latm"}, {Name: "rfc2190"}, {Name: "skip_rtcp"}, {Name: "h264_mode0"}, {Name: "send_bye"}, {Name: "skip_iods"}, {Name: "use_editlist"}, {Name: "use_stream_ids_as_track_ids"}, {Name: "video_track_timescale"}, {Name: "write_btrt"}, {Name: "write_prft"}, {Name: "pts"}, {Name: "wallclock"}, {Name: "write_tmcd"}}, os.List())
_, err = os.Get("invalid", NewOptionSearchFlags())
require.Error(t, err)
v, err := os.Get("brand", NewOptionSearchFlags())
require.NoError(t, err)
require.Equal(t, "", v)
require.Error(t, os.Set("invalid", "", NewOptionSearchFlags()))
const brand = "test"
require.NoError(t, os.Set("brand", brand, NewOptionSearchFlags()))
v, err = os.Get("brand", NewOptionSearchFlags())
require.NoError(t, err)
require.Equal(t, brand, v)
}

18
private_data.go Normal file
View File

@@ -0,0 +1,18 @@
package astiav
import "unsafe"
type PrivateData struct {
c unsafe.Pointer
}
func newPrivateDataFromC(c unsafe.Pointer) *PrivateData {
if c == nil {
return nil
}
return &PrivateData{c: c}
}
func (pd *PrivateData) Options() *Options {
return newOptionsFromC(pd.c)
}