From e586f0262eca58720b53b77c1bc12adbf61538a6 Mon Sep 17 00:00:00 2001 From: tinyzimmer <38474291+tinyzimmer@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:38:17 +0300 Subject: [PATCH] finish GstTagList, GstToc, setters, and add auto go type conversion for functions taking gvalues --- gst/c_util.go | 33 ++ gst/cgo_exports.go | 9 + gst/gst.go.h | 6 + gst/gst_caps.go | 11 +- gst/gst_message_constructors.go | 11 +- gst/gst_structure.go | 2 +- gst/gst_tag_list.go | 563 +++++++++++++++++++++++++++++++- gst/gst_tag_setter.go | 14 +- gst/gst_wrappers.go | 18 +- 9 files changed, 639 insertions(+), 28 deletions(-) diff --git a/gst/c_util.go b/gst/c_util.go index 091e0ce..4fcaf25 100644 --- a/gst/c_util.go +++ b/gst/c_util.go @@ -4,6 +4,8 @@ package gst import "C" import ( + "fmt" + "time" "unsafe" ) @@ -20,6 +22,37 @@ func gboolean(b bool) C.gboolean { return C.gboolean(0) } +// gdateToTime converts a GDate to a time object. +func gdateToTime(gdate *C.GDate) time.Time { + tm := time.Time{} + tm.AddDate(int(C.g_date_get_year(gdate)), int(C.g_date_get_month(gdate)), int(C.g_date_get_day(gdate))) + return tm +} + +// gstDateTimeToTime converts a GstDateTime to a time object. If the datetime object could not be parsed, +// an empty time object is returned. +func gstDateTimeToTime(gstdatetime *C.GstDateTime) time.Time { + dateStr := fmt.Sprintf( + "%s %s %d:%d:%d %s %d", + time.Weekday(C.gst_date_time_get_day(gstdatetime)).String(), + time.Month(C.gst_date_time_get_month(gstdatetime)).String(), + int(C.gst_date_time_get_hour(gstdatetime)), + int(C.gst_date_time_get_minute(gstdatetime)), + int(C.gst_date_time_get_second(gstdatetime)), + formatOffset(C.gst_date_time_get_time_zone_offset(gstdatetime)), + int(C.gst_date_time_get_year(gstdatetime)), + ) + tm, _ := time.Parse("Mon Jan 2 15:04:05 -0700 2006", dateStr) + return tm +} + +func formatOffset(offset C.gfloat) string { + if offset < 0 { + return fmt.Sprintf("-0%d00", int(offset)) + } + return fmt.Sprintf("+0%d00", int(offset)) +} + // goStrings returns a string slice for an array of size argc starting at the address argv. func goStrings(argc C.int, argv **C.gchar) []string { length := int(argc) diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index 7d6122d..5f154cd 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -13,6 +13,15 @@ import ( gopointer "github.com/mattn/go-pointer" ) +//export goTagForEachFunc +func goTagForEachFunc(tagList *C.GstTagList, tag *C.gchar, userData C.gpointer) { + cbIface := gopointer.Restore(unsafe.Pointer(userData)) + cbFunc := cbIface.(TagListForEachFunc) + cbFunc(wrapTagList(tagList), Tag(C.GoString(tag))) +} + +// func goTagMergeFunc(dest, src *C.GValue) {} + //export goBufferListForEachCb func goBufferListForEachCb(buf **C.GstBuffer, idx C.guint, userData C.gpointer) C.gboolean { cbIface := gopointer.Restore(unsafe.Pointer(userData)) diff --git a/gst/gst.go.h b/gst/gst.go.h index af37e51..ac2a1c5 100644 --- a/gst/gst.go.h +++ b/gst/gst.go.h @@ -35,6 +35,7 @@ inline GstSample * toGstSample (void *p) { return (GST_SAM inline GstStreamCollection * toGstStreamCollection (void *p) { return (GST_STREAM_COLLECTION_CAST(p)); } inline GstStream * toGstStream (void *p) { return (GST_STREAM_CAST(p)); } inline GstStructure * toGstStructure (void *p) { return (GST_STRUCTURE(p)); } +inline GstTagList * toGstTagList (void *p) { return (GST_TAG_LIST(p)); } inline GstURIHandler * toGstURIHandler (void *p) { return (GST_URI_HANDLER(p)); } inline GstUri * toGstURI (void *p) { return (GST_URI(p)); } @@ -126,6 +127,11 @@ inline GstToc * makeTocWritable (GstToc * toc) { return inline GstToc * tocRef (GstToc * toc) { return gst_toc_ref(toc); } inline void tocUnref (GstToc * toc) { gst_toc_unref(toc); } +/* TagList utilities */ + +inline gboolean tagListIsWritable (GstTagList * tagList) { return gst_tag_list_is_writable(tagList); } +inline GstTagList * makeTagListWritable (GstTagList * tagList) { return gst_tag_list_make_writable(tagList); } + /* Object Utilities */ inline GObjectClass * getGObjectClass (void * p) { return (G_OBJECT_GET_CLASS(p)); } diff --git a/gst/gst_caps.go b/gst/gst_caps.go index 390d941..4575ca6 100644 --- a/gst/gst_caps.go +++ b/gst/gst_caps.go @@ -392,12 +392,17 @@ func (c *Caps) SetFeaturesSimple(features *CapsFeatures) { } // SetValue sets the given field on all structures of caps to the given value. This is a convenience -// function for calling SetValue on all structures of caps. -func (c *Caps) SetValue(field string, val *glib.Value) { +// function for calling SetValue on all structures of caps. If the value cannot be coerced to a C type, +// then nothing will happen. +func (c *Caps) SetValue(field string, val interface{}) { + gVal, err := glib.GValue(val) + if err != nil { + return + } C.gst_caps_set_value( c.Instance(), (*C.gchar)(unsafe.Pointer(C.CString(field))), - (*C.GValue)(val.GetPointer()), + (*C.GValue)(gVal.Native()), ) } diff --git a/gst/gst_message_constructors.go b/gst/gst_message_constructors.go index c9284c1..efebc99 100644 --- a/gst/gst_message_constructors.go +++ b/gst/gst_message_constructors.go @@ -288,17 +288,22 @@ func NewProgressMessage(src interface{}, progressType ProgressType, code, text s return wrapMessage(C.gst_message_new_progress(srcObj, C.GstProgressType(progressType), cCode, cText)) } -// NewPropertyNotifyMessage creates a new message notifying an object's properties have changed. -func NewPropertyNotifyMessage(src interface{}, propName string, val *glib.Value) *Message { +// NewPropertyNotifyMessage creates a new message notifying an object's properties have changed. If the +// source OR the value cannot be coereced to C types, the function will return nil. +func NewPropertyNotifyMessage(src interface{}, propName string, val interface{}) *Message { srcObj := getMessageSourceObj(src) if srcObj == nil { return nil } + gVal, err := glib.GValue(val) + if err != nil { + return nil + } cName := (*C.gchar)(unsafe.Pointer(C.CString(propName))) return wrapMessage(C.gst_message_new_property_notify( srcObj, cName, - (*C.GValue)(val.GetPointer()), + (*C.GValue)(gVal.Native()), )) } diff --git a/gst/gst_structure.go b/gst/gst_structure.go index 61e4407..c12d282 100644 --- a/gst/gst_structure.go +++ b/gst/gst_structure.go @@ -87,7 +87,7 @@ func (s *Structure) SetValue(key string, value interface{}) error { } cKey := C.CString(key) defer C.free(unsafe.Pointer(cKey)) - C.gst_structure_set_value(s.Instance(), cKey, (*C.GValue)(gVal.GetPointer())) + C.gst_structure_set_value(s.Instance(), cKey, (*C.GValue)(unsafe.Pointer(gVal.GValue))) return nil } diff --git a/gst/gst_tag_list.go b/gst/gst_tag_list.go index 075c2fe..55992d5 100644 --- a/gst/gst_tag_list.go +++ b/gst/gst_tag_list.go @@ -1,9 +1,25 @@ package gst -// #include "gst.go.h" +/* +#include "gst.go.h" + +extern void goTagForEachFunc (const GstTagList * tagList, const gchar * tag, gpointer user_data); + +void cgoTagForEachFunc (const GstTagList * tagList, const gchar * tag, gpointer user_data) +{ + goTagForEachFunc(tagList, tag, user_data); +} + +*/ import "C" -import "unsafe" +import ( + "time" + "unsafe" + + "github.com/gotk3/gotk3/glib" + gopointer "github.com/mattn/go-pointer" +) // TagList is a go wrapper around a GstTagList. For now, until the rest of the methods are // implemnented, this struct is primarily used for retrieving serialized copies of the tags. @@ -11,8 +27,30 @@ type TagList struct { ptr *C.GstTagList } +// NewEmptyTagList returns a new empty tag list. +// +// tagList := gst.NewEmptyTagList() +// fmt.Println(tagList.IsEmpty()) +// // true +// +func NewEmptyTagList() *TagList { + return wrapTagList(C.gst_tag_list_new_empty()) +} + +// NewTagListFromString creates a new tag list from the given string. This is the same format produced +// by the stringer interface on the TagList. +func NewTagListFromString(tags string) *TagList { + ctags := C.CString(tags) + defer C.free(unsafe.Pointer(ctags)) + tagList := C.gst_tag_list_new_from_string((*C.gchar)(unsafe.Pointer(ctags))) + if tagList == nil { + return nil + } + return wrapTagList(tagList) +} + // Instance returns the underlying GstTagList instance. -func (t *TagList) Instance() *C.GstTagList { return t.ptr } +func (t *TagList) Instance() *C.GstTagList { return C.toGstTagList(unsafe.Pointer(t.ptr)) } // String implements a stringer on the TagList and serializes it to a string. func (t *TagList) String() string { return C.GoString(C.gst_tag_list_to_string(t.Instance())) } @@ -24,17 +62,522 @@ func (t *TagList) Ref() *TagList { return wrapTagList(C.gst_tag_list_ref(t.Insta // is destroyed. func (t *TagList) Unref() { C.gst_tag_list_unref(t.Instance()) } -// Size returns the number of key/value pairs in ths TagList -func (t *TagList) Size() int { return int(C.gst_tag_list_n_tags(t.Instance())) } +// AddValue adds a value to a given tag using the given merge mode. If the value provided +// cannot be coerced to a GValue, nothing will happen. +// +// tagList := gst.NewEmptyTagList() +// tagList.AddValue(gst.TagMergeAppend, gst.TagAlbum, "MyNewAlbum") +// myAlbum, _ := tagList.GetString(gst.TagAlbum) +// fmt.Println(myAlbum) +// // MyNewAlbum +// +func (t *TagList) AddValue(mergeMode TagMergeMode, tag Tag, value interface{}) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + gVal, err := glib.GValue(value) + if err != nil { + return + } + C.gst_tag_list_add_value( + t.Instance(), + C.GstTagMergeMode(mergeMode), + (*C.gchar)(ctag), + (*C.GValue)(gVal.Native()), + ) +} + +// AddValues can be used to add multiple values to a tag with the given merge mode. +// Values that cannot be coerced to C types will be ignored. +func (t *TagList) AddValues(mergeMode TagMergeMode, tag Tag, vals ...interface{}) { + for _, val := range vals { + t.AddValue(mergeMode, tag, val) + } +} + +// Copy creates a new TagList as a copy of the old taglist. The new taglist will have a refcount of 1, +// owned by the caller, and will be writable as a result. +// +// Note that this function is the semantic equivalent of a Ref followed by a MakeWritable. If you only want +// to hold on to a reference to the data, you should use Ref. +// +// When you are finished with the taglist, call Unref on it. +func (t *TagList) Copy() *TagList { return wrapTagList(C.gst_tag_list_copy(t.Instance())) } + +// TagListForEachFunc is a function that will be called in ForEach. The function may not modify the tag list. +type TagListForEachFunc func(tagList *TagList, tag Tag) + +// ForEach calls the given function for each tag inside the tag list. Note that if there is no tag, +// the function won't be called at all. +// +// tagList := gst.NewEmptyTagList() +// +// tagList.AddValue(gst.TagMergeAppend, gst.TagAlbumArtist, "tinyzimmer") +// tagList.AddValue(gst.TagMergeAppend, gst.TagAlbum, "GstreamerInGo") +// +// tagList.ForEach(func(_ *gst.TagList, tag gst.Tag) { +// val, _ := tagList.GetString(tag) +// fmt.Println(tag, ":", val) +// }) +// +// // album-artist : tinyzimmer +// // album : GstreamerInGo +// +func (t *TagList) ForEach(f TagListForEachFunc) { + ptr := gopointer.Save(f) + defer gopointer.Unref(ptr) + C.gst_tag_list_foreach( + t.Instance(), + C.GstTagForeachFunc(C.cgoTagForEachFunc), + (C.gpointer)(ptr), + ) +} + +// GetBool returns the boolean value at the given tag key. If multiple values are associated with the tag they +// are merged. +func (t *TagList) GetBool(tag Tag) (value, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gboolean + gok := C.gst_tag_list_get_boolean( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return gobool(gout), gobool(gok) +} + +// GetBoolIndex retrieves the bool at the given index in the tag key. +func (t *TagList) GetBoolIndex(tag Tag, idx uint) (value, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gboolean + gok := C.gst_tag_list_get_boolean_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + return gobool(gout), gobool(gok) +} + +// GetDate returns the date stored at the given tag key. If there are multiple values, the first one +// is returned. +func (t *TagList) GetDate(tag Tag) (value time.Time, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GDate + gok := C.gst_tag_list_get_date( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + if gobool(gok) { + defer C.g_date_free(gout) + return gdateToTime(gout), true + } + return time.Time{}, false +} + +// GetDateIndex returns the date stored at the given index in tag key. +func (t *TagList) GetDateIndex(tag Tag, idx uint) (value time.Time, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GDate + gok := C.gst_tag_list_get_date_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + if gobool(gok) { + defer C.g_date_free(gout) + return gdateToTime(gout), true + } + return time.Time{}, false +} + +// GetDateTime returns the date and time stored at the given tag key. If there are multiple values, the first one +// is returned. +func (t *TagList) GetDateTime(tag Tag) (value time.Time, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GstDateTime + gok := C.gst_tag_list_get_date_time( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + if gobool(gok) { + defer C.gst_date_time_unref(gout) + return gstDateTimeToTime(gout), true + } + return time.Time{}, false +} + +// GetDateTimeIndex returns the date and time stored at the given tag key at the given index. +func (t *TagList) GetDateTimeIndex(tag Tag, idx uint) (value time.Time, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GstDateTime + gok := C.gst_tag_list_get_date_time_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + if gobool(gok) { + defer C.gst_date_time_unref(gout) + return gstDateTimeToTime(gout), true + } + return time.Time{}, false +} + +// GetFloat64 returns the float at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. This is the equivalent of a C double stored in the tag. +func (t *TagList) GetFloat64(tag Tag) (value float64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gdouble + gok := C.gst_tag_list_get_double( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return float64(gout), gobool(gok) +} + +// GetFloat64Index returns the float at the index of the given tag key. +func (t *TagList) GetFloat64Index(tag Tag, idx uint) (value float64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gdouble + gok := C.gst_tag_list_get_double_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return float64(gout), gobool(gok) +} + +// GetFloat32 returns the float at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. +func (t *TagList) GetFloat32(tag Tag) (value float32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gfloat + gok := C.gst_tag_list_get_float( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return float32(gout), gobool(gok) +} + +// GetFloat32Index returns the float at the index of the given tag key. +func (t *TagList) GetFloat32Index(tag Tag, idx uint) (value float32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gfloat + gok := C.gst_tag_list_get_float_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return float32(gout), gobool(gok) +} + +// GetInt32 returns the integer at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. +func (t *TagList) GetInt32(tag Tag) (value int32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gint + gok := C.gst_tag_list_get_int( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return int32(gout), gobool(gok) +} + +// GetInt32Index returns the integer at the index of the given tag key. +func (t *TagList) GetInt32Index(tag Tag, idx uint) (value int32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gint + gok := C.gst_tag_list_get_int_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return int32(gout), gobool(gok) +} + +// GetInt64 returns the integer at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. +func (t *TagList) GetInt64(tag Tag) (value int64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gint64 + gok := C.gst_tag_list_get_int64( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return int64(gout), gobool(gok) +} + +// GetInt64Index returns the integer at the index of the given tag key. +func (t *TagList) GetInt64Index(tag Tag, idx uint) (value int64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gint64 + gok := C.gst_tag_list_get_int64_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return int64(gout), gobool(gok) +} + +// GetPointer returns the C pointer stored at the given tag key, merging values if there are multiple. +func (t *TagList) GetPointer(tag Tag) (value unsafe.Pointer, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gpointer + gok := C.gst_tag_list_get_pointer( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return unsafe.Pointer(gout), gobool(gok) +} + +// GetPointerIndex returns the C pointer stored at the given tag key index. +func (t *TagList) GetPointerIndex(tag Tag, idx uint) (value unsafe.Pointer, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.gpointer + gok := C.gst_tag_list_get_pointer_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + return unsafe.Pointer(gout), gobool(gok) +} + +// GetSample copies the first sample for the given tag in the taglist. Free the sample with Unref when it +// is no longer needed. +func (t *TagList) GetSample(tag Tag) (value *Sample, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GstSample + gok := C.gst_tag_list_get_sample( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + if gobool(gok) { + return wrapSample(gout), true + } + return nil, false +} + +// GetSampleIndex copies the sample for the given index in tag in the taglist. Free the sample with Unref +// when it is no longer needed. +func (t *TagList) GetSampleIndex(tag Tag, idx uint) (value *Sample, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.GstSample + gok := C.gst_tag_list_get_sample_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + if gobool(gok) { + return wrapSample(gout), true + } + return nil, false +} + +// GetString returns the string for the given tag, possibly merging multiple values into one. +func (t *TagList) GetString(tag Tag) (value string, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.gchar + gok := C.gst_tag_list_get_string( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + defer C.g_free((C.gpointer)(unsafe.Pointer(gout))) + return C.GoString(gout), gobool(gok) +} + +// GetStringIndex returns the string for the given index in tag. +func (t *TagList) GetStringIndex(tag Tag, idx uint) (value string, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.gchar + gok := C.gst_tag_list_get_string_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + defer C.g_free((C.gpointer)(unsafe.Pointer(gout))) + return C.GoString(gout), gobool(gok) +} + +// GetUint32 returns the unsigned integer at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. +func (t *TagList) GetUint32(tag Tag) (value uint32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.guint + gok := C.gst_tag_list_get_uint( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return uint32(gout), gobool(gok) +} + +// GetUint32Index returns the unsigned integer at the index of the given tag key. +func (t *TagList) GetUint32Index(tag Tag, idx uint) (value uint32, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.guint + gok := C.gst_tag_list_get_uint_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return uint32(gout), gobool(gok) +} + +// GetUint64 returns the unsigned integer at the given tag key, merging multiple values into one if multiple values +// are associated with the tag. +func (t *TagList) GetUint64(tag Tag) (value uint64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.guint64 + gok := C.gst_tag_list_get_uint64( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + &gout, + ) + return uint64(gout), gobool(gok) +} + +// GetUint64Index returns the unsigned integer at the index of the given tag key. +func (t *TagList) GetUint64Index(tag Tag, idx uint) (value uint64, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout C.guint64 + gok := C.gst_tag_list_get_uint64_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.uint(idx), + &gout, + ) + return uint64(gout), gobool(gok) +} + +// GetValueIndex retrieves the GValue at the given index in tag, or nil if none exists. +// Note that this function can also return nil if the stored value cannot be cleanly coerced +// to a go type. It is safer to use the other functions provided when you know the expected +// return type. +func (t *TagList) GetValueIndex(tag Tag, idx uint) interface{} { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + gval := C.gst_tag_list_get_value_index(t.Instance(), (*C.gchar)(unsafe.Pointer(ctag)), C.guint(idx)) + if gval == nil { + return nil + } + val := glib.ValueFromNative(unsafe.Pointer(gval)) + iface, _ := val.GoValue() + return iface +} + +// GetScope returns the scope for this TagList. +func (t *TagList) GetScope() TagScope { return TagScope(C.gst_tag_list_get_scope(t.Instance())) } + +// GetTagSize returns the number of tag values at the given tag key. +func (t *TagList) GetTagSize(tagKey string) int { + cStr := C.CString(tagKey) + defer C.free(unsafe.Pointer(cStr)) + return int(C.gst_tag_list_get_tag_size(t.Instance(), (*C.gchar)(cStr))) +} + +// Insert inserts the tags from the provided list using the given merge mode. +func (t *TagList) Insert(tagList *TagList, mergeMode TagMergeMode) { + C.gst_tag_list_insert(t.Instance(), tagList.Instance(), C.GstTagMergeMode(mergeMode)) +} + +// IsEmpty returns true if this tag list is empty. +func (t *TagList) IsEmpty() bool { return gobool(C.gst_tag_list_is_empty(t.Instance())) } + +// IsEqual checks if the two tag lists are equal. +func (t *TagList) IsEqual(tagList *TagList) bool { + return gobool(C.gst_tag_list_is_equal(t.Instance(), tagList.Instance())) +} + +// IsWritable returns true if this TagList is writable. +func (t *TagList) IsWritable() bool { return gobool(C.tagListIsWritable(t.Instance())) } + +// MakeWritable will return a writable copy of the tag list if it is not already so. +func (t *TagList) MakeWritable() *TagList { + return wrapTagList(C.makeTagListWritable(t.Instance())) +} + +// Merge merges the two tag lists with the given mode. +func (t *TagList) Merge(tagList *TagList, mergeMode TagMergeMode) *TagList { + return wrapTagList(C.gst_tag_list_merge( + t.Instance(), + tagList.Instance(), + C.GstTagMergeMode(mergeMode), + )) +} + +// NumTags returns the number of key/value pairs in ths TagList +func (t *TagList) NumTags() int { return int(C.gst_tag_list_n_tags(t.Instance())) } // TagNameAt returns the tag name at the given index. func (t *TagList) TagNameAt(idx int) string { return C.GoString(C.gst_tag_list_nth_tag_name(t.Instance(), C.guint(idx))) } -// NumValuesAt returns the number of tag values at the given tag key. -func (t *TagList) NumValuesAt(tagKey string) int { - cStr := C.CString(tagKey) - defer C.free(unsafe.Pointer(cStr)) - return int(C.gst_tag_list_get_tag_size(t.Instance(), (*C.gchar)(cStr))) +// PeekStringIndex peeks at the value that is at the given index for the given tag in the given list. +func (t *TagList) PeekStringIndex(tag Tag, idx uint) (value string, ok bool) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + var gout *C.gchar + gok := C.gst_tag_list_peek_string_index( + t.Instance(), + (*C.gchar)(unsafe.Pointer(ctag)), + C.guint(idx), + &gout, + ) + defer C.g_free((C.gpointer)(unsafe.Pointer(gout))) + return C.GoString(gout), gobool(gok) +} + +// RemoveTag removes the values for the given tag in this list. +func (t *TagList) RemoveTag(tag Tag) { + ctag := C.CString(string(tag)) + defer C.free(unsafe.Pointer(ctag)) + C.gst_tag_list_remove_tag(t.Instance(), (*C.gchar)(unsafe.Pointer(ctag))) +} + +// SetScope sets the scope of this TagList. By default, the scope of a tag list is stream scope. +func (t *TagList) SetScope(scope TagScope) { + C.gst_tag_list_set_scope(t.Instance(), C.GstTagScope(scope)) } diff --git a/gst/gst_tag_setter.go b/gst/gst_tag_setter.go index 31e20a6..e480823 100644 --- a/gst/gst_tag_setter.go +++ b/gst/gst_tag_setter.go @@ -12,9 +12,9 @@ import ( type TagSetter interface { // Returns the current list of tags the setter uses. The list should not be modified or freed. GetTagList() *TagList - // Adds the given tag/value pair using the given merge mode. - // TODO: Either an additional function or this one should be modified to accept go types. - AddTagValue(mergeMode TagMergeMode, tagKey string, tagValue *glib.Value) + // Adds the given tag/value pair using the given merge mode. If the tag value cannot be coerced + // to a GValue when dealing with C elements, nothing will happen. + AddTagValue(mergeMode TagMergeMode, tagKey string, tagValue interface{}) // Merges a tag list with the given merge mode MergeTags(*TagList, TagMergeMode) // Resets the internal tag list. Elements should call this from within the state-change handler. @@ -42,14 +42,18 @@ func (t *gstTagSetter) GetTagList() *TagList { return wrapTagList(tagList) } -func (t *gstTagSetter) AddTagValue(mergeMode TagMergeMode, tagKey string, tagValue *glib.Value) { +func (t *gstTagSetter) AddTagValue(mergeMode TagMergeMode, tagKey string, tagValue interface{}) { ckey := C.CString(tagKey) defer C.free(unsafe.Pointer(ckey)) + gVal, err := glib.GValue(tagValue) + if err != nil { + return + } C.gst_tag_setter_add_tag_value( t.Instance(), C.GstTagMergeMode(mergeMode), (*C.gchar)(unsafe.Pointer(ckey)), - (*C.GValue)(tagValue.GetPointer()), + (*C.GValue)(gVal.Native()), ) } diff --git a/gst/gst_wrappers.go b/gst/gst_wrappers.go index c6d14d4..03edbad 100644 --- a/gst/gst_wrappers.go +++ b/gst/gst_wrappers.go @@ -2,11 +2,6 @@ package gst /* #include "gst.go.h" - -GValue * gvalPtrToGval (gpointer p) -{ - return (GValue*)(p); -} */ import "C" @@ -134,6 +129,10 @@ func init() { T: glib.Type(C.GST_TYPE_TOC), F: marshalTOC, }, + { + T: glib.Type(C.GST_TYPE_TAG_LIST), + F: marsalTagList, + }, // Boxed {T: glib.Type(C.gst_message_get_type()), F: marshalMessage}, @@ -142,7 +141,8 @@ func init() { } func uintptrToGVal(p uintptr) *C.GValue { - return (*C.GValue)(unsafe.Pointer(p)) + return (*C.GValue)(unsafe.Pointer(p)) // vet thinks this is unsafe and there is no way around it for now. + // but the given ptr is an address to a C object so go's concerns are misplaced. } // Object wrappers @@ -372,3 +372,9 @@ func marshalTOCEntry(p uintptr) (interface{}, error) { obj := (*C.GstTocEntry)(unsafe.Pointer(c)) return wrapTOCEntry(obj), nil } + +func marsalTagList(p uintptr) (interface{}, error) { + c := C.g_value_get_object(uintptrToGVal(p)) + obj := (*C.GstTagList)(unsafe.Pointer(c)) + return wrapTagList(obj), nil +}